Compare commits

..

41 Commits

Author SHA1 Message Date
Chris Miles c966f26ac1 [Release] 22.62.1 (#4627) 2025-01-27 16:36:51 -06:00
Akkadius fe66c24352 Revert "[Memory Leak] Change raw pointer to unique_ptr to avoid potential leak in dbg stream (#4616)"
This reverts commit 1f3ac2dc4f.
2025-01-27 16:34:05 -06:00
Chris Miles bdcded7d45 [Performance] Re-use OP_PlayerStateAdd packet memory (#4626) 2025-01-27 15:33:03 -06:00
Chris Miles f939f25aa1 [Performance] Re-use OP_Damage packet memory (#4625) 2025-01-27 15:32:55 -06:00
Chris Miles 401f1038f3 [Performance] Re-use OP_SendFindableNPCs packet memory (#4623) 2025-01-27 15:32:48 -06:00
Chris Miles 95c6560e7c [Performance] Re-use OP_HPUpdate packet memory (#4622) 2025-01-27 15:32:40 -06:00
Chris Miles 59f645b5c3 [Performance] Re-use OP_Animation packet (#4621) 2025-01-27 15:32:30 -06:00
Chris Miles bbfed8300c [Repop] Make #repop instant (#4620) 2025-01-27 15:32:22 -06:00
Chris Miles 07c762068f [Performance] Re-use ClientUpdate packet memory (#4619) 2025-01-27 15:32:14 -06:00
dependabot[bot] 6525051d2d Bump golang.org/x/net in /utils/scripts/build/should-release (#4618)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.23.0 to 0.33.0.
- [Commits](https://github.com/golang/net/compare/v0.23.0...v0.33.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-26 21:28:40 -06:00
Chris Miles b7f94e8315 [Release] 22.62.0 (#4617) 2025-01-26 21:26:34 -06:00
Chris Miles d1f368ab7f [Databuckets] Implement Nested Databuckets (#4604)
* WIP

* Fixes

* Fin

* Update data_bucket.cpp

* Update data_bucket.cpp

* Cleanup

* Update data_bucket.cpp

* Update data_bucket.cpp

* NESTED_KEY_DELIMITER
2025-01-26 21:13:31 -06:00
Alex 1f3ac2dc4f [Memory Leak] Change raw pointer to unique_ptr to avoid potential leak in dbg stream (#4616)
Co-authored-by: KimLS <KimLS@peqtgc.com>
2025-01-26 19:05:45 -06:00
Mitch Freeman 7a226ca4ef [Bazaar] Improve Bazaar Search Performance (#4615)
* Create Alternate BazaarSearch Routine

Establishes an alterative to the in memory bazaar search routine and instead uses a db query process.  For large Bazaars (with 1000s of items) this is much faster.  Testing with 30k items produced a search in ~1sec version 2.7sec for the in memory version.
Default is false - Do not use this version.

* Indexes for trader and items

* Set query-based bazaar search the default

* Update database_update_manifest.cpp

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-01-26 19:04:56 -06:00
Akkadius 119151c0e3 [Hotfix] Delete later in RemoveItem second case 2025-01-25 14:04:57 -06:00
Chris Miles faa8a492f7 [Memory Leak] Fix leak in Client::RemoveDuplicateLore (#4614) 2025-01-24 13:22:58 -05:00
Chris Miles 2926b4df78 [Memory Leak] Fix memory leak in Client::Handle_OP_MoveMultipleItems (#4613)
* [Memory Leak] Fix memory leak in Client::Handle_OP_MoveMultipleItems

* Update client_packet.cpp
2025-01-24 02:59:08 -06:00
Chris Miles 986eda44aa [Memory Leak] Fix leaks in Client::Handle_OP_AugmentItem (#4612)
* [Memory Leak] Fix leaks in Client::Handle_OP_AugmentItem

* Update client_packet.cpp
2025-01-24 02:58:58 -06:00
Chris Miles b2f71f16fc [Memory Leak] Fix leak in NPC::RemoveItem (#4611)
* [Memory Leak] Fix leak in NPC::RemoveItem

* Update loot.cpp
2025-01-24 03:10:06 -05:00
Chris Miles 861eac3660 [Memory Leak] Fix leak in QuestManager::varlink (#4610) 2025-01-24 02:56:22 -05:00
Chris Miles a376bc4471 [Memory Leak] Fix leak in BuyTraderItemOutsideBazaar (#4609) 2025-01-24 02:56:15 -05:00
Mitch Freeman e83d0942ad [Fix] Repair levers opening the Evolving XP Transfer Window (#4607) 2025-01-22 18:55:21 -06:00
catapultam-habeo 31abaf8016 [Feature] Implement Custom Pet Names (#4594)
* rebase\tidy up to address commends

* I blame git for this one

* last typo

* spaces

* formating fixes I think?

* Repository fixes

* Cleanup

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-01-22 03:31:05 -06:00
Chris Miles 0acad18067 [CLI] Add --skip-backup to world database:updates (#4605) 2025-01-21 19:11:58 -06:00
Akkadius 90c37390f1 [Hotfix] CLI help menu from parsing correctly in World 2025-01-21 18:48:02 -06:00
Akkadius dba494cd8e [Hotfix] Update database version to match manifest 2025-01-21 17:39:33 -06:00
Chris Miles 37ced4b003 [Databuckets] Add Account Scoped Databuckets (#4603)
* [Databuckets] Add Account Scoped Databuckets

* Add variation

* Fix Lua after testing
2025-01-21 16:06:18 -06:00
Chris Miles d13c725a74 [Linux] Implement KSM Kernel Samepage Merging with Maps (#4601)
* KSM work

* Windows fixes

* Add KSM logging, cleanup

* Cleanup raycast logging
2025-01-21 15:50:20 -06:00
Chris Miles 25826c6686 [Performance] Client / NPC Position Update Optimizations (#4602)
* Zone optimizations

* More changes

* More

* Update entity.cpp

* Beautiful

* Amazing

* Feature flag all logic

* Broadcast to group

* Update mob.cpp

* Updates

* Update client.cpp

* Update client.cpp

* Add rule Zone:EnableEntityClipping

* Little bit of cleanup

* Don't send update to self while in group

* Remove visibility work and feature flags

* Cleanup

* Logging

* Improve CheckSendBulkNpcPositions

* No need to cast

* Field cleanup

* Build initial list on zone-in
2025-01-21 15:35:19 -06:00
Akkadius 1a27127c39 [Hotfix] Fix query error in character_evolving_items 2025-01-20 23:21:37 -06:00
Alex King 75698a809f [Feature] Add Support for Item Previews (#4599)
* [Feature] Add Support for Item Previews

* Update client_packet.cpp
2025-01-19 19:18:17 -06:00
Mitch Freeman 37a7b7fc41 [Feature] Add Alternate Bazaar Search Approach (#4600)
* Add Alternate Bazaar Search

This adds an alternate bazaar search allowing multinstance bazaar searching and traders above 600.  Allows searches based on Bazaar Shard

* Update worldserver.cpp

---------

Co-authored-by: Mitch Freeman <neckkola@gmail.com>
Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-01-19 19:02:53 -06:00
Mitch Freeman f21cc170df [Feature] Evolving Item Support for RoF2 (#4496)
* basic evolving items framework created

* Implement evolving tab in the inventory window

* Implement experience and number of kills

* Move zone evolving map to a evolvingitemsmanager class

* rework gm commands

* rework GetInventory

* wip

* wip loot testing

* Fix Duplicate Message

* reworked evolving item looting, swapping, etc

* reworked const functions for evolving methods

* Functioning Player Trade of evolving items test item_id is 89550

* First pass of Final Result link working

* First pass of item upgrading when reaching 100%

* Add strings and logic for displaying the evolving item xp transfer window in Corathus

* Prototype of xp transfer window sending items

* WIP for evolve xp transfer

* WIP for evolve xp transfer.  First tests passed

* XP Transfer Cleanup

* XP Transfer Cleanup

* Add Rule for evolving items equip timer/  default is 30 secs

* Add logging and player events

Add logging and player events

* Formatting

* Database updates

* Updates for linux build

* Perl/Cleanup

* Command cleanup

* Lua

* Added a crash condition check if final item id is blank or not found.

* Review Changes

Updates to resolve review comments and a rebase.

* migrate to content_db for items_evolving_details

migrate to content_db for items_evolving_details

* Simplify, don't hit database unless evolving

* Update 2025_01_19_items_evolving_details.sql

* Update client.cpp

* Update manifest with items_evolving_details

* character_id vs char_id

* Remove _Struct from structs

* Remove license header in evolving.cpp

* Move evolving constants from eq_constants.h to evolving.h since it is more specific

* Update database_schema.h

* General cleanup

* Be more specific with `evolving_items` vs `evolving`

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-01-19 18:10:19 -06:00
Mitch Freeman d47bf687d0 [Fix] Update trader add/remove packets to limits for RoF2 (#4595)
* Update trader add/remove to limit for RoF2

Update trader add/remove to limit for RoF2

* Update for feedback

---------

Co-authored-by: Mitch Freeman <neckkola@gmail.com>
2025-01-19 16:37:09 -06:00
Alex King 035c51944f [Quest API] Add SetAAEXPPercentage to Perl/Lua (#4597) 2025-01-19 16:36:48 -06:00
Mitch Freeman c2ebc2540a [Fix] Update a few Bazaar RoF2 routines for memory leaks (#4592)
Memory leak repairs
2025-01-08 17:45:03 -06:00
Chris Miles c82f1b9afc [Zone] Implement zone player count sharding (#4536)
* [Zone] Implement zone player count sharding

* Update client.cpp

* Update database_instances.cpp

* You must request a shard change from the zone you are currently in.

* // zone sharding

* You cannot request a shard change while in combat.

* Query adjustment

* Use safe coords

* Changes

* Fixes to instance query

* Push

* Push

* Final push

* Update client.cpp

* Update eq_packet_structs.h

* Remove pick menu

* Comment

* Update character_data_repository.h

* Update zoning.cpp

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2025-01-08 17:41:16 -06:00
Mitch Freeman 15684567cf [Fix] Add Bazaar BulkSendTrader Limit for RoF2 (#4590)
Add Bazaar BulkSendTrader Limit
2025-01-08 13:28:44 -06:00
Mitch Freeman 533dc997fd [Fix] Repair a memory leak in #summonitem (#4591) 2025-01-07 23:50:34 -05:00
Chris Miles 03b30d5c7a [Database] Change npc_types walkspeed to be of type float (#4589)
* [Database] Change npc_types walkspeed to be of type float

* Update database_update_manifest.cpp
2025-01-07 14:46:32 -05:00
Mitch Freeman d89f9bdcc7 [Fix] Repair an incorrect safe_delete call memory leak. (#4588) 2025-01-07 00:01:49 -05:00
118 changed files with 7751 additions and 2602 deletions
+1
View File
@@ -68,3 +68,4 @@ compile_flags.txt
# CMake Files
cmake-build-relwithdebinfo/*
skill-caps.diff
+84
View File
@@ -1,3 +1,87 @@
## [22.62.1] 1/27/2025
### Memory Leak
* Revert "Change raw pointer to unique_ptr to avoid potential leak in dbg stream" ([#4616](https://github.com/EQEmu/Server/pull/4616)) @Akkadius 2025-01-27
### Performance
* Re-use ClientUpdate packet memory ([#4619](https://github.com/EQEmu/Server/pull/4619)) @Akkadius 2025-01-27
* Re-use OP_Animation packet ([#4621](https://github.com/EQEmu/Server/pull/4621)) @Akkadius 2025-01-27
* Re-use OP_Damage packet memory ([#4625](https://github.com/EQEmu/Server/pull/4625)) @Akkadius 2025-01-27
* Re-use OP_HPUpdate packet memory ([#4622](https://github.com/EQEmu/Server/pull/4622)) @Akkadius 2025-01-27
* Re-use OP_PlayerStateAdd packet memory ([#4626](https://github.com/EQEmu/Server/pull/4626)) @Akkadius 2025-01-27
* Re-use OP_SendFindableNPCs packet memory ([#4623](https://github.com/EQEmu/Server/pull/4623)) @Akkadius 2025-01-27
### Repop
* Make #repop instant ([#4620](https://github.com/EQEmu/Server/pull/4620)) @Akkadius 2025-01-27
## [22.62.0] 1/26/2025
### Bazaar
* Improve Bazaar Search Performance ([#4615](https://github.com/EQEmu/Server/pull/4615)) @neckkola 2025-01-27
### CLI
* Add --skip-backup to world database:updates ([#4605](https://github.com/EQEmu/Server/pull/4605)) @Akkadius 2025-01-22
### Database
* Change npc_types walkspeed to be of type float ([#4589](https://github.com/EQEmu/Server/pull/4589)) @Akkadius 2025-01-07
### Databuckets
* Add Account Scoped Databuckets ([#4603](https://github.com/EQEmu/Server/pull/4603)) @Akkadius 2025-01-21
* Implement Nested Databuckets ([#4604](https://github.com/EQEmu/Server/pull/4604)) @Akkadius 2025-01-27
### Feature
* Add Alternate Bazaar Search Approach ([#4600](https://github.com/EQEmu/Server/pull/4600)) @neckkola 2025-01-20
* Add Support for Item Previews ([#4599](https://github.com/EQEmu/Server/pull/4599)) @Kinglykrab 2025-01-20
* Evolving Item Support for RoF2 ([#4496](https://github.com/EQEmu/Server/pull/4496)) @neckkola 2025-01-20
* Implement Custom Pet Names ([#4594](https://github.com/EQEmu/Server/pull/4594)) @catapultam-habeo 2025-01-22
### Fixes
* Add Bazaar BulkSendTrader Limit for RoF2 ([#4590](https://github.com/EQEmu/Server/pull/4590)) @neckkola 2025-01-08
* CLI help menu from parsing correctly in World @Akkadius 2025-01-22
* Delete later in RemoveItem second case @Akkadius 2025-01-25
* Fix query error in character_evolving_items @Akkadius 2025-01-21
* Repair a memory leak in #summonitem ([#4591](https://github.com/EQEmu/Server/pull/4591)) @neckkola 2025-01-08
* Repair an incorrect safe_delete call memory leak. ([#4588](https://github.com/EQEmu/Server/pull/4588)) @neckkola 2025-01-07
* Repair levers opening the Evolving XP Transfer Window ([#4607](https://github.com/EQEmu/Server/pull/4607)) @neckkola 2025-01-23
* Update a few Bazaar RoF2 routines for memory leaks ([#4592](https://github.com/EQEmu/Server/pull/4592)) @neckkola 2025-01-08
* Update database version to match manifest @Akkadius 2025-01-21
* Update trader add/remove packets to limits for RoF2 ([#4595](https://github.com/EQEmu/Server/pull/4595)) @neckkola 2025-01-19
### Linux
* Implement KSM Kernel Samepage Merging with Maps ([#4601](https://github.com/EQEmu/Server/pull/4601)) @Akkadius 2025-01-21
### Memory Leak
* Change raw pointer to unique_ptr to avoid potential leak in dbg stream ([#4616](https://github.com/EQEmu/Server/pull/4616)) @KimLS 2025-01-27
* Fix leak in BuyTraderItemOutsideBazaar ([#4609](https://github.com/EQEmu/Server/pull/4609)) @Akkadius 2025-01-24
* Fix leak in Client::RemoveDuplicateLore ([#4614](https://github.com/EQEmu/Server/pull/4614)) @Akkadius 2025-01-24
* Fix leak in NPC::RemoveItem ([#4611](https://github.com/EQEmu/Server/pull/4611)) @Akkadius 2025-01-24
* Fix leak in QuestManager::varlink ([#4610](https://github.com/EQEmu/Server/pull/4610)) @Akkadius 2025-01-24
* Fix leaks in Client::Handle_OP_AugmentItem ([#4612](https://github.com/EQEmu/Server/pull/4612)) @Akkadius 2025-01-24
* Fix memory leak in Client::Handle_OP_MoveMultipleItems ([#4613](https://github.com/EQEmu/Server/pull/4613)) @Akkadius 2025-01-24
### Performance
* Client / NPC Position Update Optimizations ([#4602](https://github.com/EQEmu/Server/pull/4602)) @Akkadius 2025-01-21
### Quest API
* Add SetAAEXPPercentage to Perl/Lua ([#4597](https://github.com/EQEmu/Server/pull/4597)) @Kinglykrab 2025-01-19
### Zone
* Implement zone player count sharding ([#4536](https://github.com/EQEmu/Server/pull/4536)) @Akkadius 2025-01-08
## [22.61.0] 1/6/2025
### Bots
+7 -5
View File
@@ -36,12 +36,14 @@
#include "../../common/file.h"
#include "../../common/events/player_event_logs.h"
#include "../../common/skill_caps.h"
#include "../../common/evolving_items.h"
EQEmuLogSys LogSys;
WorldContentService content_service;
ZoneStore zone_store;
PathManager path;
PlayerEventLogs player_event_logs;
EQEmuLogSys LogSys;
WorldContentService content_service;
ZoneStore zone_store;
PathManager path;
PlayerEventLogs player_event_logs;
EvolvingItemsManager evolving_items_manager;
void ExportSpells(SharedDatabase *db);
void ExportSkillCaps(SharedDatabase *db);
+7 -5
View File
@@ -30,12 +30,14 @@
#include "../../common/repositories/base_data_repository.h"
#include "../../common/file.h"
#include "../../common/events/player_event_logs.h"
#include "../../common/evolving_items.h"
EQEmuLogSys LogSys;
WorldContentService content_service;
ZoneStore zone_store;
PathManager path;
PlayerEventLogs player_event_logs;
EQEmuLogSys LogSys;
WorldContentService content_service;
ZoneStore zone_store;
PathManager path;
PlayerEventLogs player_event_logs;
EvolvingItemsManager evolving_items_manager;
void ImportSpells(SharedDatabase *db);
void ImportSkillCaps(SharedDatabase *db);
+7
View File
@@ -39,6 +39,7 @@ SET(common_sources
event_sub.cpp
events/player_event_logs.cpp
events/player_event_discord_formatter.cpp
evolving_items.cpp
expedition_lockout_timer.cpp
extprofile.cpp
discord/discord_manager.cpp
@@ -97,6 +98,7 @@ SET(common_sources
json/json.hpp
json/jsoncpp.cpp
zone_store.cpp
memory/ksm.hpp
net/console_server.cpp
net/console_server_connection.cpp
net/crc32.cpp
@@ -172,6 +174,7 @@ SET(repositories
repositories/base/base_character_currency_repository.h
repositories/base/base_character_data_repository.h
repositories/base/base_character_disciplines_repository.h
repositories/base/base_character_evolving_items_repository.h
repositories/base/base_character_expedition_lockouts_repository.h
repositories/base/base_character_exp_modifiers_repository.h
repositories/base/base_character_inspect_messages_repository.h
@@ -240,6 +243,7 @@ SET(repositories
repositories/base/base_inventory_snapshots_repository.h
repositories/base/base_ip_exemptions_repository.h
repositories/base/base_items_repository.h
repositories/base/base_items_evolving_details_repository.h
repositories/base/base_ldon_trap_entries_repository.h
repositories/base/base_ldon_trap_templates_repository.h
repositories/base/base_level_exp_mods_repository.h
@@ -355,6 +359,7 @@ SET(repositories
repositories/character_currency_repository.h
repositories/character_data_repository.h
repositories/character_disciplines_repository.h
repositories/character_evolving_items_repository.h
repositories/character_expedition_lockouts_repository.h
repositories/character_exp_modifiers_repository.h
repositories/character_inspect_messages_repository.h
@@ -423,6 +428,7 @@ SET(repositories
repositories/inventory_snapshots_repository.h
repositories/ip_exemptions_repository.h
repositories/items_repository.h
repositories/items_evolving_details_repository.h
repositories/ldon_trap_entries_repository.h
repositories/ldon_trap_templates_repository.h
repositories/level_exp_mods_repository.h
@@ -555,6 +561,7 @@ SET(common_headers
events/player_event_discord_formatter.h
events/player_events.h
event_sub.h
evolving_items.h
expedition_lockout_timer.h
extprofile.h
faction.h
+246 -284
View File
@@ -6,7 +6,8 @@
std::vector<BazaarSearchResultsFromDB_Struct>
Bazaar::GetSearchResults(
SharedDatabase &db,
Database &db,
Database &content_db,
BazaarSearchCriteria_Struct search,
uint32 char_zone_id,
int32 char_zone_instance_id
@@ -31,7 +32,132 @@ Bazaar::GetSearchResults(
char_zone_instance_id
);
std::string search_criteria_trader("TRUE ");
static std::map<uint8, uint32> item_slot_searches_new = {
{EQ::invslot::slotCharm, 1},
{EQ::invslot::slotEar1, 2},
{EQ::invslot::slotHead, 4},
{EQ::invslot::slotFace, 8},
{EQ::invslot::slotEar2, 16},
{EQ::invslot::slotNeck, 32},
{EQ::invslot::slotShoulders, 64},
{EQ::invslot::slotArms, 128},
{EQ::invslot::slotBack, 256},
{EQ::invslot::slotWrist1, 512},
{EQ::invslot::slotWrist2, 1024},
{EQ::invslot::slotRange, 2048},
{EQ::invslot::slotHands, 4096},
{EQ::invslot::slotPrimary, 8192},
{EQ::invslot::slotSecondary, 16384},
{EQ::invslot::slotFinger1, 32768},
{EQ::invslot::slotFinger2, 65536},
{EQ::invslot::slotChest, 131072},
{EQ::invslot::slotLegs, 262144},
{EQ::invslot::slotFeet, 524288},
{EQ::invslot::slotWaist, 1048576},
{EQ::invslot::slotPowerSource, 2097152},
{EQ::invslot::slotAmmo, 4194304},
};
struct ItemSearchType {
EQ::item::ItemType type;
std::string condition;
};
std::vector<ItemSearchType> item_search_types_new = {
{EQ::item::ItemType::ItemTypeBook, " AND (items.itemclass = 2 or items.itemclass = 31)"},
{EQ::item::ItemType::ItemTypeContainer, " AND (items.itemclass = 1 or items.itemclass = 67)"},
{EQ::item::ItemType::ItemTypeAllEffects, " AND (items.scrolleffect > 0 && items.scrolleffect < 65000)"},
{EQ::item::ItemType::ItemTypeUnknown9, " AND items.worneffect = 998"},
{EQ::item::ItemType::ItemTypeUnknown10, " AND (items.worneffect >= 1298 && items.worneffect <= 1307)"},
{EQ::item::ItemType::ItemTypeFocusEffect, " AND items.focuseffect > 0"},
{EQ::item::ItemType::ItemTypeArmor, " AND items.itemtype = 10"},
{EQ::item::ItemType::ItemType1HBlunt, " AND items.itemtype = 3"},
{EQ::item::ItemType::ItemType1HPiercing, " AND items.itemtype = 2"},
{EQ::item::ItemType::ItemType1HSlash, " AND items.itemtype = 0"},
{EQ::item::ItemType::ItemType2HBlunt, " AND items.itemtype = 4"},
{EQ::item::ItemType::ItemType2HSlash, " AND items.itemtype = 1"},
{EQ::item::ItemType::ItemTypeBow, " AND items.itemtype = 5"},
{EQ::item::ItemType::ItemTypeShield, " AND items.itemtype = 8"},
{EQ::item::ItemType::ItemTypeMisc, " AND items.itemtype = 11"},
{EQ::item::ItemType::ItemTypeFood, " AND items.itemtype = 14"},
{EQ::item::ItemType::ItemTypeDrink, " AND items.itemtype = 15"},
{EQ::item::ItemType::ItemTypeLight, " AND items.itemtype = 16"},
{EQ::item::ItemType::ItemTypeCombinable, " AND items.itemtype = 17"},
{EQ::item::ItemType::ItemTypeBandage, " AND items.itemtype = 18"},
{EQ::item::ItemType::ItemTypeSmallThrowing, " AND (items.itemtype = 19 OR items.itemtype = 7)"},
{EQ::item::ItemType::ItemTypeSpell, " AND items.itemtype = 20"},
{EQ::item::ItemType::ItemTypePotion, " AND items.itemtype = 21"},
{EQ::item::ItemType::ItemTypeBrassInstrument, " AND items.itemtype = 25"},
{EQ::item::ItemType::ItemTypeWindInstrument, " AND items.itemtype = 23"},
{EQ::item::ItemType::ItemTypeStringedInstrument, " AND items.itemtype = 24"},
{EQ::item::ItemType::ItemTypePercussionInstrument, " AND items.itemtype = 26"},
{EQ::item::ItemType::ItemTypeArrow, " AND items.itemtype = 27"},
{EQ::item::ItemType::ItemTypeJewelry, " AND items.itemtype = 29"},
{EQ::item::ItemType::ItemTypeNote, " AND items.itemtype = 32"},
{EQ::item::ItemType::ItemTypeKey, " AND items.itemtype = 33"},
{EQ::item::ItemType::ItemType2HPiercing, " AND items.itemtype = 35"},
{EQ::item::ItemType::ItemTypeAlcohol, " AND items.itemtype = 38"},
{EQ::item::ItemType::ItemTypeMartial, " AND items.itemtype = 45"},
{EQ::item::ItemType::ItemTypeAugmentation, " AND items.itemtype = 54"},
{EQ::item::ItemType::ItemTypeAlternateAbility, " AND items.itemtype = 57"},
{EQ::item::ItemType::ItemTypeCount, " AND items.itemtype = 65"},
{EQ::item::ItemType::ItemTypeCollectible, " AND items.itemtype = 66"}
};
// item stat searches
struct ItemStatSearch {
std::string query_string;
EQ::skills::SkillType skill_type;
};
std::map<uint32, ItemStatSearch> item_stat_searches_new = {
{STAT_AC, {" items.ac" , static_cast<EQ::skills::SkillType>(0)} },
{STAT_AGI, {" items.aagi", static_cast<EQ::skills::SkillType>(0)} },
{STAT_CHA, {" items.acha", static_cast<EQ::skills::SkillType>(0)} },
{STAT_DEX, {" items.adex", static_cast<EQ::skills::SkillType>(0)} },
{STAT_INT, {" items.aint", static_cast<EQ::skills::SkillType>(0)} },
{STAT_STA, {" items.asta", static_cast<EQ::skills::SkillType>(0)} },
{STAT_STR, {" items.astr", static_cast<EQ::skills::SkillType>(0)} },
{STAT_WIS, {" items.awis", static_cast<EQ::skills::SkillType>(0)} },
{STAT_COLD, {" items.cr", static_cast<EQ::skills::SkillType>(0)} },
{STAT_DISEASE, {" items.dr", static_cast<EQ::skills::SkillType>(0)} },
{STAT_FIRE, {" items.fr", static_cast<EQ::skills::SkillType>(0)} },
{STAT_MAGIC, {" items.mr", static_cast<EQ::skills::SkillType>(0)} },
{STAT_POISON, {" items.pr", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HP, {" items.hp", static_cast<EQ::skills::SkillType>(0)} },
{STAT_MANA, {" items.mana", static_cast<EQ::skills::SkillType>(0)} },
{STAT_ENDURANCE, {" items.endur", static_cast<EQ::skills::SkillType>(0)} },
{STAT_ATTACK, {" items.attack", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HP_REGEN, {" items.regen", static_cast<EQ::skills::SkillType>(0)} },
{STAT_MANA_REGEN, {" items.manaregen", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HASTE, {" items.haste", static_cast<EQ::skills::SkillType>(0)} },
{STAT_DAMAGE_SHIELD, {" items.damageshield", static_cast<EQ::skills::SkillType>(0)} },
{STAT_DS_MITIGATION, {" items.dsmitigation", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEAL_AMOUNT, {" items.healamt", static_cast<EQ::skills::SkillType>(0)} },
{STAT_SPELL_DAMAGE, {" items.spelldmg", static_cast<EQ::skills::SkillType>(0)} },
{STAT_CLAIRVOYANCE, {" items.clairvoyance", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_AGILITY, {" items.heroic_agi", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_CHARISMA, {" items.heroic_cha", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_DEXTERITY, {" items.heroic_dex", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_INTELLIGENCE, {" items.heroic_int", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_STAMINA, {" items.heroic_sta", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_STRENGTH, {" items.heroic_str", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_WISDOM, {" items.heroic_wis", static_cast<EQ::skills::SkillType>(0)} },
{STAT_BASH, {" items.skillmodvalue", EQ::skills::SkillBash} },
{STAT_BACKSTAB, {" items.backstabdmg", EQ::skills::SkillBackstab} },
{STAT_DRAGON_PUNCH, {" items.skillmodvalue", EQ::skills::SkillDragonPunch} },
{STAT_EAGLE_STRIKE, {" items.skillmodvalue", EQ::skills::SkillEagleStrike} },
{STAT_FLYING_KICK, {" items.skillmodvalue", EQ::skills::SkillFlyingKick} },
{STAT_KICK, {" items.skillmodvalue", EQ::skills::SkillKick} },
{STAT_ROUND_KICK, {" items.skillmodvalue", EQ::skills::SkillRoundKick} },
{STAT_TIGER_CLAW, {" items.skillmodvalue", EQ::skills::SkillTigerClaw} },
{STAT_FRENZY, {" items.skillmodvalue", EQ::skills::SkillFrenzy} },
};
bool convert = false;
std::string search_criteria_trader("TRUE");
std::string field_criteria_items("FALSE");
std::string where_criteria_items(" TRUE ");
if (search.search_scope == NonRoFBazaarSearchScope) {
search_criteria_trader.append(
@@ -51,8 +177,24 @@ Bazaar::GetSearchResults(
);
}
else if (search.trader_id > 0) {
search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id));
if (RuleB(Bazaar, UseAlternateBazaarSearch)) {
if (search.trader_id >= TraderRepository::TRADER_CONVERT_ID) {
convert = true;
search_criteria_trader.append(fmt::format(
" AND trader.char_zone_id = {} AND trader.char_zone_instance_id = {}",
Zones::BAZAAR,
search.trader_id - TraderRepository::TRADER_CONVERT_ID)
);
}
else {
search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id));
}
}
else {
search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id));
}
}
if (search.min_cost != 0) {
search_criteria_trader.append(fmt::format(" AND trader.item_cost >= {}", search.min_cost * 1000));
}
@@ -60,301 +202,121 @@ Bazaar::GetSearchResults(
search_criteria_trader.append(fmt::format(" AND trader.item_cost <= {}", (uint64) search.max_cost * 1000));
}
// not yet implemented
// if (search.prestige != 0) {
// 0xffffffff prestige only, 0xfffffffe non-prestige, 0 all
// search_criteria.append(fmt::format(" AND items.type = {} ", search.prestige));
// }
if (search.slot != std::numeric_limits<uint32>::max()) {
if (item_slot_searches_new.contains(search.slot)) {
where_criteria_items.append(
fmt::format(" AND items.slots & {0} = {0}", item_slot_searches_new[search.slot]));
}
}
std::string query = fmt::format(
"SELECT COUNT(item_id), trader.char_id, trader.item_id, trader.item_sn, trader.item_charges, trader.item_cost, "
"trader.slot_id, SUM(trader.item_charges), trader.char_zone_id, trader.char_entity_id, character_data.name, "
"aug_slot_1, aug_slot_2, aug_slot_3, aug_slot_4, aug_slot_5, aug_slot_6, trader.char_zone_instance_id "
"FROM trader, character_data "
"WHERE {} AND trader.char_id = character_data.id "
"GROUP BY trader.item_sn, trader.item_charges, trader.char_id",
search_criteria_trader.c_str()
);
if (search.type != std::numeric_limits<uint32>::max()) {
for (auto const &[type, condition]: item_search_types_new) {
if (type == search.type) {
where_criteria_items.append(condition);
break;
}
}
}
if (search.race != std::numeric_limits<uint32>::max()) {
where_criteria_items.append(
fmt::format(" AND items.races & {0} = {0}", GetPlayerRaceBit(GetRaceIDFromPlayerRaceValue(search.race))));
}
if (search._class != std::numeric_limits<uint32>::max()) {
where_criteria_items.append(fmt::format(" AND items.classes & {0} = {0}", GetPlayerClassBit(search._class)));
}
if (search.item_stat != std::numeric_limits<uint32>::max()) {
if (item_stat_searches_new.contains(search.item_stat)) {
field_criteria_items = fmt::format("{}", item_stat_searches_new[search.item_stat].query_string);
if (item_stat_searches_new[search.item_stat].skill_type) {
where_criteria_items.append(
fmt::format(" AND items.skillmodtype = {} ", item_stat_searches_new[search.item_stat].skill_type));
}
else {
where_criteria_items.append(
fmt::format(" AND {} > 0 ", item_stat_searches_new[search.item_stat].query_string));
}
}
}
if (search.augment) {
where_criteria_items.append(fmt::format(
" AND (items.augslot1type = {0} OR "
"items.augslot2type = {0} OR "
"items.augslot3type = {0} OR "
"items.augslot4type = {0} OR "
"items.augslot5type = {0} OR "
"items.augslot6type = {0})",
search.augment)
);
}
if (search.min_level != 1) {
where_criteria_items.append(fmt::format(" AND items.reclevel >= {}", search.min_level));
}
if (search.max_level != 100) {
where_criteria_items.append(fmt::format(" AND items.reclevel <= {}", search.max_level));
}
std::vector<BazaarSearchResultsFromDB_Struct> all_entries;
std::vector<std::string> trader_items_ids{};
auto results = db.QueryDatabase(query);
if (!results.Success()) {
auto const trader_results = TraderRepository::GetBazaarTraderDetails(db, search_criteria_trader);
if (trader_results.empty()) {
LogTradingDetail("Bazaar - No traders found in bazaar search.");
return all_entries;
}
struct ItemSearchType {
EQ::item::ItemType type;
bool condition;
};
for (auto const &i: trader_results) {
trader_items_ids.push_back(std::to_string(i.trader.item_id));
}
struct AddititiveSearchCriteria {
bool should_check;
bool condition;
};
auto const item_results = ItemsRepository::GetItemsForBazaarSearch(
content_db,
trader_items_ids,
std::string(search.item_name),
field_criteria_items,
where_criteria_items
);
if (item_results.empty()) {
LogError("Bazaar - No items found in bazaar search.");
return all_entries;
}
all_entries.reserve(trader_results.size());
for (auto const& t:trader_results) {
if (!item_results.contains(t.trader.item_id)) {
continue;
}
for (auto row: results) {
BazaarSearchResultsFromDB_Struct r{};
r.count = 1;
r.trader_id = t.trader.char_id;
r.serial_number = t.trader.item_sn;
r.cost = t.trader.item_cost;
r.slot_id = t.trader.slot_id;
r.sum_charges = t.trader.item_charges;
r.stackable = item_results.at(t.trader.item_id).stackable;
r.icon_id = item_results.at(t.trader.item_id).icon;
r.trader_zone_id = t.trader.char_zone_id;
r.trader_zone_instance_id = t.trader.char_zone_instance_id;
r.trader_entity_id = t.trader.char_entity_id;
r.serial_number_RoF = fmt::format("{:016}\0", t.trader.item_sn);
r.item_name = fmt::format("{:.63}\0", item_results.at(t.trader.item_id).name);
r.trader_name = fmt::format("{:.63}\0", t.trader_name);
r.item_stat = item_results.at(t.trader.item_id).stats;
r.item_id = Strings::ToInt(row[2]);
r.charges = Strings::ToInt(row[4]);
auto item = db.GetItem(r.item_id);
if (!item) {
continue;
}
uint32 aug_slot_1 = Strings::ToUnsignedInt(row[11]);
uint32 aug_slot_2 = Strings::ToUnsignedInt(row[12]);
uint32 aug_slot_3 = Strings::ToUnsignedInt(row[13]);
uint32 aug_slot_4 = Strings::ToUnsignedInt(row[14]);
uint32 aug_slot_5 = Strings::ToUnsignedInt(row[15]);
uint32 aug_slot_6 = Strings::ToUnsignedInt(row[16]);
std::unique_ptr<EQ::ItemInstance> inst(
db.CreateItem(
item,
r.charges,
aug_slot_1,
aug_slot_2,
aug_slot_3,
aug_slot_4,
aug_slot_5,
aug_slot_6
)
);
if (!inst->GetItem()) {
continue;
}
r.count = Strings::ToInt(row[0]);
r.trader_id = Strings::ToInt(row[1]);
r.serial_number = Strings::ToInt(row[3]);
r.cost = Strings::ToInt(row[5]);
r.slot_id = Strings::ToInt(row[6]);
r.sum_charges = Strings::ToInt(row[7]);
r.stackable = item->Stackable;
r.icon_id = item->Icon;
r.trader_zone_id = Strings::ToInt(row[8]);
r.trader_zone_instance_id = Strings::ToInt(row[17]);
r.trader_entity_id = Strings::ToInt(row[9]);
r.serial_number_RoF = fmt::format("{:016}\0", Strings::ToInt(row[3]));
r.item_name = fmt::format("{:.63}\0", item->Name);
r.trader_name = fmt::format("{:.63}\0", std::string(row[10]).c_str());
LogTradingDetail(
"Searching against item [{}] ({}) for trader [{}]",
item->Name,
item->ID,
r.trader_name
);
// item stat searches
std::map<uint32, uint32> item_stat_searches = {
{STAT_AC, inst->GetItemArmorClass(true)},
{STAT_AGI, static_cast<uint32>(inst->GetItemAgi(true))},
{STAT_CHA, static_cast<uint32>(inst->GetItemCha(true))},
{STAT_DEX, static_cast<uint32>(inst->GetItemDex(true))},
{STAT_INT, static_cast<uint32>(inst->GetItemInt(true))},
{STAT_STA, static_cast<uint32>(inst->GetItemSta(true))},
{STAT_STR, static_cast<uint32>(inst->GetItemStr(true))},
{STAT_WIS, static_cast<uint32>(inst->GetItemWis(true))},
{STAT_COLD, static_cast<uint32>(inst->GetItemCR(true))},
{STAT_DISEASE, static_cast<uint32>(inst->GetItemDR(true))},
{STAT_FIRE, static_cast<uint32>(inst->GetItemFR(true))},
{STAT_MAGIC, static_cast<uint32>(inst->GetItemMR(true))},
{STAT_POISON, static_cast<uint32>(inst->GetItemPR(true))},
{STAT_HP, static_cast<uint32>(inst->GetItemHP(true))},
{STAT_MANA, static_cast<uint32>(inst->GetItemMana(true))},
{STAT_ENDURANCE, static_cast<uint32>(inst->GetItemEndur(true))},
{STAT_ATTACK, static_cast<uint32>(inst->GetItemAttack(true))},
{STAT_HP_REGEN, static_cast<uint32>(inst->GetItemRegen(true))},
{STAT_MANA_REGEN, static_cast<uint32>(inst->GetItemManaRegen(true))},
{STAT_HASTE, static_cast<uint32>(inst->GetItemHaste(true))},
{STAT_DAMAGE_SHIELD, static_cast<uint32>(inst->GetItemDamageShield(true))},
{STAT_DS_MITIGATION, static_cast<uint32>(inst->GetItemDSMitigation(true))},
{STAT_HEAL_AMOUNT, static_cast<uint32>(inst->GetItemHealAmt(true))},
{STAT_SPELL_DAMAGE, static_cast<uint32>(inst->GetItemSpellDamage(true))},
{STAT_CLAIRVOYANCE, static_cast<uint32>(inst->GetItemClairvoyance(true))},
{STAT_HEROIC_AGILITY, static_cast<uint32>(inst->GetItemHeroicAgi(true))},
{STAT_HEROIC_CHARISMA, static_cast<uint32>(inst->GetItemHeroicCha(true))},
{STAT_HEROIC_DEXTERITY, static_cast<uint32>(inst->GetItemHeroicDex(true))},
{STAT_HEROIC_INTELLIGENCE, static_cast<uint32>(inst->GetItemHeroicInt(true))},
{STAT_HEROIC_STAMINA, static_cast<uint32>(inst->GetItemHeroicSta(true))},
{STAT_HEROIC_STRENGTH, static_cast<uint32>(inst->GetItemHeroicStr(true))},
{STAT_HEROIC_WISDOM, static_cast<uint32>(inst->GetItemHeroicWis(true))},
{STAT_BASH, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillBash, true))},
{STAT_BACKSTAB, static_cast<uint32>(inst->GetItemBackstabDamage(true))},
{STAT_DRAGON_PUNCH, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillDragonPunch, true))},
{STAT_EAGLE_STRIKE, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillEagleStrike, true))},
{STAT_FLYING_KICK, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillFlyingKick, true))},
{STAT_KICK, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillKick, true))},
{STAT_ROUND_KICK, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillRoundKick, true))},
{STAT_TIGER_CLAW, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillTigerClaw, true))},
{STAT_FRENZY, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillFrenzy, true))},
};
r.item_stat = item_stat_searches.contains(search.item_stat) ? item_stat_searches[search.item_stat] : 0;
if (item_stat_searches.contains(search.item_stat) && item_stat_searches[search.item_stat] <= 0) {
continue;
}
static std::map<uint8, uint32> item_slot_searches = {
{EQ::invslot::slotCharm, 1},
{EQ::invslot::slotEar1, 2},
{EQ::invslot::slotHead, 4},
{EQ::invslot::slotFace, 8},
{EQ::invslot::slotEar2, 16},
{EQ::invslot::slotNeck, 32},
{EQ::invslot::slotShoulders, 64},
{EQ::invslot::slotArms, 128},
{EQ::invslot::slotBack, 256},
{EQ::invslot::slotWrist1, 512},
{EQ::invslot::slotWrist2, 1024},
{EQ::invslot::slotRange, 2048},
{EQ::invslot::slotHands, 4096},
{EQ::invslot::slotPrimary, 8192},
{EQ::invslot::slotSecondary, 16384},
{EQ::invslot::slotFinger1, 32768},
{EQ::invslot::slotFinger2, 65536},
{EQ::invslot::slotChest, 131072},
{EQ::invslot::slotLegs, 262144},
{EQ::invslot::slotFeet, 524288},
{EQ::invslot::slotWaist, 1048576},
{EQ::invslot::slotPowerSource, 2097152},
{EQ::invslot::slotAmmo, 4194304},
};
auto GetEquipmentSlotBit = [&](uint32 slot) -> uint32 {
return item_slot_searches.contains(slot) ? item_slot_searches[slot] : 0;
};
auto FindItemAugSlot = [&]() -> bool {
for (auto const &s: inst->GetItem()->AugSlotType) {
return s == search.augment;
}
return false;
};
// item type searches
std::vector<ItemSearchType> item_search_types = {
{EQ::item::ItemType::ItemTypeAll, true},
{EQ::item::ItemType::ItemTypeBook, item->ItemClass == EQ::item::ItemType::ItemTypeBook},
{EQ::item::ItemType::ItemTypeContainer, item->ItemClass == EQ::item::ItemType::ItemTypeContainer ||
item->IsClassBag()},
{EQ::item::ItemType::ItemTypeAllEffects, item->Scroll.Effect > 0 && item->Scroll.Effect < 65000},
{EQ::item::ItemType::ItemTypeUnknown9, item->Worn.Effect == 998},
{EQ::item::ItemType::ItemTypeUnknown10, item->Worn.Effect >= 1298 && item->Worn.Effect <= 1307},
{EQ::item::ItemType::ItemTypeFocusEffect, item->Focus.Effect > 0},
{EQ::item::ItemType::ItemTypeArmor, item->ItemType == EQ::item::ItemType::ItemTypeArmor},
{EQ::item::ItemType::ItemType1HBlunt, item->ItemType == EQ::item::ItemType::ItemType1HBlunt},
{EQ::item::ItemType::ItemType1HPiercing, item->ItemType == EQ::item::ItemType::ItemType1HPiercing},
{EQ::item::ItemType::ItemType1HSlash, item->ItemType == EQ::item::ItemType::ItemType1HSlash},
{EQ::item::ItemType::ItemType2HBlunt, item->ItemType == EQ::item::ItemType::ItemType2HBlunt},
{EQ::item::ItemType::ItemType2HSlash, item->ItemType == EQ::item::ItemType::ItemType2HSlash},
{EQ::item::ItemType::ItemTypeBow, item->ItemType == EQ::item::ItemType::ItemTypeBow},
{EQ::item::ItemType::ItemTypeShield, item->ItemType == EQ::item::ItemType::ItemTypeShield},
{EQ::item::ItemType::ItemTypeMisc, item->ItemType == EQ::item::ItemType::ItemTypeMisc},
{EQ::item::ItemType::ItemTypeFood, item->ItemType == EQ::item::ItemType::ItemTypeFood},
{EQ::item::ItemType::ItemTypeDrink, item->ItemType == EQ::item::ItemType::ItemTypeDrink},
{EQ::item::ItemType::ItemTypeLight, item->ItemType == EQ::item::ItemType::ItemTypeLight},
{EQ::item::ItemType::ItemTypeCombinable, item->ItemType == EQ::item::ItemType::ItemTypeCombinable},
{EQ::item::ItemType::ItemTypeBandage, item->ItemType == EQ::item::ItemType::ItemTypeBandage},
{EQ::item::ItemType::ItemTypeSmallThrowing, item->ItemType == EQ::item::ItemType::ItemTypeSmallThrowing ||
item->ItemType == EQ::item::ItemType::ItemTypeLargeThrowing},
{EQ::item::ItemType::ItemTypeSpell, item->ItemType == EQ::item::ItemType::ItemTypeSpell},
{EQ::item::ItemType::ItemTypePotion, item->ItemType == EQ::item::ItemType::ItemTypePotion},
{EQ::item::ItemType::ItemTypeBrassInstrument, item->ItemType == EQ::item::ItemType::ItemTypeBrassInstrument},
{EQ::item::ItemType::ItemTypeWindInstrument, item->ItemType == EQ::item::ItemType::ItemTypeWindInstrument},
{EQ::item::ItemType::ItemTypeStringedInstrument, item->ItemType == EQ::item::ItemType::ItemTypeStringedInstrument},
{EQ::item::ItemType::ItemTypePercussionInstrument, item->ItemType == EQ::item::ItemType::ItemTypePercussionInstrument},
{EQ::item::ItemType::ItemTypeArrow, item->ItemType == EQ::item::ItemType::ItemTypeArrow},
{EQ::item::ItemType::ItemTypeJewelry, item->ItemType == EQ::item::ItemType::ItemTypeJewelry},
{EQ::item::ItemType::ItemTypeNote, item->ItemType == EQ::item::ItemType::ItemTypeNote},
{EQ::item::ItemType::ItemTypeKey, item->ItemType == EQ::item::ItemType::ItemTypeKey},
{EQ::item::ItemType::ItemType2HPiercing, item->ItemType == EQ::item::ItemType::ItemType2HPiercing},
{EQ::item::ItemType::ItemTypeAlcohol, item->ItemType == EQ::item::ItemType::ItemTypeAlcohol},
{EQ::item::ItemType::ItemTypeMartial, item->ItemType == EQ::item::ItemType::ItemTypeMartial},
{EQ::item::ItemType::ItemTypeAugmentation, item->ItemType == EQ::item::ItemType::ItemTypeAugmentation},
{EQ::item::ItemType::ItemTypeAlternateAbility, item->ItemType == EQ::item::ItemType::ItemTypeAlternateAbility},
{EQ::item::ItemType::ItemTypeCount, item->ItemType == EQ::item::ItemType::ItemTypeCount},
{EQ::item::ItemType::ItemTypeCollectible, item->ItemType == EQ::item::ItemType::ItemTypeCollectible}
};
bool met_filter = false;
bool has_filter = false;
for (auto &i: item_search_types) {
if (i.type == search.type) {
has_filter = true;
if (i.condition) {
LogTradingDetail("Item [{}] met search criteria for type [{}]", item->Name, uint8(i.type));
met_filter = true;
break;
}
}
}
if (has_filter && !met_filter) {
continue;
}
// TODO: Add catch-all item type filter for specific item types
// item additive searches
std::vector<AddititiveSearchCriteria> item_additive_searches = {
{
.should_check = search.min_level != 1 && inst->GetItemRequiredLevel(true) > 0,
.condition = inst->GetItemRequiredLevel(true) >= search.min_level
},
{
.should_check = search.max_level != 1 && inst->GetItemRequiredLevel(true) > 0,
.condition = inst->GetItemRequiredLevel(true) <= search.max_level
},
{
.should_check = !std::string(search.item_name).empty(),
.condition = Strings::ContainsLower(item->Name, search.item_name)
},
{
.should_check = search._class != 0xFFFFFFFF,
.condition = static_cast<bool>(item->Classes & GetPlayerClassBit(search._class))
},
{
.should_check = search.race != 0xFFFFFFFF,
.condition = static_cast<bool>(item->Races & GetPlayerRaceBit(GetRaceIDFromPlayerRaceValue(search.race)))
},
{
.should_check = search.augment != 0,
.condition = FindItemAugSlot()
},
{
.should_check = search.slot != 0xFFFFFFFF,
.condition = static_cast<bool>(item->Slots & GetEquipmentSlotBit(search.slot))
},
};
bool should_add = true;
for (auto &i: item_additive_searches) {
LogTradingDetail(
"Checking item [{}] for search criteria - should_check [{}] condition [{}]",
item->Name,
i.should_check,
i.condition
);
if (i.should_check && !i.condition) {
should_add = false;
continue;
if (RuleB(Bazaar, UseAlternateBazaarSearch)) {
if (convert || (r.trader_zone_id == Zones::BAZAAR && r.trader_zone_instance_id != char_zone_instance_id)) {
r.trader_id = TraderRepository::TRADER_CONVERT_ID + r.trader_zone_instance_id;
}
}
if (!should_add) {
continue;
}
LogTradingDetail("Found item [{}] meeting search criteria.", r.item_name);
all_entries.push_back(r);
}
+3 -1
View File
@@ -3,11 +3,13 @@
#include <vector>
#include "shareddb.h"
#include "../../common/item_instance.h"
class Bazaar {
public:
static std::vector<BazaarSearchResultsFromDB_Struct>
GetSearchResults(SharedDatabase &db, BazaarSearchCriteria_Struct search, unsigned int char_zone_id, int char_zone_instance_id);
GetSearchResults(Database &content_db, Database &db, BazaarSearchCriteria_Struct search, unsigned int char_zone_id, int char_zone_instance_id);
};
+7
View File
@@ -50,6 +50,7 @@
#include "../common/repositories/raid_members_repository.h"
#include "../common/repositories/reports_repository.h"
#include "../common/repositories/variables_repository.h"
#include "../common/repositories/character_pet_name_repository.h"
#include "../common/events/player_event_logs.h"
// Disgrace: for windows compile
@@ -313,6 +314,12 @@ bool Database::ReserveName(uint32 account_id, const std::string& name)
return false;
}
const auto& p = CharacterPetNameRepository::GetWhere(*this, where_filter);
if (!p.empty()) {
LogInfo("Account [{}] requested name [{}] but name is already taken by an Pet", account_id, name);
return false;
}
auto e = CharacterDataRepository::NewEntity();
e.account_id = account_id;
+11 -1
View File
@@ -169,7 +169,10 @@ bool DatabaseUpdate::UpdateManifest(
LogSys.EnableMySQLErrorLogs();
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
if (!missing_migrations.empty()) {
if (!missing_migrations.empty() && m_skip_backup) {
LogInfo("Skipping database backup");
}
else if (!missing_migrations.empty()) {
LogInfo("Automatically backing up database before applying updates");
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
auto s = DatabaseDumpService();
@@ -271,6 +274,13 @@ DatabaseUpdate *DatabaseUpdate::SetContentDatabase(Database *db)
return this;
}
DatabaseUpdate *DatabaseUpdate::SetSkipBackup(bool skip)
{
m_skip_backup = skip;
return this;
}
bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b)
{
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
+3
View File
@@ -29,12 +29,15 @@ public:
DatabaseUpdate *SetDatabase(Database *db);
DatabaseUpdate *SetContentDatabase(Database *db);
DatabaseUpdate *SetSkipBackup(bool skip);
bool HasPendingUpdates();
private:
bool m_skip_backup = false;
Database *m_database;
Database *m_content_database;
static bool CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b);
void InjectBotsVersionColumn();
};
#endif //EQEMU_DATABASE_UPDATE_H
+597 -1
View File
@@ -5782,7 +5782,603 @@ CREATE INDEX idx_bot_expires ON data_buckets (bot_id, expires);
ALTER TABLE `trader`
ADD COLUMN `char_zone_instance_id` INT NULL DEFAULT '0' AFTER `char_zone_id`;
)"
}
},
ManifestEntry{
.version = 9287,
.description = "2024_11_26_bazaar_find_trader.sql",
.check = "SHOW COLUMNS FROM `npc_types` LIKE 'walkspeed'",
.condition = "missing",
.match = "float",
.sql = R"(
ALTER TABLE `npc_types` MODIFY COLUMN `walkspeed` float NOT NULL DEFAULT 0;
)",
.content_schema_update = true
},
ManifestEntry{
.version = 9288,
.description = "2024_11_10_zone_player_partitioning.sql",
.check = "SHOW CREATE TABLE `zone`",
.condition = "missing",
.match = "shard_at_player_count",
.sql = R"(
ALTER TABLE `zone`
ADD COLUMN `shard_at_player_count` int(11) NULL DEFAULT 0 AFTER `seconds_before_idle`;
)",
.content_schema_update = true
},
ManifestEntry{
.version = 9289,
.description = "2025_01_19_evolving_items__character_evolving_items",
.check = "SHOW TABLES LIKE 'character_evolving_items'",
.condition = "empty",
.match = "",
.sql = R"(
CREATE TABLE `character_evolving_items` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`character_id` INT(10) UNSIGNED NULL DEFAULT '0',
`item_id` INT(10) UNSIGNED NULL DEFAULT '0',
`activated` TINYINT(1) UNSIGNED NULL DEFAULT '0',
`equipped` TINYINT(3) UNSIGNED NULL DEFAULT '0',
`current_amount` BIGINT(20) NULL DEFAULT '0',
`progression` DOUBLE(22,0) NULL DEFAULT '0',
`final_item_id` INT(10) UNSIGNED NULL DEFAULT '0',
`deleted_at` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1
;
)"
},
ManifestEntry{
.version = 9290,
.description = "2025_01_19_evolving_items__items_evolving_details",
.check = "SHOW TABLES LIKE 'items_evolving_details'",
.condition = "empty",
.match = "",
.sql = R"(
CREATE TABLE `items_evolving_details` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`item_evo_id` int(10) unsigned DEFAULT 0 COMMENT 'items.evoid',
`item_evolve_level` int(10) unsigned DEFAULT 0 COMMENT 'items.evolvinglevel',
`item_id` int(10) unsigned DEFAULT 0 COMMENT 'items.id',
`type` int(10) unsigned DEFAULT 0,
`sub_type` int(10) unsigned DEFAULT 0,
`required_amount` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
INSERT INTO `items_evolving_details` VALUES
(1,1000,1,86900,99,1,0),
(2,1000,2,86901,99,1,0),
(3,1000,3,86902,99,1,0),
(4,1000,4,86903,99,1,0),
(5,1000,5,86904,99,1,0),
(6,1001,1,86910,99,1,0),
(7,1001,2,86911,99,1,0),
(8,1001,3,86912,99,1,0),
(9,1001,4,86913,99,1,0),
(10,1001,5,86914,99,1,0),
(11,1001,6,86915,99,1,0),
(12,1001,7,86916,99,1,0),
(13,1002,1,86920,99,1,0),
(14,1002,2,86921,99,1,0),
(15,1002,3,86922,99,1,0),
(16,1002,4,86923,99,1,0),
(17,1002,5,86924,99,1,0),
(18,1002,6,86925,99,1,0),
(19,1002,7,86926,99,1,0),
(20,1003,1,86930,99,1,0),
(21,1003,2,86931,99,1,0),
(22,1003,3,86932,99,1,0),
(23,1003,4,86933,99,1,0),
(24,1003,5,86934,99,1,0),
(25,1003,6,86935,99,1,0),
(26,1004,1,86940,99,1,0),
(27,1004,2,86941,99,1,0),
(28,1004,3,86942,99,1,0),
(29,1004,4,86943,99,1,0),
(30,1004,5,86944,99,1,0),
(31,1004,6,86945,99,1,0),
(32,1005,1,86950,99,1,0),
(33,1005,2,86951,99,1,0),
(34,1005,3,86952,99,1,0),
(35,1005,4,86953,99,1,0),
(36,1005,5,86954,99,1,0),
(37,1005,6,86955,99,1,0),
(38,1005,7,86956,99,1,0),
(39,1006,1,86960,99,1,0),
(40,1006,2,86961,99,1,0),
(41,1006,3,86962,99,1,0),
(42,1006,4,86963,99,1,0),
(43,1006,5,86964,99,1,0),
(44,1006,6,86965,99,1,0),
(45,1006,7,86966,99,1,0),
(46,1007,1,86970,99,1,0),
(47,1007,2,86971,99,1,0),
(48,1007,3,86972,99,1,0),
(49,1007,4,86973,99,1,0),
(50,1007,5,86974,99,1,0),
(51,1007,6,86975,99,1,0),
(52,1007,7,86976,99,1,0),
(53,1008,1,86980,99,1,0),
(54,1008,2,86981,99,1,0),
(55,1008,3,86982,99,1,0),
(56,1008,4,86983,99,1,0),
(57,1008,5,86984,99,1,0),
(58,1009,1,86990,99,1,0),
(59,1009,2,86991,99,1,0),
(60,1009,3,86992,99,1,0),
(61,1009,4,86993,99,1,0),
(62,1009,5,86994,99,1,0),
(63,1009,6,86995,99,1,0),
(64,1009,7,86996,99,1,0),
(65,1009,8,86997,99,1,0),
(66,1009,9,86998,99,1,0),
(67,1009,10,86999,99,1,0),
(68,1010,1,90001,99,1,0),
(69,1010,2,90002,99,1,0),
(70,1010,3,90003,99,1,0),
(71,1010,6,90006,99,1,0),
(72,1010,7,90007,99,1,0),
(73,1010,8,90008,99,1,0),
(74,1010,10,90010,99,1,0),
(75,1050,1,89270,99,1,0),
(76,1050,2,89271,99,1,0),
(77,1050,3,89272,99,1,0),
(78,1050,4,89273,99,1,0),
(79,1050,5,89274,99,1,0),
(80,1050,6,89275,99,1,0),
(81,1051,1,89280,99,1,0),
(82,1051,2,89281,99,1,0),
(83,1051,3,89282,99,1,0),
(84,1051,4,89283,99,1,0),
(85,1051,5,89284,99,1,0),
(86,1052,1,89290,99,1,0),
(87,1052,2,89291,99,1,0),
(88,1052,3,89292,99,1,0),
(89,1052,4,89293,99,1,0),
(90,1052,5,89294,99,1,0),
(91,1052,6,89295,99,1,0),
(92,1052,7,89296,99,1,0),
(93,1053,1,89300,99,1,0),
(94,1053,2,89301,99,1,0),
(95,1053,3,89302,99,1,0),
(96,1053,4,89303,99,1,0),
(97,1053,5,89304,99,1,0),
(98,1053,6,89305,99,1,0),
(99,1053,7,89306,99,1,0),
(100,1053,8,89307,99,1,0),
(101,1053,9,89308,99,1,0),
(102,1054,1,89310,99,1,0),
(103,1054,2,89311,99,1,0),
(104,1054,3,89312,99,1,0),
(105,1054,4,89313,99,1,0),
(106,1054,5,89314,99,1,0),
(107,1054,6,89315,99,1,0),
(108,1055,1,89320,99,1,0),
(109,1055,2,89321,99,1,0),
(110,1055,3,89322,99,1,0),
(111,1055,4,89323,99,1,0),
(112,1055,5,89324,99,1,0),
(113,1055,6,89325,99,1,0),
(114,1055,7,89326,99,1,0),
(115,1056,1,89330,99,1,0),
(116,1056,2,89331,99,1,0),
(117,1056,3,89332,99,1,0),
(118,1056,4,89333,99,1,0),
(119,1056,5,89334,99,1,0),
(120,1057,1,89340,99,1,0),
(121,1057,2,89341,99,1,0),
(122,1057,3,89342,99,1,0),
(123,1057,4,89343,99,1,0),
(124,1057,5,89344,99,1,0),
(125,1057,6,89345,99,1,0),
(126,1057,7,89346,99,1,0),
(127,1057,8,89347,99,1,0),
(128,1058,1,89350,99,1,0),
(129,1058,2,89351,99,1,0),
(130,1058,3,89352,99,1,0),
(131,1058,4,89353,99,1,0),
(132,1058,5,89354,99,1,0),
(133,1058,6,89355,99,1,0),
(134,1058,7,89356,99,1,0),
(135,1059,1,89360,99,1,0),
(136,1059,2,89361,99,1,0),
(137,1059,3,89362,99,1,0),
(138,1060,1,89490,99,1,0),
(139,1060,2,89491,99,1,0),
(140,1060,3,89492,99,1,0),
(141,1061,1,89500,99,1,0),
(142,1061,2,89501,99,1,0),
(143,1061,3,89502,99,1,0),
(144,1062,1,89510,99,1,0),
(145,1062,2,89511,99,1,0),
(146,1062,3,89512,99,1,0),
(147,1063,1,89520,99,1,0),
(148,1063,2,89521,99,1,0),
(149,1063,3,89522,99,1,0),
(150,1064,1,89530,99,1,0),
(151,1064,2,89531,99,1,0),
(152,1064,3,89532,99,1,0),
(153,1065,1,89540,99,1,0),
(154,1065,2,89541,99,1,0),
(155,1065,3,89542,99,1,0),
(156,1066,1,89550,3,274,500),
(157,1066,2,89551,3,274,1000),
(158,1066,3,89552,3,274,2000),
(159,1067,1,89560,99,1,0),
(160,1067,2,89561,99,1,0),
(161,1067,3,89562,99,1,0),
(162,1069,1,85571,99,1,0),
(163,1069,2,85572,99,1,0),
(164,1069,3,85573,99,1,0),
(165,1200,1,95001,99,1,0),
(166,1200,2,95002,99,1,0),
(167,1200,3,95003,99,1,0),
(168,1200,4,95004,99,1,0),
(169,1200,5,95005,99,1,0),
(170,1200,6,95006,99,1,0),
(171,1200,7,95007,99,1,0),
(172,1201,1,95008,99,1,0),
(173,1201,2,95009,99,1,0),
(174,1201,3,95010,99,1,0),
(175,1201,4,95011,99,1,0),
(176,1201,5,95012,99,1,0),
(177,1201,6,95013,99,1,0),
(178,1201,7,95014,99,1,0),
(179,1202,1,95015,99,1,0),
(180,1202,2,95016,99,1,0),
(181,1202,3,95017,99,1,0),
(182,1202,4,95018,99,1,0),
(183,1202,5,95019,99,1,0),
(184,1202,6,95020,99,1,0),
(185,1202,7,95021,99,1,0),
(186,1203,1,95022,99,1,0),
(187,1203,2,95023,99,1,0),
(188,1203,3,95024,99,1,0),
(189,1203,4,95025,99,1,0),
(190,1203,5,95026,99,1,0),
(191,1203,6,95027,99,1,0),
(192,1203,7,95028,99,1,0),
(193,1204,1,95029,99,1,0),
(194,1204,2,95030,99,1,0),
(195,1204,3,95031,99,1,0),
(196,1204,4,95032,99,1,0),
(197,1204,5,95033,99,1,0),
(198,1204,6,95034,99,1,0),
(199,1204,7,95035,99,1,0),
(200,1205,1,95036,99,1,0),
(201,1205,2,95037,99,1,0),
(202,1205,3,95038,99,1,0),
(203,1205,4,95039,99,1,0),
(204,1205,5,95040,99,1,0),
(205,1205,6,95041,99,1,0),
(206,1205,7,95042,99,1,0),
(207,1206,1,95043,99,1,0),
(208,1206,2,95044,99,1,0),
(209,1206,3,95045,99,1,0),
(210,1206,4,95046,99,1,0),
(211,1206,5,95047,99,1,0),
(212,1206,6,95048,99,1,0),
(213,1206,7,95049,99,1,0),
(214,1207,1,95050,99,1,0),
(215,1207,2,95051,99,1,0),
(216,1207,3,95052,99,1,0),
(217,1207,4,95053,99,1,0),
(218,1207,5,95054,99,1,0),
(219,1207,6,95055,99,1,0),
(220,1207,7,95056,99,1,0),
(221,1208,1,95057,99,1,0),
(222,1208,2,95058,99,1,0),
(223,1208,3,95059,99,1,0),
(224,1208,4,95060,99,1,0),
(225,1208,5,95061,99,1,0),
(226,1208,6,95062,99,1,0),
(227,1208,7,95063,99,1,0),
(228,1209,1,95064,99,1,0),
(229,1209,2,95065,99,1,0),
(230,1209,3,95066,99,1,0),
(231,1209,4,95067,99,1,0),
(232,1209,5,95068,99,1,0),
(233,1209,6,95069,99,1,0),
(234,1209,7,95070,99,1,0),
(235,1210,1,95071,99,1,0),
(236,1210,2,95072,99,1,0),
(237,1210,3,95073,99,1,0),
(238,1210,4,95074,99,1,0),
(239,1210,5,95075,99,1,0),
(240,1210,6,95076,99,1,0),
(241,1210,7,95077,99,1,0),
(242,1211,1,85612,1,1,100000),
(243,1211,2,85613,1,1,200000),
(244,1211,3,85614,1,1,300000),
(245,1214,1,80035,99,1,0),
(246,1301,1,102700,99,1,0),
(247,1301,4,102703,99,1,0),
(248,1302,1,102704,99,1,0),
(249,1302,2,102705,99,1,0),
(250,1303,1,102706,99,1,0),
(251,1303,2,102707,99,1,0),
(252,1303,3,102708,99,1,0),
(253,1304,1,102709,99,1,0),
(254,1304,5,102713,99,1,0),
(255,1305,1,102714,99,1,0),
(256,1306,1,102716,99,1,0),
(257,1306,5,102720,99,1,0),
(258,1307,1,102721,99,1,0),
(259,1307,3,102723,99,1,0),
(260,1308,1,102724,99,1,0),
(261,1309,1,102727,99,1,0),
(262,1309,2,102728,99,1,0),
(263,1310,1,102729,99,1,0),
(264,1310,3,102731,99,1,0),
(265,1311,1,102732,99,1,0),
(266,1311,4,102735,99,1,0),
(267,1312,1,102736,99,1,0),
(268,1312,3,102738,99,1,0),
(269,1313,1,102739,99,1,0),
(270,1314,1,102743,99,1,0),
(271,1314,2,102744,99,1,0),
(272,1314,3,102745,99,1,0),
(273,1315,1,102746,99,1,0),
(274,1315,2,102747,99,1,0),
(275,1316,1,102748,99,1,0),
(276,1316,5,102752,99,1,0),
(277,1317,1,102753,99,1,0),
(278,1318,1,102756,99,1,0),
(279,1319,1,102759,99,1,0),
(280,1319,3,102761,99,1,0),
(281,1320,1,102762,99,1,0),
(282,1321,1,102765,99,1,0),
(283,1321,2,102766,99,1,0),
(284,1321,3,102767,99,1,0),
(285,1322,1,102768,99,1,0),
(286,1322,2,102769,99,1,0),
(287,1322,3,102770,99,1,0),
(288,1323,1,102771,99,1,0),
(289,1324,1,102774,99,1,0),
(290,1400,1,102800,99,1,0),
(291,1401,1,102807,99,1,0),
(292,1401,7,102813,99,1,0),
(293,1402,1,102814,99,1,0),
(294,1402,7,102820,99,1,0),
(295,1403,1,102821,99,1,0),
(296,1404,1,102828,99,1,0),
(297,1405,1,102835,99,1,0),
(298,1406,1,102842,99,1,0),
(299,1408,1,109310,99,1,0),
(300,1408,5,109314,99,1,0),
(301,1409,1,109315,99,1,0),
(302,1409,5,109319,99,1,0),
(303,1410,1,109320,99,1,0),
(304,1410,5,109324,99,1,0),
(305,1411,1,109325,99,1,0),
(306,1411,5,109329,99,1,0),
(307,1412,1,109330,99,1,0),
(308,1412,5,109334,99,1,0),
(309,1413,1,109335,99,1,0),
(310,1413,5,109339,99,1,0),
(311,1414,1,109340,99,1,0),
(312,1414,5,109344,99,1,0),
(313,1415,1,109345,99,1,0),
(314,1415,5,109349,99,1,0),
(315,1416,1,109350,99,1,0),
(316,1416,2,109351,99,1,0),
(317,1416,5,109354,99,1,0),
(318,1417,1,109355,99,1,0),
(319,1417,5,109359,99,1,0),
(320,1418,1,109360,99,1,0),
(321,1418,5,109364,99,1,0),
(322,1419,1,109365,99,1,0),
(323,1419,3,109367,99,1,0),
(324,1419,5,109369,99,1,0),
(325,1420,1,109370,99,1,0),
(326,1420,5,109374,99,1,0),
(327,1421,1,109375,99,1,0),
(328,1421,5,109379,99,1,0),
(329,1422,1,109380,99,1,0),
(330,1422,5,109384,99,1,0),
(331,1423,1,109385,99,1,0),
(332,1423,2,109386,99,1,0),
(333,1423,5,109389,99,1,0),
(334,1436,1,120378,99,1,0),
(335,1436,2,120379,99,1,0),
(336,1436,3,120380,99,1,0),
(337,1436,4,120381,99,1,0),
(338,1436,5,120382,99,1,0),
(339,1436,6,120383,99,1,0),
(340,1436,7,120384,99,1,0),
(341,1436,8,120385,99,1,0),
(342,1436,9,120386,99,1,0),
(343,1436,10,120387,99,1,0),
(344,1436,11,120388,99,1,0),
(345,1436,12,120389,99,1,0),
(346,1436,13,120390,99,1,0),
(347,1436,14,120391,99,1,0),
(348,1436,15,120392,99,1,0),
(349,1436,16,120393,99,1,0),
(350,1436,17,120394,99,1,0),
(351,1436,18,120395,99,1,0),
(352,1436,19,120396,99,1,0),
(353,1436,20,120397,99,1,0),
(354,1440,1,56992,99,1,0),
(355,1440,2,56993,99,1,0),
(356,1440,3,56994,99,1,0),
(357,1440,4,56995,99,1,0),
(358,1440,5,56996,99,1,0),
(359,1441,1,132787,99,1,0),
(360,1441,2,132788,99,1,0),
(361,1441,3,132789,99,1,0),
(362,1441,4,132790,99,1,0),
(363,1441,5,132791,99,1,0),
(364,1441,6,132792,99,1,0),
(365,1441,7,132793,99,1,0),
(366,1441,8,132794,99,1,0),
(367,1441,9,132795,99,1,0),
(368,1441,10,132796,99,1,0),
(369,1441,11,132797,99,1,0),
(370,1441,13,132799,99,1,0),
(371,1441,14,132800,99,1,0),
(372,1441,15,132801,99,1,0),
(373,1441,16,132802,99,1,0),
(374,1441,17,132803,99,1,0),
(375,1441,18,132804,99,1,0),
(376,1441,19,132805,99,1,0),
(377,1441,20,132806,99,1,0),
(378,1442,1,133137,99,1,0),
(379,1442,2,133138,99,1,0),
(380,1442,3,133139,99,1,0),
(381,1442,4,133140,99,1,0),
(382,1442,10,133146,99,1,0),
(383,1442,11,133147,99,1,0),
(384,1442,12,133148,99,1,0),
(385,1442,13,133149,99,1,0),
(386,1442,14,133150,99,1,0),
(387,1442,15,133151,99,1,0),
(388,1442,16,133152,99,1,0),
(389,1442,17,133153,99,1,0),
(390,1442,18,133154,99,1,0),
(391,1442,19,133155,99,1,0),
(392,1442,20,133156,99,1,0),
(393,1443,1,133406,99,1,0),
(394,1443,2,133407,99,1,0),
(395,1443,3,133408,99,1,0),
(396,1443,4,133409,99,1,0),
(397,1443,5,133410,99,1,0),
(398,1443,6,133411,99,1,0),
(399,1443,7,133412,99,1,0),
(400,1443,8,133413,99,1,0),
(401,1443,9,133414,99,1,0),
(402,1443,10,133415,99,1,0),
(403,1443,11,133416,99,1,0),
(404,1443,12,133417,99,1,0),
(405,1443,13,133418,99,1,0),
(406,1443,14,133419,99,1,0),
(407,1443,15,133420,99,1,0),
(408,1443,16,133421,99,1,0),
(409,1443,17,133422,99,1,0),
(410,1443,18,133423,99,1,0),
(411,1443,19,133424,99,1,0),
(412,1443,20,133425,99,1,0),
(413,1444,1,94938,99,1,0),
(414,1444,2,94939,99,1,0),
(415,1444,3,94940,99,1,0),
(416,1444,4,94941,99,1,0),
(417,1444,5,94942,99,1,0),
(418,1444,6,94943,99,1,0),
(419,1444,7,94944,99,1,0),
(420,1444,8,94945,99,1,0),
(421,1444,9,94946,99,1,0),
(422,1444,10,94947,99,1,0),
(423,1444,11,94948,99,1,0),
(424,1444,12,94949,99,1,0),
(425,1444,13,94950,99,1,0),
(426,1444,14,94951,99,1,0),
(427,1444,15,94952,99,1,0),
(428,1444,16,94953,99,1,0),
(429,1444,17,94954,99,1,0),
(430,1444,18,94955,99,1,0),
(431,1444,19,94956,99,1,0),
(432,1444,20,94957,99,1,0),
(433,1445,1,98858,99,1,0),
(434,1445,2,98859,99,1,0),
(435,1445,3,98860,99,1,0),
(436,1445,4,98861,99,1,0),
(437,1445,5,98862,99,1,0);
)",
.content_schema_update = true
},
ManifestEntry{
.version = 9291,
.description = "2025_01_21_add_remove_zone_fields",
.check = "SHOW COLUMNS FROM `zone` LIKE 'client_update_range'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE zone DROP COLUMN IF EXISTS npc_update_range;
ALTER TABLE zone DROP COLUMN IF EXISTS max_movement_update_range;
ALTER TABLE `zone` ADD COLUMN `client_update_range` int(11) NOT NULL DEFAULT 600 AFTER `npc_max_aggro_dist`;
)",
.content_schema_update = true,
},
ManifestEntry{
.version = 9292,
.description = "2025_01_21_data_buckets_account_id",
.check = "SHOW COLUMNS FROM `data_buckets` LIKE 'account_id'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `data_buckets`
ADD COLUMN `account_id` bigint(11) NULL DEFAULT 0 AFTER `expires`,
DROP INDEX `keys`,
ADD UNIQUE INDEX `keys` (`key`, `character_id`, `npc_id`, `bot_id`, `account_id`) USING BTREE;
-- Add the INDEX for character_id and key
ALTER TABLE `data_buckets` ADD KEY `idx_account_id_key` (`account_id`, `key`);
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9293,
.description = "2025_01_10_create_pet_names_table.sql",
.check = "SHOW TABLES LIKE 'character_pet_name'",
.condition = "empty",
.match = "",
.sql = R"(
CREATE TABLE `character_pet_name` (
`character_id` INT(11) NOT NULL PRIMARY KEY,
`name` VARCHAR(64) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
)",
},
ManifestEntry{
.version = 9294,
.description = "2025_01_26_items_table_bazaar_search_indexes.sql",
.check = "SHOW CREATE TABLE `items`",
.condition = "missing",
.match = "idx_slots_reclevel",
.sql = R"(
-- indexes for the `items` table
CREATE INDEX idx_slots_reclevel ON items (slots, reclevel);
CREATE INDEX idx_itemclass_itemtype ON items (itemclass, itemtype);
CREATE INDEX idx_augment_slots ON items (
augslot1type,
augslot2type,
augslot3type,
augslot4type,
augslot5type,
augslot6type
);
CREATE INDEX idx_races_classes ON items (races, classes);
-- common stat fields
CREATE INDEX idx_item_ac ON items (ac);
CREATE INDEX idx_item_hp ON items (hp);
CREATE INDEX idx_item_mana ON items (mana);
CREATE INDEX idx_item_reclevel ON items (reclevel);
CREATE INDEX idx_item_type_skill ON items (itemtype, skillmodtype);
)",
.content_schema_update = true
},
ManifestEntry{
.version = 9295,
.description = "2025_01_26_trader_table_bazaar_search_indexes.sql",
.check = "SHOW CREATE TABLE `trader`",
.condition = "missing",
.match = "idx_trader_item",
.sql = R"(
-- indexes for the `trader` table
CREATE INDEX idx_trader_item ON trader (item_id, item_cost);
CREATE INDEX idx_trader_char ON trader (char_id, char_zone_id, char_zone_instance_id);
CREATE INDEX idx_trader_item_sn ON trader (item_sn);
CREATE INDEX idx_trader_item_cost ON trader (item_cost);
CREATE INDEX idx_trader_active_transaction ON trader (active_transaction);
)",
.content_schema_update = false
},
// -- template; copy/paste this when you need to create a new entry
// ManifestEntry{
// .version = 9228,
+5 -5
View File
@@ -114,7 +114,9 @@ bool Database::CheckInstanceExpired(uint16 instance_id)
timeval tv{};
gettimeofday(&tv, nullptr);
return (i.start_time + i.duration) <= tv.tv_sec;
// Use uint64_t for the addition to prevent overflow
uint64_t expiration_time = static_cast<uint64_t>(i.start_time) + static_cast<uint64_t>(i.duration);
return expiration_time <= tv.tv_sec;
}
bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration)
@@ -469,15 +471,13 @@ void Database::AssignRaidToInstance(uint32 raid_id, uint32 instance_id)
void Database::DeleteInstance(uint16 instance_id)
{
// I'm not sure why this isn't in here but we should add it in a later change and make sure it's tested
// InstanceListRepository::DeleteWhere(*this, fmt::format("id = {}", instance_id));
InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id = {}", instance_id));
RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
DynamicZoneMembersRepository::DeleteByInstance(*this, instance_id);
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
CharacterCorpsesRepository::BuryInstance(*this, instance_id);
}
+4
View File
@@ -51,6 +51,7 @@ namespace DatabaseSchema {
{"character_enabledtasks", "charid"},
{"character_expedition_lockouts", "character_id"},
{"character_exp_modifiers", "character_id"},
{"character_evolving_items", "character_id"},
{"character_inspect_messages", "id"},
{"character_instance_safereturns", "character_id"},
{"character_item_recast", "id"},
@@ -63,6 +64,7 @@ namespace DatabaseSchema {
{"character_pet_buffs", "char_id"},
{"character_pet_info", "char_id"},
{"character_pet_inventory", "char_id"},
{"character_pet_name", "character_id"},
{"character_peqzone_flags", "id"},
{"character_potionbelt", "id"},
{"character_skills", "id"},
@@ -124,6 +126,7 @@ namespace DatabaseSchema {
"character_enabledtasks",
"character_expedition_lockouts",
"character_exp_modifiers",
"character_evolving_items",
"character_inspect_messages",
"character_instance_safereturns",
"character_item_recast",
@@ -212,6 +215,7 @@ namespace DatabaseSchema {
"ground_spawns",
"horses",
"items",
"items_evolving_details",
"ldon_trap_entries",
"ldon_trap_templates",
"lootdrop",
+9 -2
View File
@@ -35,7 +35,7 @@ N(OP_AltCurrencyMerchantRequest),
N(OP_AltCurrencyPurchase),
N(OP_AltCurrencyReclaim),
N(OP_AltCurrencySell),
N(OP_AltCurrencySellSelection), // Used by eqstr_us.txt 8066, 8068, 8069
N(OP_AltCurrencySellSelection), // Used by eqstr_us.txt 8066, 8068, 8069
N(OP_Animation),
N(OP_AnnoyingZoneUnknown),
N(OP_ApplyPoison),
@@ -77,6 +77,7 @@ N(OP_CashReward),
N(OP_CastSpell),
N(OP_ChangeSize),
N(OP_ChannelMessage),
N(OP_ChangePetName),
N(OP_CharacterCreate),
N(OP_CharacterCreateRequest),
N(OP_CharInventory),
@@ -162,6 +163,7 @@ N(OP_EnduranceUpdate),
N(OP_EnterChat),
N(OP_EnterWorld),
N(OP_EnvDamage),
N(OP_EvolveItem),
N(OP_ExpansionInfo),
N(OP_ExpUpdate),
N(OP_FaceChange),
@@ -283,12 +285,15 @@ N(OP_InspectMessageUpdate),
N(OP_InspectRequest),
N(OP_InstillDoubt),
N(OP_InterruptCast),
N(OP_InvokeChangePetName),
N(OP_InvokeChangePetNameImmediate),
N(OP_ItemLinkClick),
N(OP_ItemLinkResponse),
N(OP_ItemLinkText),
N(OP_ItemName),
N(OP_ItemPacket),
N(OP_ItemPreview),
N(OP_ItemPreviewRequest),
N(OP_ItemRecastDelay),
N(OP_ItemVerifyReply),
N(OP_ItemVerifyRequest),
@@ -400,6 +405,8 @@ N(OP_PetitionSearchText),
N(OP_PetitionUnCheckout),
N(OP_PetitionUpdate),
N(OP_PickPocket),
N(OP_PickZone),
N(OP_PickZoneWindow),
N(OP_PlayerProfile),
N(OP_PlayerStateAdd),
N(OP_PlayerStateRemove),
@@ -514,7 +521,7 @@ N(OP_ShopPlayerSell),
N(OP_ShopSendParcel),
N(OP_ShopDeleteParcel),
N(OP_ShopRespondParcel),
N(OP_ShopRetrieveParcel),
N(OP_ShopRetrieveParcel),
N(OP_ShopParcelIcon),
N(OP_ShopRequest),
N(OP_SimpleMessage),
+1
View File
@@ -1114,4 +1114,5 @@ enum ExpSource
namespace DoorType {
constexpr uint32 BuyerStall = 155;
}
#endif /*COMMON_EQ_CONSTANTS_H*/
+14 -6
View File
@@ -47,6 +47,7 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
ClientUnknown::constants::EXPANSION_BIT,
ClientUnknown::constants::EXPANSIONS_MASK,
ClientUnknown::INULL,
ClientUnknown::INULL,
ClientUnknown::INULL
),
/*[ClientVersion::Client62] =*/
@@ -55,6 +56,7 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
Client62::constants::EXPANSION_BIT,
Client62::constants::EXPANSIONS_MASK,
Client62::INULL,
Client62::INULL,
Client62::INULL
),
/*[ClientVersion::Titanium] =*/
@@ -63,7 +65,8 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
Titanium::constants::EXPANSION_BIT,
Titanium::constants::EXPANSIONS_MASK,
Titanium::constants::CHARACTER_CREATION_LIMIT,
Titanium::constants::SAY_LINK_BODY_SIZE
Titanium::constants::SAY_LINK_BODY_SIZE,
Titanium::INULL
),
/*[ClientVersion::SoF] =*/
EQ::constants::LookupEntry(
@@ -71,7 +74,8 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
SoF::constants::EXPANSION_BIT,
SoF::constants::EXPANSIONS_MASK,
SoF::constants::CHARACTER_CREATION_LIMIT,
SoF::constants::SAY_LINK_BODY_SIZE
SoF::constants::SAY_LINK_BODY_SIZE,
SoF::INULL
),
/*[ClientVersion::SoD] =*/
EQ::constants::LookupEntry(
@@ -79,7 +83,8 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
SoD::constants::EXPANSION_BIT,
SoD::constants::EXPANSIONS_MASK,
SoD::constants::CHARACTER_CREATION_LIMIT,
SoD::constants::SAY_LINK_BODY_SIZE
SoD::constants::SAY_LINK_BODY_SIZE,
SoD::INULL
),
/*[ClientVersion::UF] =*/
EQ::constants::LookupEntry(
@@ -87,7 +92,8 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
UF::constants::EXPANSION_BIT,
UF::constants::EXPANSIONS_MASK,
UF::constants::CHARACTER_CREATION_LIMIT,
UF::constants::SAY_LINK_BODY_SIZE
UF::constants::SAY_LINK_BODY_SIZE,
UF::INULL
),
/*[ClientVersion::RoF] =*/
EQ::constants::LookupEntry(
@@ -95,7 +101,8 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
RoF::constants::EXPANSION_BIT,
RoF::constants::EXPANSIONS_MASK,
RoF::constants::CHARACTER_CREATION_LIMIT,
RoF::constants::SAY_LINK_BODY_SIZE
RoF::constants::SAY_LINK_BODY_SIZE,
RoF::INULL
),
/*[ClientVersion::RoF2] =*/
EQ::constants::LookupEntry(
@@ -103,7 +110,8 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
RoF2::constants::EXPANSION_BIT,
RoF2::constants::EXPANSIONS_MASK,
RoF2::constants::CHARACTER_CREATION_LIMIT,
RoF2::constants::SAY_LINK_BODY_SIZE
RoF2::constants::SAY_LINK_BODY_SIZE,
RoF2::constants::MAX_BAZAAR_TRADERS
)
};
+5 -2
View File
@@ -42,6 +42,7 @@ namespace EQ
uint32 ExpansionsMask;
int16 CharacterCreationLimit;
size_t SayLinkBodySize;
uint32 BazaarTraderLimit;
LookupEntry(const LookupEntry *lookup_entry) { }
LookupEntry(
@@ -49,13 +50,15 @@ namespace EQ
uint32 ExpansionBit,
uint32 ExpansionsMask,
int16 CharacterCreationLimit,
size_t SayLinkBodySize
size_t SayLinkBodySize,
uint32 BazaarTraderLimit
) :
Expansion(Expansion),
ExpansionBit(ExpansionBit),
ExpansionsMask(ExpansionsMask),
CharacterCreationLimit(CharacterCreationLimit),
SayLinkBodySize(SayLinkBodySize)
SayLinkBodySize(SayLinkBodySize),
BazaarTraderLimit(BazaarTraderLimit)
{ }
};
+99
View File
@@ -5819,6 +5819,21 @@ struct ChangeSize_Struct
/*16*/
};
struct ChangePetName_Struct {
/*00*/ char new_pet_name[64];
/*40*/ char pet_owner_name[64];
/*80*/ int response_code;
};
enum ChangePetNameResponse : int {
Denied = 0, // 5167 You have requested an invalid name or a Customer Service Representative has denied your name request. Please try another name.
Accepted = 1, // 5976 Your request for a name change was successful.
Timeout = -3, // 5979 You must wait longer before submitting another name request. Please try again in a few minutes.
NotEligible = -4, // 5980 Your character is not eligible for a name change.
Pending = -5, // 5193 You already have a name change pending. Please wait until it is fully processed before attempting another name change.
Unhandled = -1
};
// New OpCode/Struct for SoD+
struct GroupMakeLeader_Struct
{
@@ -6440,6 +6455,90 @@ struct BuylineItemDetails_Struct {
uint32 item_quantity;
};
struct PickZoneEntry_Struct {
int16 zone_id;
int16 unknown;
int32 player_count;
int32 instance_id;
};
struct PickZoneWindow_Struct {
char padding000[64];
int64 session_id;
int8 option_count;
char padding073[23];
PickZoneEntry_Struct entries[10];
};
struct PickZone_Struct {
int64 session_id;
int32 selection_id;
};
struct EvolveItemToggle {
uint32 action;
uint32 unknown_004;
uint64 unique_id;
uint32 percentage;
uint32 activated;
};
struct EvolveXPWindowReceive {
uint32 action;
uint32 unknown_004;
uint64 item1_unique_id;
uint64 item2_unique_id;
};
struct EvolveItemMessaging {
uint32 action;
char serialized_data[];
};
struct EvolveXPWindowSend {
/*000*/ uint32 action;
/*004*/ uint64 item1_unique_id;
/*012*/ uint64 item2_unique_id;
/*020*/ uint32 compatibility;
/*024*/ uint32 max_transfer_level;
/*028*/ uint8 item1_present;
/*029*/ uint8 item2_present;
/*030*/ std::string serialize_item_1;
/*034*/ std::string serialize_item_2;
template<class Archive>
void serialize(Archive &archive)
{
archive(
CEREAL_NVP(action),
CEREAL_NVP(item1_unique_id),
CEREAL_NVP(item2_unique_id),
CEREAL_NVP(compatibility),
CEREAL_NVP(max_transfer_level),
CEREAL_NVP(item1_present),
CEREAL_NVP(item2_present),
CEREAL_NVP(serialize_item_1),
CEREAL_NVP(serialize_item_2)
);
}
};
struct EvolveTransfer {
uint32 item_from_id;
uint32 item_from_current_amount;
uint32 item_to_id;
uint32 item_to_current_amount;
uint32 compatibility;
uint32 max_transfer_level;
};
struct EvolveGetNextItem {
uint32 new_item_id;
uint64 new_current_amount;
uint64 from_current_amount;
uint32 max_transfer_level;
};
// Restore structure packing to default
#pragma pack()
+7 -1
View File
@@ -142,6 +142,9 @@ namespace Logs {
EqTime,
Corpses,
XTargets,
EvolveItem,
PositionUpdate,
KSM,
MaxCategoryID /* Don't Remove this */
};
@@ -242,7 +245,10 @@ namespace Logs {
"Zoning",
"EqTime",
"Corpses",
"XTargets"
"XTargets",
"EvolveItem",
"PositionUpdate",
"KSM" // Kernel Samepage Merging
};
}
+30
View File
@@ -131,6 +131,16 @@
OutF(LogSys, Logs::Detail, Logs::Error, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogEvolveItem(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::General, Logs::EvolveItem))\
OutF(LogSys, Logs::General, Logs::EvolveItem, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogEvolveItemDetail(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::Detail, Logs::EvolveItem))\
OutF(LogSys, Logs::Detail, Logs::EvolveItem, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogGuilds(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::General, Logs::Guilds))\
OutF(LogSys, Logs::General, Logs::Guilds, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
@@ -844,6 +854,26 @@
OutF(LogSys, Logs::Detail, Logs::XTargets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogPositionUpdate(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::General, Logs::PositionUpdate))\
OutF(LogSys, Logs::General, Logs::PositionUpdate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogPositionUpdateDetail(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::Detail, Logs::PositionUpdate))\
OutF(LogSys, Logs::Detail, Logs::PositionUpdate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__); \
} while (0)
#define LogKSM(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::General, Logs::KSM))\
OutF(LogSys, Logs::General, Logs::KSM, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogKSMDetail(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::Detail, Logs::KSM))\
OutF(LogSys, Logs::Detail, Logs::KSM, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define Log(debug_level, log_category, message, ...) do {\
if (LogSys.IsLogEnabled(debug_level, log_category))\
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
+1
View File
@@ -707,6 +707,7 @@ void PlayerEventLogs::SetSettingsDefaults()
m_settings[PlayerEvent::PARCEL_RETRIEVE].event_enabled = 1;
m_settings[PlayerEvent::PARCEL_DELETE].event_enabled = 1;
m_settings[PlayerEvent::BARTER_TRANSACTION].event_enabled = 1;
m_settings[PlayerEvent::EVOLVE_ITEM].event_enabled = 1;
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
m_settings[i].retention_days = RETENTION_DAYS_DEFAULT;
+26 -1
View File
@@ -62,6 +62,7 @@ namespace PlayerEvent {
PARCEL_RETRIEVE,
PARCEL_DELETE,
BARTER_TRANSACTION,
EVOLVE_ITEM,
MAX // dont remove
};
@@ -124,7 +125,8 @@ namespace PlayerEvent {
"Parcel Item Sent",
"Parcel Item Retrieved",
"Parcel Prune Routine",
"Barter Transaction"
"Barter Transaction",
"Evolve Item Update"
};
// Generic struct used by all events
@@ -1115,6 +1117,29 @@ namespace PlayerEvent {
);
}
};
struct EvolveItem {
std::string status;
uint32 item_id;
uint64 unique_id;
std::string item_name;
uint32 level;
double progression;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(status),
CEREAL_NVP(item_id),
CEREAL_NVP(unique_id),
CEREAL_NVP(item_name),
CEREAL_NVP(level),
CEREAL_NVP(progression)
);
}
};
}
#endif //EQEMU_PLAYER_EVENTS_H
+303
View File
@@ -0,0 +1,303 @@
#include "evolving_items.h"
#include "item_instance.h"
#include "events/player_event_logs.h"
#include "repositories/character_evolving_items_repository.h"
EvolvingItemsManager::EvolvingItemsManager()
{
m_db = nullptr;
m_content_db = nullptr;
}
void EvolvingItemsManager::LoadEvolvingItems() const
{
auto const &results = ItemsEvolvingDetailsRepository::All(*m_content_db);
if (results.empty()) {
return;
}
std::ranges::transform(
results.begin(),
results.end(),
std::inserter(
evolving_items_manager.GetEvolvingItemsCache(),
evolving_items_manager.GetEvolvingItemsCache().end()
),
[](const ItemsEvolvingDetailsRepository::ItemsEvolvingDetails &x) {
return std::make_pair(x.item_id, x);
}
);
}
void EvolvingItemsManager::SetDatabase(Database *db)
{
m_db = db;
}
void EvolvingItemsManager::SetContentDatabase(Database *db)
{
m_content_db = db;
}
double EvolvingItemsManager::CalculateProgression(const uint64 current_amount, const uint32 item_id)
{
if (!evolving_items_manager.GetEvolvingItemsCache().contains(item_id)) {
return 0;
}
return evolving_items_manager.GetEvolvingItemsCache().at(item_id).required_amount > 0
? static_cast<double>(current_amount)
/ static_cast<double>(evolving_items_manager.GetEvolvingItemsCache().at(item_id).required_amount) * 100
: 0;
}
void EvolvingItemsManager::DoLootChecks(const uint32 char_id, const uint16 slot_id, const EQ::ItemInstance &inst) const
{
inst.SetEvolveEquipped(false);
if (inst.IsEvolving() && slot_id <= EQ::invslot::EQUIPMENT_END && slot_id >= EQ::invslot::EQUIPMENT_BEGIN) {
inst.SetEvolveEquipped(true);
}
if (!inst.IsEvolving()) {
return;
}
if (!inst.GetEvolveUniqueID()) {
auto e = CharacterEvolvingItemsRepository::NewEntity();
e.character_id = char_id;
e.item_id = inst.GetID();
e.equipped = inst.GetEvolveEquipped();
e.final_item_id = evolving_items_manager.GetFinalItemID(inst);
auto r = CharacterEvolvingItemsRepository::InsertOne(*m_db, e);
e.id = r.id;
inst.SetEvolveUniqueID(e.id);
inst.SetEvolveCharID(e.character_id);
inst.SetEvolveItemID(e.item_id);
inst.SetEvolveFinalItemID(e.final_item_id);
return;
}
CharacterEvolvingItemsRepository::SetEquipped(*m_db, inst.GetEvolveUniqueID(), inst.GetEvolveEquipped());
}
uint32 EvolvingItemsManager::GetFinalItemID(const EQ::ItemInstance &inst) const
{
const auto start_iterator = std::ranges::find_if(
evolving_items_manager.GetEvolvingItemsCache().cbegin(),
evolving_items_manager.GetEvolvingItemsCache().cend(),
[&](const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &a) {
return a.second.item_evo_id == inst.GetEvolveLoreID();
}
);
if (start_iterator == std::end(evolving_items_manager.GetEvolvingItemsCache())) {
return 0;
}
const auto final_id = std::ranges::max_element(
start_iterator,
evolving_items_manager.GetEvolvingItemsCache().cend(),
[&](
const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &a,
const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &b
) {
return a.second.item_evo_id == b.second.item_evo_id &&
a.second.item_evolve_level < b.second.item_evolve_level;
}
);
return final_id->first;
}
uint32 EvolvingItemsManager::GetNextEvolveItemID(const EQ::ItemInstance &inst) const
{
int8 const current_level = inst.GetEvolveLvl();
const auto iterator = std::ranges::find_if(
evolving_items_manager.GetEvolvingItemsCache().cbegin(),
evolving_items_manager.GetEvolvingItemsCache().cend(),
[&](const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &a) {
return a.second.item_evo_id == inst.GetEvolveLoreID() &&
a.second.item_evolve_level == current_level + 1;
}
);
if (iterator == std::end(evolving_items_manager.GetEvolvingItemsCache())) {
return 0;
}
return iterator->first;
}
ItemsEvolvingDetailsRepository::ItemsEvolvingDetails EvolvingItemsManager::GetEvolveItemDetails(const uint64 unique_id)
{
if (GetEvolvingItemsCache().contains(unique_id)) {
return GetEvolvingItemsCache().at(unique_id);
}
return ItemsEvolvingDetailsRepository::NewEntity();
}
std::vector<ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> EvolvingItemsManager::GetEvolveIDItems(
const uint32 evolve_id
)
{
std::vector<ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> e{};
for (auto const &[key, value]: GetEvolvingItemsCache()) {
if (value.item_evo_id == evolve_id) {
e.push_back(value);
}
}
std::ranges::sort(
e.begin(),
e.end(),
[&](
ItemsEvolvingDetailsRepository::ItemsEvolvingDetails const &a,
ItemsEvolvingDetailsRepository::ItemsEvolvingDetails const &b
) {
return a.item_evolve_level < b.item_evolve_level;
}
);
return e;
}
uint64 EvolvingItemsManager::GetTotalEarnedXP(const EQ::ItemInstance &inst)
{
if (!inst) {
return 0;
}
uint64 xp = inst.GetEvolveCurrentAmount();
auto evolve_id_item_cache = GetEvolveIDItems(inst.GetEvolveLoreID());
auto current_level = inst.GetEvolveLvl();
for (auto const &i: evolve_id_item_cache) {
if (i.item_evolve_level < current_level) {
xp += i.required_amount;
}
}
return xp;
}
EvolveGetNextItem EvolvingItemsManager::GetNextItemByXP(const EQ::ItemInstance &inst_in, const int64 in_xp)
{
EvolveGetNextItem ets{};
const auto evolve_items = GetEvolveIDItems(inst_in.GetEvolveLoreID());
uint32 max_transfer_level = 0;
int64 xp = in_xp;
for (auto const &e: evolve_items) {
if (e.item_evolve_level < inst_in.GetEvolveLvl()) {
continue;
}
int64 have = 0;
if (e.item_evolve_level == inst_in.GetEvolveLvl()) {
have = inst_in.GetEvolveCurrentAmount();
}
const auto required = e.required_amount;
const int64 need = required - have;
const int64 balance = xp - need;
if (balance <= 0) {
ets.new_current_amount = have + xp;
ets.new_item_id = e.item_id;
ets.from_current_amount = 0;
ets.max_transfer_level = max_transfer_level;
return ets;
}
xp = balance;
max_transfer_level += 1;
ets.new_current_amount = required;
ets.new_item_id = e.item_id;
ets.from_current_amount = balance - required;
ets.max_transfer_level = max_transfer_level;
}
return ets;
}
EvolveTransfer EvolvingItemsManager::DetermineTransferResults(
const EQ::ItemInstance &inst_from,
const EQ::ItemInstance &inst_to
)
{
EvolveTransfer ets{};
auto evolving_details_inst_from = evolving_items_manager.GetEvolveItemDetails(inst_from.GetID());
auto evolving_details_inst_to = evolving_items_manager.GetEvolveItemDetails(inst_to.GetID());
if (!evolving_details_inst_from.id || !evolving_details_inst_to.id) {
return ets;
}
if (evolving_details_inst_from.type == evolving_details_inst_to.type) {
uint32 compatibility = 0;
uint64 xp = 0;
if (evolving_details_inst_from.sub_type == evolving_details_inst_to.sub_type) {
compatibility = 100;
}
else {
compatibility = 30;
}
xp = evolving_items_manager.GetTotalEarnedXP(inst_from) * compatibility / 100;
auto results = evolving_items_manager.GetNextItemByXP(inst_to, xp);
ets.item_from_id = evolving_items_manager.GetFirstItemInLoreGroup(inst_from.GetEvolveLoreID());
ets.item_from_current_amount = results.from_current_amount;
ets.item_to_id = results.new_item_id;
ets.item_to_current_amount = results.new_current_amount;
ets.compatibility = compatibility;
ets.max_transfer_level = results.max_transfer_level;
}
return ets;
}
uint32 EvolvingItemsManager::GetFirstItemInLoreGroup(const uint32 lore_id)
{
for (auto const &[key, value]: GetEvolvingItemsCache()) {
if (value.item_evo_id == lore_id && value.item_evolve_level == 1) {
return key;
}
}
return 0;
}
uint32 EvolvingItemsManager::GetFirstItemInLoreGroupByItemID(const uint32 item_id)
{
for (auto const &[key, value]: GetEvolvingItemsCache()) {
if (value.item_id == item_id) {
for (auto const &[key2, value2]: GetEvolvingItemsCache()) {
if (value2.item_evo_id == value.item_evo_id && value2.item_evolve_level == 1) {
return key;
}
}
}
}
return 0;
}
void EvolvingItemsManager::LoadPlayerEvent(const EQ::ItemInstance &inst, PlayerEvent::EvolveItem &e)
{
e.item_id = inst.GetID();
e.item_name = inst.GetItem() ? inst.GetItem()->Name : std::string();
e.level = inst.GetEvolveLvl();
e.progression = inst.GetEvolveProgression();
e.unique_id = inst.GetEvolveUniqueID();
}
+67
View File
@@ -0,0 +1,67 @@
#ifndef EVOLVING_H
#define EVOLVING_H
#include "shareddb.h"
#include "events/player_events.h"
#include "repositories/items_evolving_details_repository.h"
namespace EQ {
class ItemInstance;
}
namespace EvolvingItems {
namespace Actions {
constexpr int8 UPDATE_ITEMS = 0;
constexpr int8 TRANSFER_WINDOW_OPEN = 1;
constexpr int8 TRANSFER_WINDOW_DETAILS = 2;
constexpr int8 TRANSFER_XP = 3;
constexpr int8 FINAL_RESULT = 4;
}
namespace Types {
constexpr int8 AMOUNT_OF_EXP = 1;
constexpr int8 NUMBER_OF_KILLS = 2;
constexpr int8 SPECIFIC_MOB_RACE = 3;
constexpr int8 SPECIFIC_ZONE_ID = 4;
}
namespace SubTypes {
constexpr int8 ALL_EXP = 0;
constexpr int8 SOLO_EXP = 1;
constexpr int8 GROUP_EXP = 2;
constexpr int8 RAID_EXP = 3;
}
}
class EvolvingItemsManager
{
public:
EvolvingItemsManager();
void SetDatabase(Database *db);
void SetContentDatabase(Database *db);
void LoadEvolvingItems() const;
void DoLootChecks(uint32 char_id, uint16 slot_id, const EQ::ItemInstance &inst) const;
uint32 GetFinalItemID(const EQ::ItemInstance &inst) const;
uint32 GetNextEvolveItemID(const EQ::ItemInstance &inst) const;
uint32 GetFirstItemInLoreGroup(uint32 lore_id);
uint32 GetFirstItemInLoreGroupByItemID(uint32 item_id);
uint64 GetTotalEarnedXP(const EQ::ItemInstance &inst);
static double CalculateProgression(uint64 current_amount, uint32 item_id);
static void LoadPlayerEvent(const EQ::ItemInstance &inst, PlayerEvent::EvolveItem &e);
ItemsEvolvingDetailsRepository::ItemsEvolvingDetails GetEvolveItemDetails(uint64 id);
EvolveTransfer DetermineTransferResults(const EQ::ItemInstance& inst_from, const EQ::ItemInstance& inst_to);
EvolveGetNextItem GetNextItemByXP(const EQ::ItemInstance &inst_in, int64 in_xp);
std::map<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails>& GetEvolvingItemsCache() { return evolving_items_cache; }
std::vector<ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> GetEvolveIDItems(uint32 evolve_id);
private:
std::map<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> evolving_items_cache;
Database * m_db;
Database * m_content_db;
};
extern EvolvingItemsManager evolving_items_manager;
#endif //EVOLVING_H
+194
View File
@@ -344,6 +344,8 @@ bool EQ::InventoryProfile::SwapItem(
fail_state = swapNotAllowed;
return false;
}
source_item_instance->SetEvolveEquipped(false);
if ((destination_slot >= invslot::EQUIPMENT_BEGIN && destination_slot <= invslot::EQUIPMENT_END)) {
auto source_item = source_item_instance->GetItem();
if (!source_item) {
@@ -362,6 +364,9 @@ bool EQ::InventoryProfile::SwapItem(
fail_state = swapLevel;
return false;
}
if (source_item_instance->IsEvolving() > 0) {
source_item_instance->SetEvolveEquipped(true);
}
}
}
@@ -370,6 +375,8 @@ bool EQ::InventoryProfile::SwapItem(
fail_state = swapNotAllowed;
return false;
}
destination_item_instance->SetEvolveEquipped(false);
if ((source_slot >= invslot::EQUIPMENT_BEGIN && source_slot <= invslot::EQUIPMENT_END)) {
auto destination_item = destination_item_instance->GetItem();
if (!destination_item) {
@@ -388,6 +395,9 @@ bool EQ::InventoryProfile::SwapItem(
fail_state = swapLevel;
return false;
}
if (destination_item_instance->IsEvolving()) {
destination_item_instance->SetEvolveEquipped(true);
}
}
}
@@ -1402,6 +1412,8 @@ int16 EQ::InventoryProfile::_PutItem(int16 slot_id, ItemInstance* inst)
int16 result = INVALID_INDEX;
int16 parentSlot = INVALID_INDEX;
inst->SetEvolveEquipped(false);
if (slot_id == invslot::slotCursor) {
// Replace current item on cursor, if exists
m_cursor.pop(); // no memory delete, clients of this function know what they are doing
@@ -1410,6 +1422,9 @@ int16 EQ::InventoryProfile::_PutItem(int16 slot_id, ItemInstance* inst)
}
else if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) {
if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) != 0) {
if (inst->IsEvolving()) {
inst->SetEvolveEquipped(true);
}
m_worn[slot_id] = inst;
result = slot_id;
}
@@ -1808,3 +1823,182 @@ int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItem(const EQ::ItemData *it
}
return 0;
}
//This function has the same flaw as noted above
// Helper functions for evolving items
int16 EQ::InventoryProfile::HasEvolvingItem(uint64 evolve_unique_id, uint8 quantity, uint8 where)
{
int16 slot_id = INVALID_INDEX;
// Altered by Father Nitwit to support a specification of
// where to search, with a default value to maintain compatibility
// Check each inventory bucket
if (where & invWhereWorn) {
slot_id = _HasEvolvingItem(m_worn, evolve_unique_id, quantity);
if (slot_id != INVALID_INDEX) {
return slot_id;
}
}
if (where & invWherePersonal) {
slot_id = _HasEvolvingItem(m_inv, evolve_unique_id, quantity);
if (slot_id != INVALID_INDEX) {
return slot_id;
}
}
if (where & invWhereBank) {
slot_id = _HasEvolvingItem(m_bank, evolve_unique_id, quantity);
if (slot_id != INVALID_INDEX) {
return slot_id;
}
}
if (where & invWhereSharedBank) {
slot_id = _HasEvolvingItem(m_shbank, evolve_unique_id, quantity);
if (slot_id != INVALID_INDEX) {
return slot_id;
}
}
if (where & invWhereTrading) {
slot_id = _HasEvolvingItem(m_trade, evolve_unique_id, quantity);
if (slot_id != INVALID_INDEX) {
return slot_id;
}
}
// Behavioral change - Limbo is no longer checked due to improper handling of return value
if (where & invWhereCursor) {
// Check cursor queue
slot_id = _HasEvolvingItem(m_cursor, evolve_unique_id, quantity);
if (slot_id != INVALID_INDEX) {
return slot_id;
}
}
return slot_id;
}
// Internal Method: Checks an inventory bucket for a particular evolving item unique id
int16 EQ::InventoryProfile::_HasEvolvingItem(
std::map<int16, ItemInstance *> &bucket, uint64 evolve_unique_id, uint8 quantity)
{
uint32 quantity_found = 0;
for (auto const &[key, value]: bucket) {
if (!value) {
continue;
}
if (key <= EQ::invslot::POSSESSIONS_END && key >= EQ::invslot::POSSESSIONS_BEGIN) {
if (((uint64) 1 << key & m_lookup->PossessionsBitmask) == 0) {
continue;
}
}
else if (key <= EQ::invslot::BANK_END && key >= EQ::invslot::BANK_BEGIN) {
if (key - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) {
continue;
}
}
if (value->GetEvolveUniqueID() == evolve_unique_id) {
quantity_found += value->GetCharges() <= 0 ? 1 : value->GetCharges();
if (quantity_found >= quantity) {
return key;
}
}
for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) {
if (value->GetAugmentEvolveUniqueID(index) == evolve_unique_id && quantity <= 1) {
return invslot::SLOT_AUGMENT_GENERIC_RETURN;
}
}
if (!value->IsClassBag()) {
continue;
}
for (auto const &[bag_key, bag_value]: *value->GetContents()) {
if (!bag_value) {
continue;
}
if (bag_value->GetEvolveUniqueID() == evolve_unique_id) {
quantity_found += bag_value->GetCharges() <= 0 ? 1 : bag_value->GetCharges();
if (quantity_found >= quantity) {
return CalcSlotId(key, bag_key);
}
}
for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) {
if (bag_value->GetAugmentEvolveUniqueID(index) == evolve_unique_id && quantity <= 1) {
return invslot::SLOT_AUGMENT_GENERIC_RETURN;
}
}
}
}
return INVALID_INDEX;
}
// Internal Method: Checks an inventory queue type bucket for a particular item
int16 EQ::InventoryProfile::_HasEvolvingItem(ItemInstQueue &iqueue, uint64 evolve_unique_id, uint8 quantity)
{
// The downfall of this (these) queue procedure is that callers presume that when an item is
// found, it is presented as being available on the cursor. In cases of a parity check, this
// is sufficient. However, in cases where referential criteria is considered, this can lead
// to unintended results. Funtionality should be observed when referencing the return value
// of this query
uint32 quantity_found = 0;
for (auto const &inst: iqueue) {
if (!inst) {
continue;
}
if (inst->GetEvolveUniqueID() == evolve_unique_id) {
quantity_found += inst->GetCharges() <= 0 ? 1 : inst->GetCharges();
if (quantity_found >= quantity) {
return invslot::slotCursor;
}
}
for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) {
if (inst->GetAugmentEvolveUniqueID(index) == evolve_unique_id && quantity <= 1) {
return invslot::SLOT_AUGMENT_GENERIC_RETURN;
}
}
if (!inst->IsClassBag()) {
continue;
}
for (auto const &[bag_key, bag_value]: *inst->GetContents()) {
if (!bag_value) {
continue;
}
if (bag_value->GetEvolveUniqueID() == evolve_unique_id) {
quantity_found += bag_value->GetCharges() <= 0 ? 1 : bag_value->GetCharges();
if (quantity_found >= quantity) {
return CalcSlotId(invslot::slotCursor, bag_key);
}
}
for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) {
if (bag_value->GetAugmentEvolveUniqueID(index) == evolve_unique_id && quantity <= 1) {
return invslot::SLOT_AUGMENT_GENERIC_RETURN;
}
}
}
// We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo)
break;
}
return INVALID_INDEX;
}
+11
View File
@@ -57,6 +57,8 @@ public:
inline std::list<EQ::ItemInstance*>::const_iterator cbegin() { return m_list.cbegin(); }
inline std::list<EQ::ItemInstance*>::const_iterator cend() { return m_list.cend(); }
inline std::list<EQ::ItemInstance*>::iterator begin() { return m_list.begin(); }
inline std::list<EQ::ItemInstance*>::iterator end() { return m_list.end(); }
inline int size() { return static_cast<int>(m_list.size()); } // TODO: change to size_t
inline bool empty() { return m_list.empty(); }
@@ -211,6 +213,13 @@ namespace EQ
void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, bool value);
std::string GetCustomItemData(int16 slot_id, const std::string& identifier);
static const int GetItemStatValue(uint32 item_id, const std::string& identifier);
std::map<int16, ItemInstance*>& GetWorn() { return m_worn; }
std::map<int16, ItemInstance*>& GetPersonal() { return m_inv; }
int16 HasEvolvingItem(uint64 evolve_unique_id, uint8 quantity, uint8 where);
inline int16 PushItem(int16 slot_id, ItemInstance* inst) { return _PutItem(slot_id, inst); }
protected:
///////////////////////////////
// Protected Methods
@@ -233,6 +242,8 @@ namespace EQ
int16 _HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity);
int16 _HasItemByLoreGroup(std::map<int16, ItemInstance*>& bucket, uint32 loregroup);
int16 _HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup);
int16 _HasEvolvingItem(std::map<int16, ItemInstance*>& bucket, uint64 evolve_unique_id, uint8 quantity);
int16 _HasEvolvingItem(ItemInstQueue& iqueue, uint64 evolve_unique_id, uint8 quantity);
// Player inventory
+56 -51
View File
@@ -25,6 +25,7 @@
#include "rulesys.h"
#include "shareddb.h"
#include "strings.h"
#include "evolving_items.h"
//#include "../common/light_source.h"
@@ -76,6 +77,10 @@ EQ::ItemInstance::ItemInstance(const ItemData* item, int16 charges) {
m_color = m_item->Color;
}
if (IsEvolving()) {
SetTimer("evolve", RuleI(EvolvingItems, DelayUponEquipping));
}
m_SerialNumber = GetNextItemInstSerialNumber();
}
@@ -95,6 +100,10 @@ EQ::ItemInstance::ItemInstance(SharedDatabase *db, uint32 item_id, int16 charges
m_color = 0;
}
if (IsEvolving()) {
SetTimer("evolve", RuleI(EvolvingItems, DelayUponEquipping));
}
m_SerialNumber = GetNextItemInstSerialNumber();
}
@@ -146,7 +155,6 @@ EQ::ItemInstance::ItemInstance(const ItemInstance& copy)
m_exp = copy.m_exp;
m_evolveLvl = copy.m_evolveLvl;
m_activated = copy.m_activated;
if (copy.m_scaledItem) {
m_scaledItem = new ItemData(*copy.m_scaledItem);
@@ -154,12 +162,7 @@ EQ::ItemInstance::ItemInstance(const ItemInstance& copy)
m_scaledItem = nullptr;
}
if (copy.m_evolveInfo) {
m_evolveInfo = new EvolveInfo(*copy.m_evolveInfo);
} else {
m_evolveInfo = nullptr;
}
m_evolving_details = copy.m_evolving_details;
m_scaling = copy.m_scaling;
m_ornamenticon = copy.m_ornamenticon;
m_ornamentidfile = copy.m_ornamentidfile;
@@ -174,7 +177,6 @@ EQ::ItemInstance::~ItemInstance()
Clear();
safe_delete(m_item);
safe_delete(m_scaledItem);
safe_delete(m_evolveInfo);
}
// Query item type
@@ -1030,29 +1032,6 @@ void EQ::ItemInstance::ScaleItem() {
m_scaledItem->CharmFileID = 0; // this stops the client from trying to scale the item itself.
}
bool EQ::ItemInstance::EvolveOnAllKills() const {
return (m_evolveInfo && m_evolveInfo->AllKills);
}
int8 EQ::ItemInstance::GetMaxEvolveLvl() const {
if (m_evolveInfo)
return m_evolveInfo->MaxLvl;
else
return 0;
}
uint32 EQ::ItemInstance::GetKillsNeeded(uint8 currentlevel) {
uint32 kills = -1; // default to -1 (max uint32 value) because this value is usually divided by, so we don't want to ever return zero.
if (m_evolveInfo)
if (currentlevel != m_evolveInfo->MaxLvl)
kills = m_evolveInfo->LvlKills[currentlevel - 1];
if (kills == 0)
kills = -1;
return kills;
}
void EQ::ItemInstance::SetTimer(std::string name, uint32 time) {
Timer t(time);
t.Start(time, false);
@@ -1951,28 +1930,54 @@ void EQ::ItemInstance::ClearGUIDMap()
{
guids.clear();
}
//
// class EvolveInfo
//
EvolveInfo::EvolveInfo() {
// nothing here yet
bool EQ::ItemInstance::TransferOwnership(Database &db, const uint32 to_char_id) const
{
if (!to_char_id || !IsEvolving()) {
return false;
}
SetEvolveCharID(to_char_id);
CharacterEvolvingItemsRepository::UpdateCharID(db, GetEvolveUniqueID(), to_char_id);
return true;
}
EvolveInfo::EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 L3, uint32 L4, uint32 L5, uint32 L6, uint32 L7, uint32 L8, uint32 L9, uint32 L10) {
FirstItem = first;
MaxLvl = max;
AllKills = allkills;
LvlKills[0] = L2;
LvlKills[1] = L3;
LvlKills[2] = L4;
LvlKills[3] = L5;
LvlKills[4] = L6;
LvlKills[5] = L7;
LvlKills[6] = L8;
LvlKills[7] = L9;
LvlKills[8] = L10;
uint32 EQ::ItemInstance::GetAugmentEvolveUniqueID(uint8 augment_index) const
{
if (!m_item || !m_item->IsClassCommon()) {
return 0;
}
const auto item = GetItem(augment_index);
if (item) {
return item->GetEvolveUniqueID();
}
return 0;
}
EvolveInfo::~EvolveInfo() {
void EQ::ItemInstance::SetTimer(std::string name, uint32 time) const{
Timer t(time);
t.Start(time, false);
m_timers[name] = t;
}
void EQ::ItemInstance::SetEvolveEquipped(const bool in) const
{
if (!IsEvolving()) {
return;
}
m_evolving_details.equipped = in;
if (in && !GetTimers().contains("evolve")) {
SetTimer("evolve", RuleI(EvolvingItems, DelayUponEquipping));
return;
}
if (in) {
GetTimers().at("evolve").SetTimer(RuleI(EvolvingItems, DelayUponEquipping));
return;
}
GetTimers().at("evolve").Disable();
}
+56 -48
View File
@@ -23,6 +23,7 @@
#ifndef COMMON_ITEM_INSTANCE_H
#define COMMON_ITEM_INSTANCE_H
#include "evolving_items.h"
class ItemParse; // Parses item packets
@@ -34,6 +35,7 @@ class EvolveInfo; // Stores information about an evolving item family
#include "../common/bodytypes.h"
#include "../common/deity.h"
#include "../common/memory_buffer.h"
#include "../common/repositories/character_evolving_items_repository.h"
#include <map>
@@ -205,13 +207,9 @@ namespace EQ
bool IsDroppable(bool recurse = true) const;
bool IsScaling() const { return m_scaling; }
bool IsEvolving() const { return (m_evolveLvl >= 1); }
uint32 GetExp() const { return m_exp; }
void SetExp(uint32 exp) { m_exp = exp; }
void AddExp(uint32 exp) { m_exp += exp; }
bool IsActivated() { return m_activated; }
void SetActivated(bool activated) { m_activated = activated; }
int8 GetEvolveLvl() const { return m_evolveLvl; }
void SetScaling(bool v) { m_scaling = v; }
uint32 GetOrnamentationIcon() const { return m_ornamenticon; }
void SetOrnamentIcon(uint32 ornament_icon) { m_ornamenticon = ornament_icon; }
@@ -226,9 +224,6 @@ namespace EQ
void Initialize(SharedDatabase *db = nullptr);
void ScaleItem();
bool EvolveOnAllKills() const;
int8 GetMaxEvolveLvl() const;
uint32 GetKillsNeeded(uint8 currentlevel);
std::string Serialize(int16 slot_id) const { InternalSerializedItem_Struct s; s.slot_id = slot_id; s.inst = (const void*)this; std::string ser; ser.assign((char*)&s, sizeof(InternalSerializedItem_Struct)); return ser; }
void Serialize(OutBuffer& ob, int16 slot_id) const { InternalSerializedItem_Struct isi; isi.slot_id = slot_id; isi.inst = (const void*)this; ob.write((const char*)&isi, sizeof(isi)); }
@@ -236,8 +231,9 @@ namespace EQ
inline int32 GetSerialNumber() const { return m_SerialNumber; }
inline void SetSerialNumber(int32 id) { m_SerialNumber = id; }
std::map<std::string, ::Timer>& GetTimers() { return m_timers; }
std::map<std::string, ::Timer>& GetTimers() const { return m_timers; }
void SetTimer(std::string name, uint32 time);
void SetTimer(std::string name, uint32 time) const;
void StopTimer(std::string name);
void ClearTimers();
@@ -312,6 +308,34 @@ namespace EQ
static void AddGUIDToMap(uint64 existing_serial_number);
static void ClearGUIDMap();
// evolving items stuff
CharacterEvolvingItemsRepository::CharacterEvolvingItems &GetEvolvingDetails() const { return m_evolving_details; }
int8 GetEvolveLvl() const { if (GetItem()) { return GetItem()->EvolvingLevel; } return false; }
bool IsEvolving() const { if (GetItem()) { return GetItem()->EvolvingItem; } return false; }
int8 GetMaxEvolveLvl() const { if (GetItem()) { return GetItem()->EvolvingMax; } return false; }
bool GetEvolveActivated() const { return m_evolving_details.activated ? true : false; }
bool GetEvolveEquipped() const { return m_evolving_details.equipped ? true : false; }
double GetEvolveProgression() const { return m_evolving_details.progression; }
uint64 GetEvolveUniqueID() const { return m_evolving_details.id; }
uint32 GetEvolveCharID() const { return m_evolving_details.character_id; }
uint32 GetEvolveItemID() const { return m_evolving_details.item_id; }
uint32 GetEvolveLoreID() const { if (GetItem()) { return GetItem()->EvolvingID; } return false; }
uint64 GetEvolveCurrentAmount() const { return m_evolving_details.current_amount; }
uint32 GetEvolveFinalItemID() const { return m_evolving_details.final_item_id; }
uint32 GetAugmentEvolveUniqueID(uint8 augment_index) const;
void SetEvolveEquipped(const bool in) const;
void SetEvolveActivated(const bool in) const { m_evolving_details.activated = in; }
void SetEvolveProgression(const double in) const { m_evolving_details.progression = in; }
void SetEvolveUniqueID(const uint64 in) const { m_evolving_details.id = in; }
void SetEvolveCharID(const uint32 in) const { m_evolving_details.character_id = in; }
void SetEvolveItemID(const uint32 in) const { m_evolving_details.item_id = in; }
void SetEvolveCurrentAmount(const uint64 in) const { m_evolving_details.current_amount = in; }
void SetEvolveAddToCurrentAmount(const uint64 in) const { m_evolving_details.current_amount += in; }
void SetEvolveFinalItemID(const uint32 in) const { m_evolving_details.final_item_id = in; }
bool TransferOwnership(Database& db, const uint32 to_char_id) const;
void CalculateEvolveProgression() const { m_evolving_details.progression = evolving_items_manager.CalculateProgression(GetEvolveCurrentAmount(), GetID()); }
protected:
//////////////////////////
// Protected Members
@@ -323,48 +347,32 @@ namespace EQ
void _PutItem(uint8 index, ItemInstance* inst) { m_contents[index] = inst; }
ItemInstTypes m_use_type {ItemInstNormal}; // Usage type for item
const ItemData* m_item {nullptr}; // Ptr to item data
int16 m_charges {0}; // # of charges for chargeable items
uint32 m_price {0}; // Bazaar /trader price
uint32 m_color {0};
uint32 m_merchantslot {0};
int16 m_currentslot {0};
bool m_attuned {false};
int32 m_merchantcount {1}; //number avaliable on the merchant, -1=unlimited
int32 m_SerialNumber {0}; // Unique identifier for this instance of an item. Needed for Bazaar.
uint32 m_exp {0};
int8 m_evolveLvl {0};
bool m_activated {false};
ItemData* m_scaledItem {nullptr};
::EvolveInfo* m_evolveInfo {nullptr};
bool m_scaling {false};
uint32 m_ornamenticon {0};
uint32 m_ornamentidfile {0};
uint32 m_new_id_file {0};
uint32 m_ornament_hero_model {0};
uint32 m_recast_timestamp {0};
int m_task_delivered_count {0};
ItemInstTypes m_use_type{ItemInstNormal};// Usage type for item
const ItemData * m_item{nullptr}; // Ptr to item data
int16 m_charges{0}; // # of charges for chargeable items
uint32 m_price{0}; // Bazaar /trader price
uint32 m_color{0};
uint32 m_merchantslot{0};
int16 m_currentslot{0};
bool m_attuned{false};
int32 m_merchantcount{1};//number avaliable on the merchant, -1=unlimited
int32 m_SerialNumber{0}; // Unique identifier for this instance of an item. Needed for Bazaar.
uint32 m_exp{0};
int8 m_evolveLvl{0};
ItemData * m_scaledItem{nullptr};
bool m_scaling{false};
uint32 m_ornamenticon{0};
uint32 m_ornamentidfile{0};
uint32 m_new_id_file{0};
uint32 m_ornament_hero_model{0};
uint32 m_recast_timestamp{0};
int m_task_delivered_count{0};
mutable CharacterEvolvingItemsRepository::CharacterEvolvingItems m_evolving_details{};
// Items inside of this item (augs or contents) {};
std::map<uint8, ItemInstance*> m_contents {}; // Zero-based index: min=0, max=9
std::map<std::string, std::string> m_custom_data {};
std::map<std::string, ::Timer> m_timers {};
std::map<uint8, ItemInstance*> m_contents {}; // Zero-based index: min=0, max=9
std::map<std::string, std::string> m_custom_data {};
mutable std::map<std::string, ::Timer> m_timers {};
};
}
class EvolveInfo {
public:
friend class EQ::ItemInstance;
//temporary
uint16 LvlKills[9];
uint32 FirstItem;
uint8 MaxLvl;
bool AllKills;
EvolveInfo();
EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 L3, uint32 L4, uint32 L5, uint32 L6, uint32 L7, uint32 L8, uint32 L9, uint32 L10);
~EvolveInfo();
};
#endif /*COMMON_ITEM_INSTANCE_H*/
+220
View File
@@ -0,0 +1,220 @@
#ifndef EQEMU_KSM_HPP
#define EQEMU_KSM_HPP
#include "../eqemu_logsys.h"
#include <iostream>
#include <vector>
#include <cstring>
#ifdef _WIN32
#include <malloc.h> // For _aligned_malloc, _aligned_free
#include <windows.h>
#else
#include <sys/mman.h> // For madvise
#include <unistd.h> // For sysconf, sbrk
#endif
// Page-aligned allocator for std::vector
template <typename T>
class PageAlignedAllocator {
public:
using value_type = T;
PageAlignedAllocator() noexcept = default;
template <typename U>
PageAlignedAllocator(const PageAlignedAllocator<U>&) noexcept {}
T* allocate(std::size_t n) {
void* ptr = nullptr;
size_t size = n * sizeof(T);
#ifdef _WIN32
// Simply allocate memory without alignment
ptr = malloc(size);
if (!ptr) throw std::bad_alloc();
#else
size_t alignment = getPageSize(); // Get the system's page size
if (posix_memalign(&ptr, alignment, size) != 0) {
throw std::bad_alloc();
}
#endif
return static_cast<T*>(ptr);
}
void deallocate(T* p, std::size_t) noexcept {
free(p);
}
private:
size_t getPageSize() const
{
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
return sysInfo.dwPageSize; // Page size in bytes
#else
return static_cast<size_t>(sysconf(_SC_PAGESIZE));
#endif
};
};
template <typename T, typename U>
bool operator==(const PageAlignedAllocator<T>&, const PageAlignedAllocator<U>&) noexcept {
return true;
}
template <typename T, typename U>
bool operator!=(const PageAlignedAllocator<T>&, const PageAlignedAllocator<U>&) noexcept {
return false;
}
// Kernel Samepage Merging (KSM) functionality
namespace KSM {
#ifdef _WIN32
// Windows-specific placeholder functions (no-op)
inline void CheckPageAlignment(void* ptr) {
}
inline void* AllocatePageAligned(size_t size) {
return memset(malloc(size), 0, size);
}
inline void MarkMemoryForKSM(void* start, size_t size) {
}
inline void AlignHeapToPageBoundary() {
}
inline void* MarkHeapStart() {
return nullptr;
}
inline size_t MeasureHeapUsage(void* start) {
return 0;
}
#else
// Linux-specific functionality
inline void CheckPageAlignment(void* ptr) {
size_t page_size = sysconf(_SC_PAGESIZE);
if (reinterpret_cast<uintptr_t>(ptr) % page_size == 0) {
LogKSMDetail("Memory is page-aligned [{}]", ptr);
} else {
LogKSMDetail("Memory is NOT page-aligned [{}]", ptr);
}
}
inline void* AllocatePageAligned(size_t size) {
size_t page_size = sysconf(_SC_PAGESIZE);
void* aligned_ptr = nullptr;
if (posix_memalign(&aligned_ptr, page_size, size) != 0) {
LogKSM("Failed to allocate page-aligned memory on Linux. page_size [{}] size [{}] bytes", page_size, size);
}
std::memset(aligned_ptr, 0, size);
return aligned_ptr;
}
inline void MarkMemoryForKSM(void* start, size_t size) {
if (madvise(start, size, MADV_MERGEABLE) == 0) {
LogKSM("Marked memory for KSM | start [{}] size [{}] bytes", start, size);
} else {
perror("madvise failed");
}
}
inline void AlignHeapToPageBoundary() {
size_t page_size = sysconf(_SC_PAGESIZE);
if (page_size == 0) {
LogKSM("Failed to retrieve page size SC_PAGESIZE [{}]", page_size);
return;
}
void* current_break = sbrk(0);
if (current_break == (void*)-1) {
LogKSM("Failed to retrieve the current program break");
return;
}
uintptr_t current_address = reinterpret_cast<uintptr_t>(current_break);
size_t misalignment = current_address % page_size;
if (misalignment != 0) {
size_t adjustment = page_size - misalignment;
if (sbrk(adjustment) == (void*)-1) {
LogKSM("Failed to align heap to page boundary. adjustment [{}] bytes", adjustment);
return;
}
}
LogKSMDetail("Heap aligned to next page boundary. Current break [{}]", sbrk(0));
}
inline void* MarkHeapStart() {
void* current_pos = sbrk(0);
AlignHeapToPageBoundary();
return current_pos;
}
inline size_t MeasureHeapUsage(void* start) {
void* current_break = sbrk(0);
return static_cast<char*>(current_break) - static_cast<char*>(start);
}
#endif
inline size_t getPageSize()
{
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
return sysInfo.dwPageSize; // Page size in bytes
#else
return static_cast<size_t>(sysconf(_SC_PAGESIZE)); // POSIX page size
#endif
};
template <typename T>
inline void PageAlignVectorAligned(std::vector<T, PageAlignedAllocator<T>>& vec) {
if (vec.empty()) {
return;
}
size_t page_size = getPageSize();
void* start = vec.data();
size_t size = vec.size() * sizeof(T);
// Check if the memory is page-aligned
if (reinterpret_cast<std::uintptr_t>(start) % page_size != 0) {
// Allocate a new aligned vector
std::vector<T, PageAlignedAllocator<T>> aligned_vec(vec.get_allocator());
aligned_vec.reserve(vec.capacity()); // Match capacity to avoid reallocation during copy
// Copy elements from the original vector
aligned_vec.insert(aligned_vec.end(), vec.begin(), vec.end());
// Swap the aligned vector with the original vector
vec.swap(aligned_vec);
// Clear the temporary aligned vector to free its memory
aligned_vec.clear();
// Verify the new alignment
start = vec.data();
if (reinterpret_cast<std::uintptr_t>(start) % page_size != 0) {
throw std::runtime_error("Failed to align vector memory to page boundaries.");
}
LogKSMDetail("Vector reallocated to ensure page alignment. start [{}] size [{}] bytes", start, size);
} else {
LogKSMDetail("Vector is already page-aligned. start [{}] size [{}] bytes", start, size);
}
#ifndef _WIN32
// Mark memory for KSM (only on non-Windows systems)
MarkMemoryForKSM(start, size);
#endif
}
}
#endif // EQEMU_KSM_HPP
+1
View File
@@ -250,6 +250,7 @@ IN(OP_TraderBuy, TraderBuy_Struct);
IN(OP_Trader, Trader_ShowItems_Struct);
IN(OP_GMFind, GMSummon_Struct);
IN(OP_PickPocket, PickPocket_Struct);
IN(OP_PickZone, PickZone_Struct);
IN(OP_Bind_Wound, BindWound_Struct);
INr(OP_TrackTarget);
INr(OP_Track);
+8 -1
View File
@@ -5188,7 +5188,14 @@ namespace RoF
//sprintf(hdr.unknown000, "06e0002Y1W00");
snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%016d", item->ID);
strn0cpy(
hdr.unknown000,
fmt::format(
"{:016}\0",
packet_type == ItemPacketInvalid ? 0 : inst->GetSerialNumber()
).c_str(),
sizeof(hdr.unknown000)
);
hdr.stacksize = (inst->IsStackable() ? ((inst->GetCharges() > 1000) ? 0xFFFFFFFF : inst->GetCharges()) : 1);
hdr.unknown004 = 0;
+108 -19
View File
@@ -433,7 +433,9 @@ namespace RoF2
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_icon);
VARSTRUCT_SKIP_TYPE(uint32, eq);
}
dest->QueuePacket(outapp.get());
safe_delete(in);
break;
}
default: {
@@ -468,8 +470,8 @@ namespace RoF2
}
auto p_size = 41 * results.size() + name_size + 14;
auto buffer = std::make_unique<char[]>(p_size);
auto bufptr = buffer.get();
auto buffer = new char[p_size];
auto bufptr = buffer;
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0);
VARSTRUCT_ENCODE_TYPE(uint16, bufptr, results[0].trader_zone_id);
@@ -487,10 +489,11 @@ namespace RoF2
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.item_stat); //itemstat
}
safe_delete(in->pBuffer);
safe_delete_array(in->pBuffer);
in->size = p_size;
in->pBuffer = (uchar *) buffer.get();
in->pBuffer = (uchar*)buffer;
dest->QueuePacket(in);
safe_delete(in);
break;
}
@@ -500,21 +503,22 @@ namespace RoF2
break;
}
case WelcomeMessage: {
auto buffer = std::make_unique<char[]>(sizeof(structs::BazaarWelcome_Struct));
auto buffer = new char[sizeof(structs::BazaarWelcome_Struct)];
auto emu = (BazaarWelcome_Struct *) in->pBuffer;
auto eq = (structs::BazaarWelcome_Struct *) buffer.get();
auto eq = (structs::BazaarWelcome_Struct *) buffer;
eq->action = structs::RoF2BazaarTraderBuyerActions::WelcomeMessage;
eq->num_of_traders = emu->traders;
eq->num_of_items = emu->items;
safe_delete(in->pBuffer);
safe_delete_array(in->pBuffer);
in->SetOpcode(OP_TraderShop);
in->size = sizeof(structs::BazaarWelcome_Struct);
in->pBuffer = (uchar *) buffer.get();
in->pBuffer = (uchar *)buffer;
LogTrading("(RoF2) WelcomeMessage action <green>[{}]", action);
dest->QueuePacket(in);
safe_delete(in);
break;
}
@@ -892,7 +896,9 @@ namespace RoF2
VARSTRUCT_ENCODE_TYPE(uint16, eq, b.buyer_zone_instance_id);
VARSTRUCT_ENCODE_STRING(eq, b.buyer_name.c_str());
}
dest->QueuePacket(outapp.get());
safe_delete(inapp);
break;
}
case Barter_RemoveFromMerchantWindow: {
@@ -963,6 +969,7 @@ namespace RoF2
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.seller_quantity);
dest->QueuePacket(outapp.get());
safe_delete(inapp);
break;
}
default: {
@@ -1343,6 +1350,58 @@ namespace RoF2
dest->FastQueuePacket(&in, ack_req);
}
ENCODE(OP_EvolveItem)
{
EQApplicationPacket *in = *p;
*p = nullptr;
auto action = *reinterpret_cast<uint32 *>(in->pBuffer);
switch (action) {
case EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS: {
auto emu = reinterpret_cast<EvolveItemMessaging *>(in->pBuffer);
EvolveXPWindowSend e{};
EQ::Util::MemoryStreamReader ss(emu->serialized_data, in->size - sizeof(emu->action));
cereal::BinaryInputArchive ar(ss);
ar(e);
auto item_1 = static_cast<const EQ::ItemInstance *>(reinterpret_cast<EQ::InternalSerializedItem_Struct
*>(e.serialize_item_1.data())->inst);
auto item_2 = static_cast<const EQ::ItemInstance *>(reinterpret_cast<EQ::InternalSerializedItem_Struct
*>(e.serialize_item_2.data())->inst);
EQ::OutBuffer ob;
SerializeItem(ob, item_1, 0, 0, ItemPacketMerchant);
SerializeItem(ob, item_2, 0, 0, ItemPacketMerchant);
auto out = std::make_unique<EQApplicationPacket>(
OP_EvolveItem,
sizeof(EvolveXPWindowSendDetails_Struct) + ob.size()
);
auto data = reinterpret_cast<EvolveXPWindowSendDetails_Struct *>(out->pBuffer);
data->action = e.action;
data->compatibility = e.compatibility;
data->max_transfer_level = e.max_transfer_level;
data->item1_unique_id = e.item1_unique_id;
data->item2_unique_id = e.item2_unique_id;
data->item1_present = e.item1_present;
data->item2_present = e.item2_present;
memcpy(data->serialize_data, ob.str().data(), ob.size());
dest->QueuePacket(out.get());
safe_delete(in);
break;
}
default: {
dest->FastQueuePacket(&in);
break;
}
}
}
ENCODE(OP_ExpansionInfo)
{
ENCODE_LENGTH_EXACT(ExpansionInfo_Struct);
@@ -2046,6 +2105,33 @@ namespace RoF2
}
}
ENCODE(OP_ItemPreviewRequest)
{
EQApplicationPacket* in = *p;
*p = nullptr;
uchar* in_buf = in->pBuffer;
auto int_item = (EQ::InternalSerializedItem_Struct*) in_buf;
EQ::OutBuffer buf;
EQ::OutBuffer::pos_type last_pos = buf.tellp();
SerializeItem(buf, (const EQ::ItemInstance*) int_item->inst, int_item->slot_id, 0, ItemPacketInvalid);
if (buf.tellp() == last_pos) {
LogNetcode("RoF2::ENCODE(OP_ItemPreviewRequest) Serialization failed");
safe_delete_array(in_buf);
safe_delete(in);
return;
}
in->size = buf.size();
in->pBuffer = buf.detach();
safe_delete_array(in_buf);
dest->FastQueuePacket(&in, ack_req);
}
ENCODE(OP_ItemVerifyReply)
{
ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct);
@@ -6398,6 +6484,11 @@ namespace RoF2
hdr.scaled_value = (inst->IsScaling() ? (inst->GetExp() / 100) : 0);
hdr.instance_id = (inst->GetMerchantSlot() ? inst->GetMerchantSlot() : inst->GetSerialNumber());
hdr.parcel_item_id = packet_type == ItemPacketParcel ? inst->GetID() : 0;
if (item->EvolvingItem) {
hdr.instance_id = inst->GetEvolveUniqueID() & 0xFFFFFFFF; //lower dword
hdr.parcel_item_id = inst->GetEvolveUniqueID() >> 32; //upper dword
}
hdr.last_cast_time = inst->GetRecastTimestamp();
hdr.charges = (inst->IsStackable() ? (item->MaxCharges ? 1 : 0) : ((inst->GetCharges() > 254)
? 0xFFFFFFFF
@@ -6411,18 +6502,15 @@ namespace RoF2
ob.write((const char*)&hdr, sizeof(RoF2::structs::ItemSerializationHeader));
if (item->EvolvingItem > 0) {
RoF2::structs::EvolvingItem evotop;
RoF2::structs::EvolvingItem_Struct evotop;
evotop.unknown001 = 0;
evotop.unknown002 = 0;
evotop.unknown003 = 0;
evotop.unknown004 = 0;
evotop.evoLevel = item->EvolvingLevel;
evotop.progress = 0;
evotop.Activated = 1;
evotop.evomaxlevel = item->EvolvingMax;
evotop.final_item_id = inst->GetEvolveFinalItemID();
evotop.evolve_level = item->EvolvingLevel;
evotop.progress = inst->GetEvolveProgression();
evotop.activated = inst->GetEvolveActivated();
evotop.evolve_max_level = item->EvolvingMax;
ob.write((const char*)&evotop, sizeof(RoF2::structs::EvolvingItem));
ob.write((const char*)&evotop, sizeof(RoF2::structs::EvolvingItem_Struct));
}
/**
@@ -6796,12 +6884,13 @@ namespace RoF2
iqbs.Heirloom = 0;
iqbs.Placeable = 0;
iqbs.unknown28 = -1;
iqbs.unknown29 = packet_type == ItemPacketInvalid ? 0xFF : 0;
iqbs.unknown30 = -1;
iqbs.NoZone = 0;
iqbs.NoGround = 0;
iqbs.unknown37a = 0; // (guessed position) New to RoF2
iqbs.unknown38 = 0;
iqbs.unknown39 = 1;
iqbs.unknown39 = packet_type == ItemPacketInvalid ? 0 : 1;;
ob.write((const char*)&iqbs, sizeof(RoF2::structs::ItemQuaternaryBodyStruct));
+1
View File
@@ -272,6 +272,7 @@ namespace RoF2
const size_t SAY_LINK_BODY_SIZE = 56;
const uint32 MAX_GUILD_ID = 50000;
const uint32 MAX_BAZAAR_TRADERS = 600;
} /*constants*/
+3 -1
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
@@ -70,6 +70,7 @@ E(OP_DzMemberListName)
E(OP_DzMemberListStatus)
E(OP_DzSetLeaderName)
E(OP_Emote)
E(OP_EvolveItem)
E(OP_ExpansionInfo)
E(OP_FormattedMessage)
E(OP_GMLastName)
@@ -91,6 +92,7 @@ E(OP_InspectBuffs)
E(OP_InspectRequest)
E(OP_ItemLinkResponse)
E(OP_ItemPacket)
E(OP_ItemPreviewRequest)
E(OP_ItemVerifyReply)
E(OP_LeadershipExpUpdate)
E(OP_LogServer)
+32 -9
View File
@@ -4746,16 +4746,13 @@ struct ItemSerializationHeader
uint8 isEvolving;
};
struct EvolvingItem {
uint8 unknown001;
uint8 unknown002;
uint8 unknown003;
uint8 unknown004;
int32 evoLevel;
struct EvolvingItem_Struct {
uint32 final_item_id;
int32 evolve_level;
double progress;
uint8 Activated;
int32 evomaxlevel;
uint8 unknown005[4];
uint8 activated;
int32 evolve_max_level;
uint8 unknown005[4];
};
struct ItemSerializationHeaderFinish
@@ -5428,6 +5425,32 @@ struct Parcel_Struct
};
}; /*structs*/
struct EvolveItemToggle_Struct {
uint32 action;
uint32 unknown_004;
uint64 unique_id;
uint32 percentage;
uint32 activated;
};
struct EvolveXPWindowReceive_Struct {
uint32 action;
uint32 unknown_004;
uint64 item1_unique_id;
uint64 item2_unique_id;
};
struct EvolveXPWindowSendDetails_Struct {
/*000*/ uint32 action;
/*004*/ uint64 item1_unique_id;
/*012*/ uint64 item2_unique_id;
/*020*/ uint32 compatibility;
/*024*/ uint32 max_transfer_level;
/*028*/ uint8 item1_present;
/*029*/ uint8 item2_present;
/*030*/ char serialize_data[];
};
}; /*RoF2*/
#endif /*COMMON_ROF2_STRUCTS_H*/
+2 -2
View File
@@ -286,8 +286,8 @@ namespace Titanium
const size_t CHARACTER_CREATION_LIMIT = 8; // Hard-coded in client - DO NOT ALTER
const size_t SAY_LINK_BODY_SIZE = 45;
const uint32 MAX_GUILD_ID = 1500;
const size_t SAY_LINK_BODY_SIZE = 45;
const uint32 MAX_GUILD_ID = 1500;
} /*constants*/
@@ -0,0 +1,475 @@
/**
* 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_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H
#define EQEMU_BASE_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseCharacterEvolvingItemsRepository {
public:
struct CharacterEvolvingItems {
uint64_t id;
uint32_t character_id;
uint32_t item_id;
uint8_t activated;
uint8_t equipped;
int64_t current_amount;
double progression;
uint32_t final_item_id;
time_t deleted_at;
};
static std::string PrimaryKey()
{
return std::string("id");
}
static std::vector<std::string> Columns()
{
return {
"id",
"character_id",
"item_id",
"activated",
"equipped",
"current_amount",
"progression",
"final_item_id",
"deleted_at",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"id",
"character_id",
"item_id",
"activated",
"equipped",
"current_amount",
"progression",
"final_item_id",
"UNIX_TIMESTAMP(deleted_at)",
};
}
static std::string ColumnsRaw()
{
return std::string(Strings::Implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(Strings::Implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("character_evolving_items");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static CharacterEvolvingItems NewEntity()
{
CharacterEvolvingItems e{};
e.id = 0;
e.character_id = 0;
e.item_id = 0;
e.activated = 0;
e.equipped = 0;
e.current_amount = 0;
e.progression = 0;
e.final_item_id = 0;
e.deleted_at = 0;
return e;
}
static CharacterEvolvingItems GetCharacterEvolvingItems(
const std::vector<CharacterEvolvingItems> &character_evolving_itemss,
int character_evolving_items_id
)
{
for (auto &character_evolving_items : character_evolving_itemss) {
if (character_evolving_items.id == character_evolving_items_id) {
return character_evolving_items;
}
}
return NewEntity();
}
static CharacterEvolvingItems FindOne(
Database& db,
int character_evolving_items_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_evolving_items_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
CharacterEvolvingItems e{};
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.character_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.activated = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.equipped = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
e.current_amount = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.progression = row[6] ? strtod(row[6], nullptr) : 0;
e.final_item_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.deleted_at = strtoll(row[8] ? row[8] : "-1", nullptr, 10);
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int character_evolving_items_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
character_evolving_items_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const CharacterEvolvingItems &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[1] + " = " + std::to_string(e.character_id));
v.push_back(columns[2] + " = " + std::to_string(e.item_id));
v.push_back(columns[3] + " = " + std::to_string(e.activated));
v.push_back(columns[4] + " = " + std::to_string(e.equipped));
v.push_back(columns[5] + " = " + std::to_string(e.current_amount));
v.push_back(columns[6] + " = " + std::to_string(e.progression));
v.push_back(columns[7] + " = " + std::to_string(e.final_item_id));
v.push_back(columns[8] + " = FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static CharacterEvolvingItems InsertOne(
Database& db,
CharacterEvolvingItems e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.activated));
v.push_back(std::to_string(e.equipped));
v.push_back(std::to_string(e.current_amount));
v.push_back(std::to_string(e.progression));
v.push_back(std::to_string(e.final_item_id));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", v)
)
);
if (results.Success()) {
e.id = results.LastInsertedID();
return e;
}
e = NewEntity();
return e;
}
static int InsertMany(
Database& db,
const std::vector<CharacterEvolvingItems> &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.character_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.activated));
v.push_back(std::to_string(e.equipped));
v.push_back(std::to_string(e.current_amount));
v.push_back(std::to_string(e.progression));
v.push_back(std::to_string(e.final_item_id));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<CharacterEvolvingItems> All(Database& db)
{
std::vector<CharacterEvolvingItems> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
CharacterEvolvingItems e{};
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.character_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.activated = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.equipped = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
e.current_amount = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.progression = row[6] ? strtod(row[6], nullptr) : 0;
e.final_item_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.deleted_at = strtoll(row[8] ? row[8] : "-1", nullptr, 10);
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<CharacterEvolvingItems> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<CharacterEvolvingItems> 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) {
CharacterEvolvingItems e{};
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.character_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.activated = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.equipped = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
e.current_amount = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.progression = row[6] ? strtod(row[6], nullptr) : 0;
e.final_item_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.deleted_at = strtoll(row[8] ? row[8] : "-1", nullptr, 10);
all_entries.push_back(e);
}
return all_entries;
}
static int DeleteWhere(Database& db, const std::string &where_filter)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {}",
TableName(),
where_filter
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int Truncate(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"TRUNCATE TABLE {}",
TableName()
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int64 GetMaxId(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COALESCE(MAX({}), 0) FROM {}",
PrimaryKey(),
TableName()
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static int64 Count(Database& db, const std::string &where_filter = "")
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COUNT(*) FROM {} {}",
TableName(),
(where_filter.empty() ? "" : "WHERE " + where_filter)
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterEvolvingItems &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.activated));
v.push_back(std::to_string(e.equipped));
v.push_back(std::to_string(e.current_amount));
v.push_back(std::to_string(e.progression));
v.push_back(std::to_string(e.final_item_id));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterEvolvingItems> &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.character_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.activated));
v.push_back(std::to_string(e.equipped));
v.push_back(std::to_string(e.current_amount));
v.push_back(std::to_string(e.progression));
v.push_back(std::to_string(e.final_item_id));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H
@@ -0,0 +1,392 @@
/**
* 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_CHARACTER_PET_NAME_REPOSITORY_H
#define EQEMU_BASE_CHARACTER_PET_NAME_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseCharacterPetNameRepository {
public:
struct CharacterPetName {
int32_t character_id;
std::string name;
};
static std::string PrimaryKey()
{
return std::string("character_id");
}
static std::vector<std::string> Columns()
{
return {
"character_id",
"name",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"character_id",
"name",
};
}
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("character_pet_name");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static CharacterPetName NewEntity()
{
CharacterPetName e{};
e.character_id = 0;
e.name = "";
return e;
}
static CharacterPetName GetCharacterPetName(
const std::vector<CharacterPetName> &character_pet_names,
int character_pet_name_id
)
{
for (auto &character_pet_name : character_pet_names) {
if (character_pet_name.character_id == character_pet_name_id) {
return character_pet_name;
}
}
return NewEntity();
}
static CharacterPetName FindOne(
Database& db,
int character_pet_name_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_pet_name_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
CharacterPetName e{};
e.character_id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.name = row[1] ? row[1] : "";
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int character_pet_name_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
character_pet_name_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const CharacterPetName &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[0] + " = " + std::to_string(e.character_id));
v.push_back(columns[1] + " = '" + Strings::Escape(e.name) + "'");
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.character_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static CharacterPetName InsertOne(
Database& db,
CharacterPetName e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", v)
)
);
if (results.Success()) {
e.character_id = results.LastInsertedID();
return e;
}
e = NewEntity();
return e;
}
static int InsertMany(
Database& db,
const std::vector<CharacterPetName> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
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<CharacterPetName> All(Database& db)
{
std::vector<CharacterPetName> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
CharacterPetName e{};
e.character_id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.name = row[1] ? row[1] : "";
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<CharacterPetName> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<CharacterPetName> 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) {
CharacterPetName e{};
e.character_id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.name = row[1] ? row[1] : "";
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 CharacterPetName &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
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<CharacterPetName> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
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_CHARACTER_PET_NAME_REPOSITORY_H
@@ -23,6 +23,7 @@ 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;
@@ -36,6 +37,7 @@ public:
CEREAL_NVP(key_),
CEREAL_NVP(value),
CEREAL_NVP(expires),
CEREAL_NVP(account_id),
CEREAL_NVP(character_id),
CEREAL_NVP(npc_id),
CEREAL_NVP(bot_id)
@@ -55,6 +57,7 @@ public:
"`key`",
"value",
"expires",
"account_id",
"character_id",
"npc_id",
"bot_id",
@@ -68,6 +71,7 @@ public:
"`key`",
"value",
"expires",
"account_id",
"character_id",
"npc_id",
"bot_id",
@@ -115,6 +119,7 @@ public:
e.key_ = "";
e.value = "";
e.expires = 0;
e.account_id = 0;
e.character_id = 0;
e.npc_id = 0;
e.bot_id = 0;
@@ -158,9 +163,10 @@ 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.character_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
e.npc_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.bot_id = row[6] ? strtoll(row[6], 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;
return e;
}
@@ -197,9 +203,10 @@ public:
v.push_back(columns[1] + " = '" + Strings::Escape(e.key_) + "'");
v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'");
v.push_back(columns[3] + " = " + std::to_string(e.expires));
v.push_back(columns[4] + " = " + std::to_string(e.character_id));
v.push_back(columns[5] + " = " + std::to_string(e.npc_id));
v.push_back(columns[6] + " = " + std::to_string(e.bot_id));
v.push_back(columns[4] + " = " + std::to_string(e.account_id));
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));
auto results = db.QueryDatabase(
fmt::format(
@@ -225,6 +232,7 @@ public:
v.push_back("'" + Strings::Escape(e.key_) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back(std::to_string(e.expires));
v.push_back(std::to_string(e.account_id));
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));
@@ -261,6 +269,7 @@ public:
v.push_back("'" + Strings::Escape(e.key_) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back(std::to_string(e.expires));
v.push_back(std::to_string(e.account_id));
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));
@@ -301,9 +310,10 @@ 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.character_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
e.npc_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.bot_id = row[6] ? strtoll(row[6], 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;
all_entries.push_back(e);
}
@@ -332,9 +342,10 @@ 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.character_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
e.npc_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.bot_id = row[6] ? strtoll(row[6], 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;
all_entries.push_back(e);
}
@@ -413,6 +424,7 @@ public:
v.push_back("'" + Strings::Escape(e.key_) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back(std::to_string(e.expires));
v.push_back(std::to_string(e.account_id));
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));
@@ -442,6 +454,7 @@ public:
v.push_back("'" + Strings::Escape(e.key_) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back(std::to_string(e.expires));
v.push_back(std::to_string(e.account_id));
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));
@@ -0,0 +1,451 @@
/**
* 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_ITEMS_EVOLVING_DETAILS_REPOSITORY_H
#define EQEMU_BASE_ITEMS_EVOLVING_DETAILS_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseItemsEvolvingDetailsRepository {
public:
struct ItemsEvolvingDetails {
uint32_t id;
uint32_t item_evo_id;
uint32_t item_evolve_level;
uint32_t item_id;
uint32_t type;
uint32_t sub_type;
int64_t required_amount;
};
static std::string PrimaryKey()
{
return std::string("id");
}
static std::vector<std::string> Columns()
{
return {
"id",
"item_evo_id",
"item_evolve_level",
"item_id",
"type",
"sub_type",
"required_amount",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"id",
"item_evo_id",
"item_evolve_level",
"item_id",
"type",
"sub_type",
"required_amount",
};
}
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("items_evolving_details");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static ItemsEvolvingDetails NewEntity()
{
ItemsEvolvingDetails e{};
e.id = 0;
e.item_evo_id = 0;
e.item_evolve_level = 0;
e.item_id = 0;
e.type = 0;
e.sub_type = 0;
e.required_amount = 0;
return e;
}
static ItemsEvolvingDetails GetItemsEvolvingDetails(
const std::vector<ItemsEvolvingDetails> &items_evolving_detailss,
int items_evolving_details_id
)
{
for (auto &items_evolving_details : items_evolving_detailss) {
if (items_evolving_details.id == items_evolving_details_id) {
return items_evolving_details;
}
}
return NewEntity();
}
static ItemsEvolvingDetails FindOne(
Database& db,
int items_evolving_details_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
items_evolving_details_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
ItemsEvolvingDetails e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.item_evo_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.sub_type = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int items_evolving_details_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
items_evolving_details_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const ItemsEvolvingDetails &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[1] + " = " + std::to_string(e.item_evo_id));
v.push_back(columns[2] + " = " + std::to_string(e.item_evolve_level));
v.push_back(columns[3] + " = " + std::to_string(e.item_id));
v.push_back(columns[4] + " = " + std::to_string(e.type));
v.push_back(columns[5] + " = " + std::to_string(e.sub_type));
v.push_back(columns[6] + " = " + std::to_string(e.required_amount));
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static ItemsEvolvingDetails InsertOne(
Database& db,
ItemsEvolvingDetails e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.item_evo_id));
v.push_back(std::to_string(e.item_evolve_level));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.type));
v.push_back(std::to_string(e.sub_type));
v.push_back(std::to_string(e.required_amount));
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<ItemsEvolvingDetails> &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.item_evo_id));
v.push_back(std::to_string(e.item_evolve_level));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.type));
v.push_back(std::to_string(e.sub_type));
v.push_back(std::to_string(e.required_amount));
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<ItemsEvolvingDetails> All(Database& db)
{
std::vector<ItemsEvolvingDetails> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
ItemsEvolvingDetails e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.item_evo_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.sub_type = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<ItemsEvolvingDetails> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<ItemsEvolvingDetails> 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) {
ItemsEvolvingDetails e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.item_evo_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.sub_type = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.required_amount = row[6] ? strtoll(row[6], 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 ItemsEvolvingDetails &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.item_evo_id));
v.push_back(std::to_string(e.item_evolve_level));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.type));
v.push_back(std::to_string(e.sub_type));
v.push_back(std::to_string(e.required_amount));
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<ItemsEvolvingDetails> &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.item_evo_id));
v.push_back(std::to_string(e.item_evolve_level));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.type));
v.push_back(std::to_string(e.sub_type));
v.push_back(std::to_string(e.required_amount));
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_ITEMS_EVOLVING_DETAILS_REPOSITORY_H
+24 -12
View File
@@ -111,12 +111,13 @@ public:
int32_t fast_regen_mana;
int32_t fast_regen_endurance;
int32_t npc_max_aggro_dist;
uint32_t max_movement_update_range;
uint32_t client_update_range;
int32_t underworld_teleport_index;
int32_t lava_damage;
int32_t min_lava_damage;
uint8_t idle_when_empty;
uint32_t seconds_before_idle;
int32_t shard_at_player_count;
};
static std::string PrimaryKey()
@@ -219,12 +220,13 @@ public:
"fast_regen_mana",
"fast_regen_endurance",
"npc_max_aggro_dist",
"max_movement_update_range",
"client_update_range",
"underworld_teleport_index",
"lava_damage",
"min_lava_damage",
"idle_when_empty",
"seconds_before_idle",
"shard_at_player_count",
};
}
@@ -323,12 +325,13 @@ public:
"fast_regen_mana",
"fast_regen_endurance",
"npc_max_aggro_dist",
"max_movement_update_range",
"client_update_range",
"underworld_teleport_index",
"lava_damage",
"min_lava_damage",
"idle_when_empty",
"seconds_before_idle",
"shard_at_player_count",
};
}
@@ -461,12 +464,13 @@ public:
e.fast_regen_mana = 180;
e.fast_regen_endurance = 180;
e.npc_max_aggro_dist = 600;
e.max_movement_update_range = 600;
e.client_update_range = 600;
e.underworld_teleport_index = 0;
e.lava_damage = 50;
e.min_lava_damage = 10;
e.idle_when_empty = 1;
e.seconds_before_idle = 60;
e.shard_at_player_count = 0;
return e;
}
@@ -595,12 +599,13 @@ public:
e.fast_regen_mana = row[89] ? static_cast<int32_t>(atoi(row[89])) : 180;
e.fast_regen_endurance = row[90] ? static_cast<int32_t>(atoi(row[90])) : 180;
e.npc_max_aggro_dist = row[91] ? static_cast<int32_t>(atoi(row[91])) : 600;
e.max_movement_update_range = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 600;
e.client_update_range = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 600;
e.underworld_teleport_index = row[93] ? static_cast<int32_t>(atoi(row[93])) : 0;
e.lava_damage = row[94] ? static_cast<int32_t>(atoi(row[94])) : 50;
e.min_lava_damage = row[95] ? static_cast<int32_t>(atoi(row[95])) : 10;
e.idle_when_empty = row[96] ? static_cast<uint8_t>(strtoul(row[96], nullptr, 10)) : 1;
e.seconds_before_idle = row[97] ? static_cast<uint32_t>(strtoul(row[97], nullptr, 10)) : 60;
e.shard_at_player_count = row[98] ? static_cast<int32_t>(atoi(row[98])) : 0;
return e;
}
@@ -725,12 +730,13 @@ public:
v.push_back(columns[89] + " = " + std::to_string(e.fast_regen_mana));
v.push_back(columns[90] + " = " + std::to_string(e.fast_regen_endurance));
v.push_back(columns[91] + " = " + std::to_string(e.npc_max_aggro_dist));
v.push_back(columns[92] + " = " + std::to_string(e.max_movement_update_range));
v.push_back(columns[92] + " = " + std::to_string(e.client_update_range));
v.push_back(columns[93] + " = " + std::to_string(e.underworld_teleport_index));
v.push_back(columns[94] + " = " + std::to_string(e.lava_damage));
v.push_back(columns[95] + " = " + std::to_string(e.min_lava_damage));
v.push_back(columns[96] + " = " + std::to_string(e.idle_when_empty));
v.push_back(columns[97] + " = " + std::to_string(e.seconds_before_idle));
v.push_back(columns[98] + " = " + std::to_string(e.shard_at_player_count));
auto results = db.QueryDatabase(
fmt::format(
@@ -844,12 +850,13 @@ public:
v.push_back(std::to_string(e.fast_regen_mana));
v.push_back(std::to_string(e.fast_regen_endurance));
v.push_back(std::to_string(e.npc_max_aggro_dist));
v.push_back(std::to_string(e.max_movement_update_range));
v.push_back(std::to_string(e.client_update_range));
v.push_back(std::to_string(e.underworld_teleport_index));
v.push_back(std::to_string(e.lava_damage));
v.push_back(std::to_string(e.min_lava_damage));
v.push_back(std::to_string(e.idle_when_empty));
v.push_back(std::to_string(e.seconds_before_idle));
v.push_back(std::to_string(e.shard_at_player_count));
auto results = db.QueryDatabase(
fmt::format(
@@ -971,12 +978,13 @@ public:
v.push_back(std::to_string(e.fast_regen_mana));
v.push_back(std::to_string(e.fast_regen_endurance));
v.push_back(std::to_string(e.npc_max_aggro_dist));
v.push_back(std::to_string(e.max_movement_update_range));
v.push_back(std::to_string(e.client_update_range));
v.push_back(std::to_string(e.underworld_teleport_index));
v.push_back(std::to_string(e.lava_damage));
v.push_back(std::to_string(e.min_lava_damage));
v.push_back(std::to_string(e.idle_when_empty));
v.push_back(std::to_string(e.seconds_before_idle));
v.push_back(std::to_string(e.shard_at_player_count));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -1102,12 +1110,13 @@ public:
e.fast_regen_mana = row[89] ? static_cast<int32_t>(atoi(row[89])) : 180;
e.fast_regen_endurance = row[90] ? static_cast<int32_t>(atoi(row[90])) : 180;
e.npc_max_aggro_dist = row[91] ? static_cast<int32_t>(atoi(row[91])) : 600;
e.max_movement_update_range = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 600;
e.client_update_range = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 600;
e.underworld_teleport_index = row[93] ? static_cast<int32_t>(atoi(row[93])) : 0;
e.lava_damage = row[94] ? static_cast<int32_t>(atoi(row[94])) : 50;
e.min_lava_damage = row[95] ? static_cast<int32_t>(atoi(row[95])) : 10;
e.idle_when_empty = row[96] ? static_cast<uint8_t>(strtoul(row[96], nullptr, 10)) : 1;
e.seconds_before_idle = row[97] ? static_cast<uint32_t>(strtoul(row[97], nullptr, 10)) : 60;
e.shard_at_player_count = row[98] ? static_cast<int32_t>(atoi(row[98])) : 0;
all_entries.push_back(e);
}
@@ -1224,12 +1233,13 @@ public:
e.fast_regen_mana = row[89] ? static_cast<int32_t>(atoi(row[89])) : 180;
e.fast_regen_endurance = row[90] ? static_cast<int32_t>(atoi(row[90])) : 180;
e.npc_max_aggro_dist = row[91] ? static_cast<int32_t>(atoi(row[91])) : 600;
e.max_movement_update_range = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 600;
e.client_update_range = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 600;
e.underworld_teleport_index = row[93] ? static_cast<int32_t>(atoi(row[93])) : 0;
e.lava_damage = row[94] ? static_cast<int32_t>(atoi(row[94])) : 50;
e.min_lava_damage = row[95] ? static_cast<int32_t>(atoi(row[95])) : 10;
e.idle_when_empty = row[96] ? static_cast<uint8_t>(strtoul(row[96], nullptr, 10)) : 1;
e.seconds_before_idle = row[97] ? static_cast<uint32_t>(strtoul(row[97], nullptr, 10)) : 60;
e.shard_at_player_count = row[98] ? static_cast<int32_t>(atoi(row[98])) : 0;
all_entries.push_back(e);
}
@@ -1396,12 +1406,13 @@ public:
v.push_back(std::to_string(e.fast_regen_mana));
v.push_back(std::to_string(e.fast_regen_endurance));
v.push_back(std::to_string(e.npc_max_aggro_dist));
v.push_back(std::to_string(e.max_movement_update_range));
v.push_back(std::to_string(e.client_update_range));
v.push_back(std::to_string(e.underworld_teleport_index));
v.push_back(std::to_string(e.lava_damage));
v.push_back(std::to_string(e.min_lava_damage));
v.push_back(std::to_string(e.idle_when_empty));
v.push_back(std::to_string(e.seconds_before_idle));
v.push_back(std::to_string(e.shard_at_player_count));
auto results = db.QueryDatabase(
fmt::format(
@@ -1516,12 +1527,13 @@ public:
v.push_back(std::to_string(e.fast_regen_mana));
v.push_back(std::to_string(e.fast_regen_endurance));
v.push_back(std::to_string(e.npc_max_aggro_dist));
v.push_back(std::to_string(e.max_movement_update_range));
v.push_back(std::to_string(e.client_update_range));
v.push_back(std::to_string(e.underworld_teleport_index));
v.push_back(std::to_string(e.lava_damage));
v.push_back(std::to_string(e.min_lava_damage));
v.push_back(std::to_string(e.idle_when_empty));
v.push_back(std::to_string(e.seconds_before_idle));
v.push_back(std::to_string(e.shard_at_player_count));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -80,6 +80,93 @@ public:
return l.empty() ? CharacterDataRepository::NewEntity() : l.front();
}
struct InstancePlayerCount {
int32_t instance_id;
uint32_t zone_id;
uint32_t player_count;
};
static std::vector<InstancePlayerCount> GetInstanceZonePlayerCounts(Database& db, int zone_id) {
std::vector<InstancePlayerCount> zone_player_counts;
uint64_t shard_instance_duration = 3155760000;
auto query = fmt::format(SQL(
SELECT
zone_id,
0 AS instance_id,
COUNT(id) AS player_count
FROM
character_data
WHERE
zone_instance = 0
AND zone_id = {}
AND last_login >= UNIX_TIMESTAMP(NOW()) - 600
GROUP BY
zone_id
ORDER BY
zone_id, player_count DESC
), zone_id);
auto results = db.QueryDatabase(query);
for (auto row = results.begin(); row != results.end(); ++row) {
InstancePlayerCount e{};
e.zone_id = std::stoi(row[0]);
e.instance_id = 0;
e.player_count = std::stoi(row[2]);
zone_player_counts.push_back(e);
}
if (zone_player_counts.empty()) {
InstancePlayerCount e{};
e.zone_id = zone_id;
e.instance_id = 0;
e.player_count = 0;
zone_player_counts.push_back(e);
}
// duration 3155760000 is for shards explicitly
query = fmt::format(
SQL(
SELECT
i.id AS instance_id,
i.zone AS zone_id,
COUNT(c.id) AS player_count
FROM
instance_list i
LEFT JOIN
character_data c
ON
i.zone = c.zone_id
AND i.id = c.zone_instance
AND c.last_login >= UNIX_TIMESTAMP(NOW()) - 600
AND (i.start_time + i.duration >= UNIX_TIMESTAMP(NOW()) OR i.never_expires = 0)
AND i.duration = {}
WHERE
i.zone IS NOT NULL AND i.zone = {}
GROUP BY
i.id, i.zone, i.version
ORDER BY
i.id ASC;
), shard_instance_duration, zone_id
);
results = db.QueryDatabase(query);
if (!results.Success() || results.RowCount() == 0) {
return zone_player_counts;
}
for (auto row = results.begin(); row != results.end(); ++row) {
InstancePlayerCount e{};
e.instance_id = std::stoi(row[0]);
e.zone_id = std::stoi(row[1]);
e.player_count = std::stoi(row[2]);
zone_player_counts.push_back(e);
}
return zone_player_counts;
}
};
#endif //EQEMU_CHARACTER_DATA_REPOSITORY_H
@@ -0,0 +1,66 @@
#ifndef EQEMU_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H
#define EQEMU_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_character_evolving_items_repository.h"
#include <boost/tuple/tuple_comparison.hpp>
class CharacterEvolvingItemsRepository: public BaseCharacterEvolvingItemsRepository {
public:
// Custom extended repository methods here
static CharacterEvolvingItems SetCurrentAmountAndProgression(Database& db, const uint64 id, const uint64 amount, const double progression)
{
auto e = FindOne(db, id);
if (e.id == 0) {
return NewEntity();
}
e.current_amount = amount;
e.progression = progression;
e.deleted_at = 0;
UpdateOne(db, e);
return e;
}
static CharacterEvolvingItems SetEquipped(Database& db, const uint64 id, const bool equipped)
{
auto e = FindOne(db, id);
if (e.id == 0) {
return NewEntity();
}
e.equipped = equipped;
e.deleted_at = 0;
UpdateOne(db, e);
return e;
}
static CharacterEvolvingItems SoftDelete(Database& db, const uint64 id)
{
auto e = FindOne(db, id);
if (e.id == 0) {
return NewEntity();
}
e.deleted_at = time(nullptr);
UpdateOne(db, e);
return e;
}
static bool UpdateCharID(Database &db, const uint64 id, const uint32 to_char_id)
{
auto e = FindOne(db, id);
if (e.id == 0) {
return false;
}
e.character_id = to_char_id;
e.deleted_at = 0;
return UpdateOne(db, e);
}
};
#endif //EQEMU_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H
@@ -0,0 +1,13 @@
#ifndef EQEMU_CHARACTER_PET_NAME_REPOSITORY_H
#define EQEMU_CHARACTER_PET_NAME_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_character_pet_name_repository.h"
class CharacterPetNameRepository: public BaseCharacterPetNameRepository {
public:
// Custom extended repository methods here
};
#endif //EQEMU_CHARACTER_PET_NAME_REPOSITORY_H
@@ -0,0 +1,14 @@
#ifndef EQEMU_ITEMS_EVOLVING_DETAILS_REPOSITORY_H
#define EQEMU_ITEMS_EVOLVING_DETAILS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_items_evolving_details_repository.h"
class ItemsEvolvingDetailsRepository: public BaseItemsEvolvingDetailsRepository {
public:
// Custom extended repository methods here
};
#endif //EQEMU_ITEMS_EVOLVING_DETAILS_REPOSITORY_H
+53
View File
@@ -7,6 +7,14 @@
class ItemsRepository: public BaseItemsRepository {
public:
struct Bazaar_Results {
uint32 item_id;
std::string name;
bool stackable;
uint32 icon;
uint32 stats;
};
static std::vector<int32> GetItemIDsBySearchCriteria(
Database& db,
std::string search_string,
@@ -37,6 +45,51 @@ public:
return item_id_list;
}
static std::unordered_map<uint32, Bazaar_Results> GetItemsForBazaarSearch(
Database& db,
const std::vector<std::string> &search_ids,
const std::string &name,
const std::string &field_criteria_items,
const std::string &where_criteria_items,
const uint32 query_limit = 0
)
{
auto query = fmt::format(
"SELECT id, name, stackable, icon, {} "
"FROM items "
"WHERE `name` LIKE '%%{}%%' AND {} AND id IN({}) "
"ORDER BY id ASC",
field_criteria_items,
Strings::Escape(name),
where_criteria_items,
Strings::Implode(",", search_ids)
);
if (query_limit >= 1) {
query += fmt::format(" LIMIT {}", query_limit);
}
std::unordered_map<uint32, Bazaar_Results> item_list;
auto results = db.QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
return item_list;
}
item_list.reserve(results.RowCount());
for (auto row : results) {
Bazaar_Results br{};
br.item_id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
br.name = row[1] ? row[1] : "";
br.stackable = atoi(row[2]) ? true : false;
br.icon = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
br.stats = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
item_list.emplace(br.item_id, br);
}
return item_list;
}
};
+97 -3
View File
@@ -12,6 +12,7 @@
class TraderRepository : public BaseTraderRepository {
public:
static constexpr uint32 TRADER_CONVERT_ID = 4000000000;
struct DistinctTraders_Struct {
uint32 trader_id;
@@ -27,6 +28,11 @@ public:
std::vector<DistinctTraders_Struct> traders{};
};
struct BazaarTraderSearch_Struct {
Trader trader;
std::string trader_name;
};
struct WelcomeData_Struct {
uint32 count_of_traders;
uint32 count_of_items;
@@ -40,15 +46,24 @@ public:
int32 char_zone_instance_id
);
static BulkTraders_Struct GetDistinctTraders(Database &db)
static BulkTraders_Struct GetDistinctTraders(
Database &db,
uint32 char_zone_instance_id,
uint32 max_results = std::numeric_limits<uint32>::max()
)
{
BulkTraders_Struct all_entries{};
std::vector<DistinctTraders_Struct> distinct_traders;
auto results = db.QueryDatabase(
auto results = db.QueryDatabase(fmt::format(
"SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name "
"FROM trader AS t "
"JOIN character_data AS c ON t.char_id = c.id;"
"JOIN character_data AS c ON t.char_id = c.id "
"WHERE t.char_zone_instance_id = {} "
"ORDER BY t.char_zone_instance_id ASC "
"LIMIT {}",
char_zone_instance_id,
max_results)
);
distinct_traders.reserve(results.RowCount());
@@ -224,6 +239,85 @@ public:
return DeleteWhere(db, fmt::format("`id` IN({})", Strings::Implode(",", delete_ids)));
}
static DistinctTraders_Struct GetTraderByInstanceAndSerialnumber(
Database &db,
uint32 instance_id,
const char *serial_number
)
{
DistinctTraders_Struct trader{};
auto query = fmt::format(
"SELECT t.id, t.char_id, c.name "
"FROM trader AS t "
"JOIN character_data AS c ON c.id = t.char_id "
"WHERE t.char_zone_id = 151 AND t.char_zone_instance_id = {} AND t.item_sn = {} LIMIT 1",
instance_id,
serial_number
);
auto results = db.QueryDatabase(query);
if (results.RowCount() == 0) {
return trader;
}
auto row = results.begin();
std::string name = row[2];
trader.trader_id = Strings::ToUnsignedInt(row[1]);
trader.trader_name = row[2] ? row[2] : "";
return trader;
}
static std::vector<BazaarTraderSearch_Struct> GetBazaarTraderDetails(
Database &db,
std::string &search_criteria_trader
)
{
std::vector<BazaarTraderSearch_Struct> all_entries{};
auto query = fmt::format(
"SELECT trader.*, c.`name` FROM `trader` INNER JOIN character_data AS c ON trader.char_id = c.id "
"WHERE {} ORDER BY trader.char_id ASC",
search_criteria_trader
);
auto results = db.QueryDatabase(query);
if (results.RowCount() == 0) {
return all_entries;
}
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
BazaarTraderSearch_Struct e{};
e.trader.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.trader.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.trader.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.trader.aug_slot_1 = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.trader.aug_slot_2 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.trader.aug_slot_3 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.trader.aug_slot_4 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.trader.aug_slot_5 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.trader.aug_slot_6 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.trader.item_sn = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.trader.item_charges = row[10] ? static_cast<int32_t>(atoi(row[10])) : 0;
e.trader.item_cost = row[11] ? static_cast<uint32_t>(strtoul(row[11], nullptr, 10)) : 0;
e.trader.slot_id = row[12] ? static_cast<uint8_t>(strtoul(row[12], nullptr, 10)) : 0;
e.trader.char_entity_id = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.trader.char_zone_id = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.trader.char_zone_instance_id = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
e.trader.active_transaction = row[16] ? static_cast<uint8_t>(strtoul(row[16], nullptr, 10)) : 0;
e.trader_name = row[17] ? row[17] : std::string("");
all_entries.push_back(e);
}
return all_entries;
}
};
#endif //EQEMU_TRADER_REPOSITORY_H
+9
View File
@@ -372,6 +372,7 @@ RULE_INT(Zone, FishingChance, 399, "Chance of fishing from zone table vs global
RULE_BOOL(Zone, AllowCrossZoneSpellsOnBots, false, "Set to true to allow cross zone spells (cast/remove) to affect bots")
RULE_BOOL(Zone, AllowCrossZoneSpellsOnMercs, false, "Set to true to allow cross zone spells (cast/remove) to affect mercenaries")
RULE_BOOL(Zone, AllowCrossZoneSpellsOnPets, false, "Set to true to allow cross zone spells (cast/remove) to affect pets")
RULE_BOOL(Zone, ZoneShardQuestMenuOnly, false, "Set to true if you only want quests to show the zone shard menu")
RULE_CATEGORY_END()
RULE_CATEGORY(Map)
@@ -829,6 +830,7 @@ RULE_REAL(Bazaar, ParcelDeliveryCostMod, 0.20, "Cost of parcel delivery for a ba
RULE_INT(Bazaar, VoucherDeliveryCost, 200, "Number of vouchers for direct delivery for a bazaar purchase. Default is 200 vouchers. RoF+ Only.")
RULE_BOOL(Bazaar, EnableParcelDelivery, true, "Enable bazaar purchases via parcel delivery. Default is True.")
RULE_INT(Bazaar, MaxBuyerInventorySearchResults, 200, "Maximum number of search results when a Buyer searches the global item list. Default is 200. RoF+ Only.")
RULE_BOOL(Bazaar, UseAlternateBazaarSearch, false, "Allows the bazaar search window to search across bazaar shards. Default is false.")
RULE_CATEGORY_END()
RULE_CATEGORY(Mail)
@@ -1037,6 +1039,13 @@ RULE_INT(Parcel, ParcelMaxItems, 50, "The maximum number of parcels a player is
RULE_INT(Parcel, ParcelPruneDelay, 30, "The number of days after which a parcel is deleted. Items are lost!")
RULE_CATEGORY_END()
RULE_CATEGORY(EvolvingItems)
RULE_REAL(EvolvingItems, PercentOfSoloExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.")
RULE_REAL(EvolvingItems, PercentOfGroupExperience, 0.1, "Percentage of group experience allocated to evolving items that require experience.")
RULE_REAL(EvolvingItems, PercentOfRaidExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.")
RULE_INT(EvolvingItems, DelayUponEquipping, 30000, "Delay in ms before an evolving item will earn rewards after equipping. Default is 30000ms or 30s.")
RULE_CATEGORY_END()
#undef RULE_CATEGORY
#undef RULE_INT
#undef RULE_REAL
+68 -12
View File
@@ -37,11 +37,13 @@
#include "strings.h"
#include "eqemu_config.h"
#include "data_verification.h"
#include "evolving_items.h"
#include "repositories/criteria/content_filter_criteria.h"
#include "repositories/account_repository.h"
#include "repositories/faction_association_repository.h"
#include "repositories/starting_items_repository.h"
#include "path_manager.h"
#include "../zone/client.h"
#include "repositories/loottable_repository.h"
#include "repositories/character_item_recast_repository.h"
#include "repositories/character_corpses_repository.h"
@@ -645,15 +647,23 @@ bool SharedDatabase::GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is
return true;
}
// Overloaded: Retrieve character inventory based on character id (zone entry)
bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
//bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
bool SharedDatabase::GetInventory(Client *c)
{
if (!char_id || !inv)
if (!c) {
return false;
}
uint32 char_id = c->CharacterID();
EQ::InventoryProfile &inv = c->GetInv();
// Retrieve character inventory
auto results = InventoryRepository::GetWhere(*this, fmt::format("`charid` = '{}' ORDER BY `slotid`;", char_id));
auto results = InventoryRepository::GetWhere(*this, fmt::format("`charid` = '{}' ORDER BY `slotid`", char_id));
auto e_results = CharacterEvolvingItemsRepository::GetWhere(
*this, fmt::format("`character_id` = '{}' AND `deleted_at` IS NULL", char_id)
);
if (results.empty()) {
LogError("Error loading inventory for char_id {} from the database.", char_id);
return false;
@@ -667,8 +677,8 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
const auto timestamps = GetItemRecastTimestamps(char_id);
auto cv_conflict = false;
const auto pmask = inv->GetLookup()->PossessionsBitmask;
const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank;
const auto pmask = inv.GetLookup()->PossessionsBitmask;
const auto bank_size = inv.GetLookup()->InventoryTypeSize.Bank;
std::vector<InventoryRepository::Inventory> queue{};
for (auto &row: results) {
@@ -785,9 +795,55 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
}
}
if (item->EvolvingItem) {
if (slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <= EQ::invslot::EQUIPMENT_END) {
inst->SetEvolveEquipped(true);
}
auto t = std::ranges::find_if(
e_results.cbegin(),
e_results.cend(),
[&](const CharacterEvolvingItemsRepository::CharacterEvolvingItems &x) {
return x.item_id == item_id;
}
);
if (t == std::end(e_results)) {
auto e = CharacterEvolvingItemsRepository::NewEntity();
e.character_id = char_id;
e.item_id = item_id;
e.equipped = inst->GetEvolveEquipped();
e.final_item_id = evolving_items_manager.GetFinalItemID(*inst);
auto r = CharacterEvolvingItemsRepository::InsertOne(*this, e);
e.id = r.id;
e_results.push_back(e);
inst->SetEvolveUniqueID(e.id);
inst->SetEvolveCharID(e.character_id);
inst->SetEvolveItemID(e.item_id);
inst->SetEvolveActivated(e.activated);
inst->SetEvolveEquipped(e.equipped);
inst->SetEvolveCurrentAmount(e.current_amount);
inst->CalculateEvolveProgression();
inst->SetEvolveFinalItemID(e.final_item_id);
}
else {
inst->SetEvolveUniqueID(t->id);
inst->SetEvolveCharID(t->character_id);
inst->SetEvolveItemID(t->item_id);
inst->SetEvolveActivated(t->activated);
inst->SetEvolveEquipped(t->equipped);
inst->SetEvolveCurrentAmount(t->current_amount);
inst->CalculateEvolveProgression();
inst->SetEvolveFinalItemID(t->final_item_id);
}
}
int16 put_slot_id;
if (slot_id >= 8000 && slot_id <= 8999) {
put_slot_id = inv->PushCursor(*inst);
put_slot_id = inv.PushCursor(*inst);
}
else if (slot_id >= 3111 && slot_id <= 3179) {
// Admins: please report any occurrences of this error
@@ -797,10 +853,10 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
item_id,
slot_id
);
put_slot_id = inv->PushCursor(*inst);
put_slot_id = inv.PushCursor(*inst);
}
else {
put_slot_id = inv->PutItem(slot_id, *inst);
put_slot_id = inv.PutItem(slot_id, *inst);
}
row.guid = inst->GetSerialNumber();
@@ -825,8 +881,8 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
"ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])",
char_name,
char_id,
EQ::versions::MobVersionName(inv->InventoryVersion()),
(inv->GMInventory() ? "true" : "false")
EQ::versions::MobVersionName(inv.InventoryVersion()),
(inv.GMInventory() ? "true" : "false")
);
}
@@ -837,7 +893,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
EQ::ItemInstance::ClearGUIDMap();
// Retrieve shared inventory
return GetSharedBank(char_id, inv, true);
return GetSharedBank(char_id, &inv, true);
}
// Overloaded: Retrieve character inventory based on account_id and character name (char select)
+3 -1
View File
@@ -28,6 +28,8 @@
#include "fixed_memory_variable_hash_set.h"
#include "say_link.h"
#include "repositories/command_subsettings_repository.h"
#include "repositories/items_evolving_details_repository.h"
#include "../common/repositories/character_evolving_items_repository.h"
#include <list>
#include <map>
@@ -103,7 +105,7 @@ public:
bool GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is_charid);
int32 GetSharedPlatinum(uint32 account_id);
bool SetSharedPlatinum(uint32 account_id, int32 amount_to_add);
bool GetInventory(uint32 char_id, EQ::InventoryProfile *inv);
bool GetInventory(Client* c);
bool GetInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv); // deprecated
std::map<uint32, uint32> GetItemRecastTimestamps(uint32 char_id);
uint32 GetItemRecastTimestamp(uint32 char_id, uint32 recast_type);
+3
View File
@@ -86,6 +86,9 @@ struct BenchTimer
void reset() { start_time = clock::now(); }
// this is seconds
double elapsed() { return std::chrono::duration<double> (clock::now() - start_time).count(); }
std::chrono::milliseconds::rep elapsedMilliseconds() { return std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - start_time).count(); }
std::chrono::microseconds::rep elapsedMicroseconds() { return std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - start_time).count(); }
std::chrono::nanoseconds::rep elapsedNanoseconds() { return std::chrono::duration_cast<std::chrono::nanoseconds>(clock::now() - start_time).count(); }
private:
std::chrono::time_point<std::chrono::high_resolution_clock> start_time;
};
+2 -2
View File
@@ -25,7 +25,7 @@
// Build variables
// these get injected during the build pipeline
#define CURRENT_VERSION "22.61.0-dev" // always append -dev to the current version for custom-builds
#define CURRENT_VERSION "22.62.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 9286
#define CURRENT_BINARY_DATABASE_VERSION 9295
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045
#endif
-6
View File
@@ -676,12 +676,6 @@ int ZoneStore::GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version)
return z ? z->npc_max_aggro_dist : DEFAULT_ZONE_MAX_AGGRO_DISTANCE;
}
uint32 ZoneStore::GetZoneMaximumMovementUpdateRange(uint32 zone_id, int version)
{
const auto& z = GetZoneVersionWithFallback(zone_id, version);
return z ? z->max_movement_update_range : DEFAULT_ZONE_MAX_MOVEMENT_UPDATE_RANGE;
}
int8 ZoneStore::GetZoneMinimumExpansion(uint32 zone_id, int version)
{
const auto& z = GetZoneVersionWithFallback(zone_id, version);
-1
View File
@@ -94,7 +94,6 @@ public:
int GetZoneFastRegenMana(uint32 zone_id, int version = 0);
int GetZoneFastRegenEndurance(uint32 zone_id, int version = 0);
int GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version = 0);
uint32 GetZoneMaximumMovementUpdateRange(uint32 zone_id, int version = 0);
int8 GetZoneMinimumExpansion(uint32 zone_id, int version = 0);
int8 GetZoneMaximumExpansion(uint32 zone_id, int version = 0);
const std::string GetZoneContentFlags(uint32 zone_id, int version = 0);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "eqemu-server",
"version": "22.61.0",
"version": "22.62.1",
"repository": {
"type": "git",
"url": "https://github.com/EQEmu/Server.git"
+7 -5
View File
@@ -33,12 +33,14 @@
#include "../common/zone_store.h"
#include "../common/path_manager.h"
#include "../common/events/player_event_logs.h"
#include "../common/evolving_items.h"
EQEmuLogSys LogSys;
WorldContentService content_service;
ZoneStore zone_store;
PathManager path;
PlayerEventLogs player_event_logs;
EQEmuLogSys LogSys;
WorldContentService content_service;
ZoneStore zone_store;
PathManager path;
PlayerEventLogs player_event_logs;
EvolvingItemsManager evolving_items_manager;
#ifdef _WINDOWS
#include <direct.h>
+17 -1
View File
@@ -261,6 +261,7 @@ OP_ItemPacket=0x368e
OP_ItemLinkResponse=0x70c0
OP_ItemLinkClick=0x4cef
OP_ItemPreview=0x6b5c
OP_ItemPreviewRequest=0x7f80
OP_NewSpawn=0x6097
OP_Track=0x17e5
OP_TrackTarget=0x695e
@@ -566,7 +567,7 @@ OP_RaidUpdate=0x3973
OP_RaidJoin=0x0000
OP_RaidDelegateAbility=0x2b33
OP_MarkRaidNPC=0x5a58
OP_RaidClearNPCMarks=0x20d3
OP_RaidClearNPCMarks=0x20d3
# Button-push commands
OP_Taunt=0x2703
@@ -728,3 +729,18 @@ OP_InitialHPUpdate=0x0000
#aura related
OP_UpdateAura=0x1456
OP_RemoveTrap=0x71da
OP_PickZoneWindow=0x72d8
OP_PickZone=0xaaba
#evolve item related
OP_EvolveItem=0x7cfb
# This is bugged in ROF2
# OP_InvokeChangePetNameImmediate is supposed to write DisablePetNameChangeReminder=0 to reset the 'nag reminder'
# It actually sets DisableNameChangeReminder=0 (player name change nag reminder). Additionally, clicking the
# 'Don't remind me later' button sets DisableNameChangeReminder=1
# This can be fixed with a client patch.
OP_InvokeChangePetNameImmediate=0x046d
OP_InvokeChangePetName=0x4506
OP_ChangePetName=0x5dab
+1 -1
View File
@@ -11,6 +11,6 @@ require (
github.com/golang/protobuf v1.3.2 // indirect
github.com/google/go-querystring v1.1.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/net v0.33.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
)
+2 -2
View File
@@ -14,8 +14,8 @@ golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -11,13 +11,13 @@ void WorldserverCLI::CopyCharacter(int argc, char **argv, argh::parser &cmd, std
"destination_account_name"
};
std::vector<std::string> options = {};
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
if (cmd[{"-h", "--help"}]) {
return;
}
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
std::string source_character_name = cmd(2).str();
std::string destination_character_name = cmd(3).str();
std::string destination_account_name = cmd(4).str();
+2 -2
View File
@@ -24,12 +24,12 @@ void WorldserverCLI::DatabaseDump(int argc, char **argv, argh::parser &cmd, std:
"--compress"
};
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
if (cmd[{"-h", "--help"}]) {
return;
}
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
auto s = new DatabaseDumpService();
bool dump_all = cmd[{"-a", "--all"}];
+5
View File
@@ -4,12 +4,17 @@ void WorldserverCLI::DatabaseUpdates(int argc, char **argv, argh::parser &cmd, s
{
description = "Runs database updates manually";
std::vector<std::string> options = {
"--skip-backup",
};
if (cmd[{"-h", "--help"}]) {
return;
}
DatabaseUpdate update;
update.SetDatabase(&database)
->SetSkipBackup(cmd[{"--skip-backup"}] )
->SetContentDatabase(&content_db)
->CheckDbUpdates();
}
+19 -1
View File
@@ -1,6 +1,8 @@
#include <cereal/archives/json.hpp>
#include <cereal/types/vector.hpp>
#include <iomanip>
#include "../../common/events/player_events.h"
#include "../../common/memory/ksm.hpp"
void WorldserverCLI::TestCommand(int argc, char **argv, argh::parser &cmd, std::string &description)
{
@@ -10,5 +12,21 @@ void WorldserverCLI::TestCommand(int argc, char **argv, argh::parser &cmd, std::
return;
}
void* start_marker = KSM::MarkHeapStart();
std::cout << "Start marker: " << start_marker << "\n";
std::vector<std::string> vec = {};
for (int i = 0; i < 100000; i++) {
vec.push_back("Some random string");
}
// Measure allocated memory size
size_t allocated_size = KSM::MeasureHeapUsage(start_marker);
// Convert to MB as a float and output with precision
double allocated_size_mb = static_cast<double>(allocated_size) / (1024 * 1024);
std::cout << std::fixed << std::setprecision(3)
<< "Allocated size: " << allocated_size_mb << " MB\n";
// Mark memory for KSM
KSM::MarkMemoryForKSM(start_marker, allocated_size);
}
+2
View File
@@ -46,6 +46,7 @@
#include "client.h"
#include "worlddb.h"
#include "wguild_mgr.h"
#include "../common/evolving_items.h"
#ifdef _WINDOWS
#include <process.h>
@@ -111,6 +112,7 @@ WorldContentService content_service;
WebInterfaceList web_interface;
PathManager path;
PlayerEventLogs player_event_logs;
EvolvingItemsManager evolving_items_manager;
void CatchSignal(int sig_num);
+1
View File
@@ -17,6 +17,7 @@ SET(zone_sources
botspellsai.cpp
cheat_manager.cpp
client.cpp
client_evolving_items.cpp
client_bot.cpp
client_mods.cpp
client_packet.cpp
+21 -27
View File
@@ -2559,16 +2559,12 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
const uint8 killed_level = GetLevel();
if (GetClass() == Class::LDoNTreasure) { // open chest
auto outapp = new EQApplicationPacket(OP_Animation, sizeof(Animation_Struct));
auto a = (Animation_Struct*) outapp->pBuffer;
static EQApplicationPacket p(OP_Animation, sizeof(Animation_Struct));
auto a = (Animation_Struct*) p.pBuffer;
a->spawnid = GetID();
a->action = 0x0F;
a->speed = 10;
entity_list.QueueCloseClients(this, outapp);
safe_delete(outapp);
entity_list.QueueCloseClients(this, &p);
}
auto app = new EQApplicationPacket(OP_Death, sizeof(Death_Struct));
@@ -2815,7 +2811,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
if (con_level != ConsiderColor::Gray) {
if (!GetOwner() || (GetOwner() && !GetOwner()->IsClient())) {
give_exp_client->AddEXP(ExpSource::Kill, final_exp, con_level);
give_exp_client->AddEXP(ExpSource::Kill, final_exp, con_level, false, this);
if (
killer_mob &&
@@ -3415,15 +3411,15 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) {
attacker->Damage(this, -DS, spellid, EQ::skills::SkillAbjuration/*hackish*/, false);
//we can assume there is a spell now
auto outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct));
CombatDamage_Struct* cds = (CombatDamage_Struct*)outapp->pBuffer;
cds->target = attacker->GetID();
cds->source = GetID();
cds->type = spellbonuses.DamageShieldType;
cds->spellid = 0x0;
cds->damage = DS;
entity_list.QueueCloseClients(this, outapp);
safe_delete(outapp);
static EQApplicationPacket p(OP_Damage, sizeof(CombatDamage_Struct));
auto b = (CombatDamage_Struct *) p.pBuffer;
b->target = attacker->GetID();
b->source = GetID();
b->type = spellbonuses.DamageShieldType;
b->spellid = 0x0;
b->damage = DS;
entity_list.QueueCloseClients(this, &p);
}
else if (DS > 0 && !spell_ds) {
//we are healing the attacker...
@@ -4540,8 +4536,8 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
//send damage packet...
if (!iBuffTic) { //buff ticks do not send damage, instead they just call SendHPUpdate(), which is done above
auto outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct));
CombatDamage_Struct* a = (CombatDamage_Struct*)outapp->pBuffer;
static EQApplicationPacket p(OP_Damage, sizeof(CombatDamage_Struct));
auto a = (CombatDamage_Struct *) p.pBuffer;
a->target = GetID();
if (!attacker) {
@@ -4622,7 +4618,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
if (!FromDamageShield) {
entity_list.QueueCloseClients(
attacker, /* Sender */
outapp, /* packet */
&p, /* packet */
false, /* Skip Sender */
((IsValidSpell(spell_id)) ? RuleI(Range, SpellMessages) : RuleI(Range, DamageMessages)),
0, /* don't skip anyone on spell */
@@ -4696,11 +4692,11 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
filter = FilterMyMisses;
if (attacker->IsClient()) {
attacker->CastToClient()->QueuePacket(outapp, true, CLIENT_CONNECTED, filter);
attacker->CastToClient()->QueuePacket(&p, true, CLIENT_CONNECTED, filter);
} else {
entity_list.QueueCloseClients(
attacker, /* Sender */
outapp, /* packet */
&p, /* packet */
false, /* Skip Sender */
(
IsValidSpell(spell_id) ?
@@ -4755,7 +4751,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
a->type = DamageTypeSpell;
entity_list.QueueCloseClients(
this, /* Sender */
outapp, /* packet */
&p, /* packet */
false, /* Skip Sender */
range, /* distance packet travels at the speed of sound */
0, /* don't skip anyone on spell */
@@ -4766,7 +4762,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
else {
//I dont think any filters apply to damage affecting us
if (IsClient()) {
CastToClient()->QueuePacket(outapp);
CastToClient()->QueuePacket(&p);
}
// Send normal message to observers
@@ -4776,7 +4772,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
if (!owner || (owner && !owner->IsClient())) {
entity_list.QueueCloseClients(
this, /* Sender */
outapp, /* packet */
&p, /* packet */
true, /* Skip Sender */
range, /* distance packet travels at the speed of sound */
(IsValidSpell(spell_id) && skill_used != EQ::skills::SkillTigerClaw) ? 0 : skip,
@@ -4786,8 +4782,6 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
}
}
}
safe_delete(outapp);
}
else {
//else, it is a buff tic...
+5 -4
View File
@@ -735,21 +735,22 @@ bool Aura::Process()
if (movement_type == AuraMovement::Follow && GetPosition() != owner->GetPosition() && movement_timer.Check()) {
m_Position = owner->GetPosition();
auto app = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
auto spu = (PlayerPositionUpdateServer_Struct *) app->pBuffer;
static EQApplicationPacket packet(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
auto spu = (PlayerPositionUpdateServer_Struct *) packet.pBuffer;
MakeSpawnUpdate(spu);
auto it = spawned_for.begin();
while (it != spawned_for.end()) {
auto client = entity_list.GetClientByID(*it);
if (client) {
client->QueuePacket(app);
client->QueuePacket(&packet);
++it;
}
else {
it = spawned_for.erase(it);
}
}
safe_delete(app);
}
// TODO: waypoints?
-33
View File
@@ -7390,39 +7390,6 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) {
return;
}
/**
* @param close_mobs
* @param scanning_mob
*/
void EntityList::ScanCloseClientMobs(std::unordered_map<uint16, Mob*>& close_mobs, Mob* scanning_mob)
{
float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance);
close_mobs.clear();
for (auto& e : mob_list) {
auto mob = e.second;
if (!mob->IsClient()) {
continue;
}
if (mob->GetID() <= 0) {
continue;
}
float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition());
if (distance <= scan_range) {
close_mobs.insert(std::pair<uint16, Mob*>(mob->GetID(), mob));
}
else if (mob->GetAggroRange() >= scan_range) {
close_mobs.insert(std::pair<uint16, Mob*>(mob->GetID(), mob));
}
}
LogAIScanCloseDetail("Close Client Mob List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName());
}
uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid) {
uint8 need_healed = 0;
+287 -40
View File
@@ -66,6 +66,7 @@ extern volatile bool RunLoops;
#include "../common/repositories/character_spells_repository.h"
#include "../common/repositories/character_disciplines_repository.h"
#include "../common/repositories/character_data_repository.h"
#include "../common/repositories/character_pet_name_repository.h"
#include "../common/repositories/discovered_items_repository.h"
#include "../common/repositories/inventory_repository.h"
#include "../common/repositories/keyring_repository.h"
@@ -157,7 +158,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
endupkeep_timer(1000),
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
m_client_npc_aggro_scan_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)),
m_client_zone_wide_full_position_update_timer(5 * 60 * 1000),
m_client_bulk_npc_pos_update_timer(60 * 1000),
tribute_timer(Tribute_duration),
proximity_timer(ClientProximity_interval),
TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000),
@@ -402,6 +403,7 @@ Client::~Client() {
mMovementManager->RemoveClient(this);
DataBucket::DeleteCachedBuckets(DataBucketLoadType::Account, AccountID());
DataBucket::DeleteCachedBuckets(DataBucketLoadType::Client, CharacterID());
if (RuleB(Bots, Enabled)) {
@@ -1827,7 +1829,7 @@ void Client::FriendsWho(char *FriendsString) {
Message(0, "Error: World server disconnected");
else {
auto pack =
new ServerPacket(ServerOP_FriendsWho, sizeof(ServerFriendsWho_Struct) + strlen(FriendsString));
new ServerPacket(ServerOP_FriendsWho, sizeof(ServerFriendsWho_Struct) + strlen(FriendsString));
ServerFriendsWho_Struct* FriendsWho = (ServerFriendsWho_Struct*) pack->pBuffer;
FriendsWho->FromID = GetID();
strcpy(FriendsWho->FromName, GetName());
@@ -2208,7 +2210,7 @@ void Client::Stand() {
}
void Client::Sit() {
SetAppearance(eaSitting, false);
SetAppearance(eaSitting, false);
}
void Client::ChangeLastName(std::string last_name) {
@@ -4391,6 +4393,83 @@ void Client::KeyRingList()
}
}
bool Client::IsPetNameChangeAllowed() {
DataBucketKey k = GetScopedBucketKeys();
k.key = "PetNameChangesAllowed";
auto b = DataBucket::GetData(k);
if (!b.value.empty()) {
return true;
}
return false;
}
void Client::InvokeChangePetName(bool immediate) {
if (!IsPetNameChangeAllowed()) {
return;
}
auto packet_op = immediate ? OP_InvokeChangePetNameImmediate : OP_InvokeChangePetName;
auto outapp = new EQApplicationPacket(packet_op, 0);
QueuePacket(outapp);
safe_delete(outapp);
}
void Client::GrantPetNameChange() {
DataBucketKey k = GetScopedBucketKeys();
k.key = "PetNameChangesAllowed";
k.value = "true";
DataBucket::SetData(k);
InvokeChangePetName(true);
}
void Client::ClearPetNameChange() {
DataBucketKey k = GetScopedBucketKeys();
k.key = "PetNameChangesAllowed";
DataBucket::DeleteData(k);
}
bool Client::ChangePetName(std::string new_name)
{
if (new_name.empty()) {
return false;
}
if (!IsPetNameChangeAllowed()) {
return false;
}
auto pet = GetPet();
if (!pet) {
return false;
}
if (pet->GetName() == new_name) {
return false;
}
if (!database.CheckNameFilter(new_name) || database.IsNameUsed(new_name)) {
return false;
}
CharacterPetNameRepository::ReplaceOne(
database,
CharacterPetNameRepository::CharacterPetName{
.character_id = static_cast<int32_t>(CharacterID()),
.name = new_name
}
);
pet->TempName(new_name.c_str());
ClearPetNameChange();
return true;
}
bool Client::IsDiscovered(uint32 item_id) {
const auto& l = DiscoveredItemsRepository::GetWhere(
database,
@@ -5627,13 +5706,13 @@ bool Client::TryReward(uint32 claim_id)
if (amt == 1) {
query = StringFormat("DELETE FROM account_rewards "
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
auto results = database.QueryDatabase(query);
} else {
query = StringFormat("UPDATE account_rewards SET amount = (amount-1) "
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
auto results = database.QueryDatabase(query);
}
@@ -8366,7 +8445,7 @@ int Client::GetQuiverHaste(int delay)
for (int r = EQ::invslot::GENERAL_BEGIN; r <= EQ::invslot::GENERAL_END; r++) {
pi = GetInv().GetItem(r);
if (pi && pi->IsClassBag() && pi->GetItem()->BagType == EQ::item::BagTypeQuiver &&
pi->GetItem()->BagWR > 0)
pi->GetItem()->BagWR > 0)
break;
if (r == EQ::invslot::GENERAL_END)
// we will get here if we don't find a valid quiver
@@ -9840,7 +9919,7 @@ const ExpeditionLockoutTimer* Client::GetExpeditionLockout(
for (const auto& expedition_lockout : m_expedition_lockouts)
{
if ((include_expired || !expedition_lockout.IsExpired()) &&
expedition_lockout.IsSameLockout(expedition_name, event_name))
expedition_lockout.IsSameLockout(expedition_name, event_name))
{
return &expedition_lockout;
}
@@ -9855,7 +9934,7 @@ std::vector<ExpeditionLockoutTimer> Client::GetExpeditionLockouts(
for (const auto& lockout : m_expedition_lockouts)
{
if ((include_expired || !lockout.IsExpired()) &&
lockout.GetExpeditionName() == expedition_name)
lockout.GetExpeditionName() == expedition_name)
{
lockouts.emplace_back(lockout);
}
@@ -12939,50 +13018,77 @@ void Client::SendTopLevelInventory()
}
}
// On a normal basis we limit mob movement updates based on distance
// This ensures we send a periodic full zone update to a client that has started moving after 5 or so minutes
//
// For very large zones we will also force a full update based on distance
//
// We ignore a small distance around us so that we don't interrupt already pathing deltas as those npcs will appear
// to full stop when they are actually still pathing
void Client::CheckSendBulkClientPositionUpdate()
void Client::CheckSendBulkNpcPositions()
{
float distance_moved = DistanceNoZ(m_last_position_before_bulk_update, GetPosition());
bool moved_far_enough_before_bulk_update = distance_moved >= zone->GetNpcPositionUpdateDistance();
float update_range = RuleI(Range, MobCloseScanDistance);
bool moved_far_enough_before_bulk_update = distance_moved >= update_range;
bool is_ready_to_update = (
m_client_zone_wide_full_position_update_timer.Check() || moved_far_enough_before_bulk_update
m_client_bulk_npc_pos_update_timer.Check() || moved_far_enough_before_bulk_update
);
if (IsMoving() && is_ready_to_update) {
LogDebug("[[{}]] Client Zone Wide Position Update NPCs", GetCleanName());
int updated_count = 0;
int skipped_count = 0;
if (is_ready_to_update) {
auto &mob_movement_manager = MobMovementManager::Get();
auto &mob_list = entity_list.GetMobList();
for (auto &it : mob_list) {
Mob *entity = it.second;
if (!entity->IsNPC()) {
for (auto &e: entity_list.GetMobList()) {
Mob *mob = e.second;
if (!mob->IsNPC()) {
continue;
}
int animation_speed = 0;
if (entity->IsMoving()) {
if (entity->IsRunning()) {
animation_speed = (entity->IsFeared() ? entity->GetFearSpeed() : entity->GetRunspeed());
if (mob->IsMoving()) {
if (mob->IsRunning()) {
animation_speed = (mob->IsFeared() ? mob->GetFearSpeed() : mob->GetRunspeed());
}
else {
animation_speed = entity->GetWalkspeed();
animation_speed = mob->GetWalkspeed();
}
}
mob_movement_manager.SendCommandToClients(entity, 0.0, 0.0, 0.0, 0.0, animation_speed, ClientRangeAny, this);
// if we have seen this mob before, and it hasn't moved, skip it
if (m_last_seen_mob_position.contains(mob->GetID())) {
if (m_last_seen_mob_position[mob->GetID()] == mob->GetPosition()) {
LogPositionUpdateDetail(
"Mob [{}] has already been sent to client [{}] at this position, skipping",
mob->GetCleanName(),
GetCleanName()
);
skipped_count++;
continue;
}
}
mob_movement_manager.SendCommandToClients(
mob,
0.0,
0.0,
0.0,
0.0,
animation_speed,
ClientRangeAny,
this
);
updated_count++;
}
LogPositionUpdate(
"[{}] Sent [{}] bulk updated NPC positions, skipped [{}] distance_moved [{}] update_range [{}]",
GetCleanName(),
updated_count,
skipped_count,
distance_moved,
update_range
);
m_last_position_before_bulk_update = GetPosition();
}
}
const uint16 scan_npc_aggro_timer_idle = RuleI(Aggro, ClientAggroCheckIdleInterval);
const uint16 scan_npc_aggro_timer_moving = RuleI(Aggro, ClientAggroCheckMovingInterval);
@@ -13037,19 +13143,19 @@ void Client::ClientToNpcAggroProcess()
const std::vector<int16>& Client::GetInventorySlots()
{
static const std::vector<std::pair<int16, int16>> slots = {
{ EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END },
{ EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END },
{ EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END },
{ EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END },
{ EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END },
{ EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END },
{ EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END },
{EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END},
{EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END},
{EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END},
{EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END},
{EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END},
{EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END},
{EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END},
};
static std::vector<int16> slot_ids;
if (slot_ids.empty()) {
for (const auto& [begin, end] : slots) {
for (const auto &[begin, end]: slots) {
for (int16 slot_id = begin; slot_id <= end; ++slot_id) {
slot_ids.emplace_back(slot_id);
}
@@ -13058,3 +13164,144 @@ const std::vector<int16>& Client::GetInventorySlots()
return slot_ids;
}
void Client::ShowZoneShardMenu()
{
auto z = GetZone(GetZoneID());
if (z && !z->shard_at_player_count) {
return;
}
auto results = CharacterDataRepository::GetInstanceZonePlayerCounts(database, GetZoneID());
LogZoning("Zone sharding results count [{}]", results.size());
if (results.empty()) {
Message(Chat::White, "No zone shards found.");
return;
}
if (!results.empty()) {
Message(Chat::White, "Available Zone Shards:");
}
int number = 1;
for (auto &e: results) {
std::string teleport_link = Saylink::Silent(
fmt::format("#zoneshard {} {}", e.zone_id, (e.instance_id == 0 ? -1 : e.instance_id)),
"Teleport"
);
std::string yours;
if (e.zone_id == GetZoneID() && e.instance_id == GetInstanceID()) {
teleport_link = "Teleport";
yours = " (Yours)";
}
Message(
Chat::White, fmt::format(
" --> [{}] #{} {} ({}) [{}/{}] players {}",
teleport_link,
number,
z->long_name,
e.instance_id,
e.player_count,
z->shard_at_player_count,
yours
).c_str()
);
number++;
}
}
void Client::SetAAEXPPercentage(uint8 percentage)
{
const uint32 before_percentage = m_epp.perAA;
if (before_percentage > 0 && percentage == 0) {
MessageString(Chat::White, AA_OFF);
}
else if (before_percentage == 0 && percentage > 0) {
MessageString(Chat::White, AA_ON);
}
m_epp.perAA = EQ::Clamp(static_cast<int>(percentage), 0, 100);
SendAlternateAdvancementStats();
SendAlternateAdvancementTable();
}
void Client::BroadcastPositionUpdate()
{
EQApplicationPacket outapp(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
PlayerPositionUpdateServer_Struct *spu = (PlayerPositionUpdateServer_Struct *) outapp.pBuffer;
memset(spu, 0x00, sizeof(PlayerPositionUpdateServer_Struct));
spu->spawn_id = GetID();
spu->x_pos = FloatToEQ19(GetX());
spu->y_pos = FloatToEQ19(GetY());
spu->z_pos = FloatToEQ19(GetZ());
spu->heading = FloatToEQ12(GetHeading());
spu->delta_x = FloatToEQ13(0);
spu->delta_y = FloatToEQ13(0);
spu->delta_z = FloatToEQ13(0);
spu->delta_heading = FloatToEQ10(0);
spu->animation = 0;
entity_list.QueueCloseClients(this, &outapp, true, zone->GetClientUpdateRange());
Group *g = GetGroup();
if (g) {
for (auto &m: g->members) {
if (m && m->IsClient() && m != this) {
m->CastToClient()->QueuePacket(&outapp);
}
}
}
}
std::string Client::GetAccountBucket(std::string bucket_name)
{
DataBucketKey k = {};
k.account_id = AccountID();
k.key = bucket_name;
return DataBucket::GetData(k).value;
}
void Client::SetAccountBucket(std::string bucket_name, std::string bucket_value, std::string expiration)
{
DataBucketKey k = {};
k.account_id = AccountID();
k.key = bucket_name;
k.expires = expiration;
k.value = bucket_value;
DataBucket::SetData(k);
}
void Client::DeleteAccountBucket(std::string bucket_name)
{
DataBucketKey k = {};
k.account_id = AccountID();
k.key = bucket_name;
DataBucket::DeleteData(k);
}
std::string Client::GetAccountBucketExpires(std::string bucket_name)
{
DataBucketKey k = {};
k.account_id = AccountID();
k.key = bucket_name;
return DataBucket::GetDataExpires(k);
}
std::string Client::GetAccountBucketRemaining(std::string bucket_name)
{
DataBucketKey k = {};
k.account_id = AccountID();
k.key = bucket_name;
return DataBucket::GetDataRemaining(k);
}
+43 -7
View File
@@ -72,6 +72,7 @@ namespace EQ
#include "../common/repositories/trader_repository.h"
#include "../common/guild_base.h"
#include "../common/repositories/buyer_buy_lines_repository.h"
#include "../common/repositories/character_evolving_items_repository.h"
#ifdef _WINDOWS
// since windows defines these within windef.h (which windows.h include)
@@ -294,7 +295,7 @@ public:
void SendBazaarDone(uint32 trader_id);
void SendBulkBazaarTraders();
void SendBulkBazaarBuyers();
void DoBazaarInspect(const BazaarInspect_Struct &in);
void DoBazaarInspect(BazaarInspect_Struct &in);
void SendBazaarDeliveryCosts();
static std::string DetermineMoneyString(uint64 copper);
@@ -306,6 +307,11 @@ public:
void KeyRingAdd(uint32 item_id);
bool KeyRingCheck(uint32 item_id);
void KeyRingList();
bool IsPetNameChangeAllowed();
void GrantPetNameChange();
void ClearPetNameChange();
void InvokeChangePetName(bool immediate = true);
bool ChangePetName(std::string new_name);
bool IsClient() const override { return true; }
bool IsOfClientBot() const override { return true; }
bool IsOfClientBotMerc() const override { return true; }
@@ -333,7 +339,6 @@ public:
const char *message9 = nullptr);
void Tell_StringID(uint32 string_id, const char *who, const char *message);
void SendColoredText(uint32 color, std::string message);
void SendBazaarResults(uint32 trader_id, uint32 in_class, uint32 in_race, uint32 item_stat, uint32 item_slot, uint32 item_type, char item_name[64], uint32 min_price, uint32 max_price);
void SendTraderItem(uint32 item_id,uint16 quantity, TraderRepository::Trader &trader);
void DoBazaarSearch(BazaarSearchCriteria_Struct search_criteria);
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);
@@ -692,6 +697,8 @@ public:
void SetAAEXPModifier(uint32 zone_id, float aa_modifier, int16 instance_version = -1);
void SetEXPModifier(uint32 zone_id, float exp_modifier, int16 instance_version = -1);
void SetAAEXPPercentage(uint8 percentage);
bool UpdateLDoNPoints(uint32 theme_id, int points);
void SetLDoNPoints(uint32 theme_id, uint32 points);
void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; }
@@ -708,13 +715,13 @@ public:
void SendCrystalCounts();
uint64 GetExperienceForKill(Mob *against);
void AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false);
void AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false, NPC* npc = nullptr);
uint64 CalcEXP(uint8 conlevel = 0xFF, bool ignore_mods = false);
void CalculateNormalizedAAExp(uint64 &add_aaxp, uint8 conlevel, bool resexp);
void CalculateStandardAAExp(uint64 &add_aaxp, uint8 conlevel, bool resexp);
void CalculateLeadershipExp(uint64 &add_exp, uint8 conlevel);
void CalculateExp(uint64 in_add_exp, uint64 &add_exp, uint64 &add_aaxp, uint8 conlevel, bool resexp);
void SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool resexp = false);
void SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool resexp = false, NPC* npc = nullptr);
void AddLevelBasedExp(ExpSource exp_source, uint8 exp_percentage, uint8 max_level = 0, bool ignore_mods = false);
void SetLeadershipEXP(uint64 group_exp, uint64 raid_exp);
void AddLeadershipEXP(uint64 group_exp, uint64 raid_exp);
@@ -1128,6 +1135,10 @@ public:
void SetTrader(bool status) { trader = status; }
uint16 GetTraderID() { return trader_id; }
void SetTraderID(uint16 id) { trader_id = id; }
void SetTraderCount(uint32 no) { m_trader_count = no; }
uint32 GetTraderCount() { return m_trader_count; }
void IncrementTraderCount() { m_trader_count += 1; }
void DecrementTraderCount() { m_trader_count > 0 ? m_trader_count -= 1 : m_trader_count = 0; }
eqFilterMode GetFilter(eqFilterType filter_id) const { return ClientFilters[filter_id]; }
void SetFilter(eqFilterType filter_id, eqFilterMode filter_mode) { ClientFilters[filter_id] = filter_mode; }
@@ -1163,7 +1174,7 @@ public:
void Escape(); //keep or quest function
void DisenchantSummonedBags(bool client_update = true);
void RemoveNoRent(bool client_update = true);
void RemoveDuplicateLore(bool client_update = true);
void RemoveDuplicateLore();
void MoveSlotNotAllowed(bool client_update = true);
virtual bool RangedAttack(Mob* other, bool CanDoubleAttack = false);
virtual void ThrowingAttack(Mob* other, bool CanDoubleAttack = false);
@@ -1810,6 +1821,25 @@ public:
void RecordKilledNPCEvent(NPC *n);
uint32 GetEXPForLevel(uint16 check_level);
// Evolving Item Info
void ProcessEvolvingItem(const uint64 exp, const Mob* mob);
void SendEvolvingPacket(int8 action, const CharacterEvolvingItemsRepository::CharacterEvolvingItems &item);
void DoEvolveItemToggle(const EQApplicationPacket* app);
void DoEvolveItemDisplayFinalResult(const EQApplicationPacket* app);
bool DoEvolveCheckProgression(const EQ::ItemInstance &inst);
void SendEvolveXPWindowDetails(const EQApplicationPacket* app);
void DoEvolveTransferXP(const EQApplicationPacket* app);
void SendEvolveXPTransferWindow();
void SendEvolveTransferResults(const EQ::ItemInstance &inst_from, const EQ::ItemInstance &inst_to, const EQ::ItemInstance &inst_from_new, const EQ::ItemInstance &inst_to_new, const uint32 compatibility, const uint32 max_transfer_level);
// Account buckets
std::string GetAccountBucket(std::string bucket_name);
void SetAccountBucket(std::string bucket_name, std::string bucket_value, std::string expiration = "");
void DeleteAccountBucket(std::string bucket_name);
std::string GetAccountBucketExpires(std::string bucket_name);
std::string GetAccountBucketRemaining(std::string bucket_name);
protected:
friend class Mob;
void CalcEdibleBonuses(StatBonuses* newbon);
@@ -1946,6 +1976,7 @@ private:
uint8 firstlogon;
uint32 mercid; // current merc
uint8 mercSlot; // selected merc slot
uint32 m_trader_count{};
uint32 m_buyer_id;
uint32 m_barter_time;
int32 m_parcel_platinum;
@@ -1968,6 +1999,8 @@ private:
uint16 m_door_tool_entity_id;
uint16 m_object_tool_entity_id;
public:
uint16 GetDoorToolEntityId() const;
void SetDoorToolEntityId(uint16 door_tool_entity_id);
@@ -2065,12 +2098,13 @@ private:
Timer m_client_npc_aggro_scan_timer;
void CheckClientToNpcAggroTimer();
void ClientToNpcAggroProcess();
void BroadcastPositionUpdate();
// bulk position updates
glm::vec4 m_last_position_before_bulk_update;
Timer m_client_zone_wide_full_position_update_timer;
Timer m_client_bulk_npc_pos_update_timer;
Timer m_position_update_timer;
void CheckSendBulkClientPositionUpdate();
void CheckSendBulkNpcPositions();
void BulkSendInventoryItems();
@@ -2236,6 +2270,8 @@ private:
public:
const std::string &GetMailKeyFull() const;
const std::string &GetMailKey() const;
void ShowZoneShardMenu();
void Handle_OP_ChangePetName(const EQApplicationPacket *app);
};
#endif
+451
View File
@@ -0,0 +1,451 @@
#include "../common/evolving_items.h"
#include "../common/events/player_event_logs.h"
#include "../common/global_define.h"
#include "client.h"
#include "string_ids.h"
#include "worldserver.h"
extern WorldServer worldserver;
void Client::DoEvolveItemToggle(const EQApplicationPacket *app)
{
const auto in = reinterpret_cast<EvolveItemToggle *>(app->pBuffer);
auto item = CharacterEvolvingItemsRepository::FindOne(database, in->unique_id);
LogEvolveItemDetail(
"Character ID <green>[{}] requested to set evolve item with unique id <yellow>[{}] to status <yellow>[{}]",
CharacterID(),
in->unique_id,
in->activated
);
if (!item.id) {
LogEvolveItemDetail(
"Character ID <green>[{}] toggle evolve item unique id <yellow>[{}] failed", CharacterID(), in->unique_id);
return;
}
item.activated = in->activated;
const auto inst = GetInv().GetItem(GetInv().HasItem(item.item_id));
inst->SetEvolveActivated(item.activated ? true : false);
CharacterEvolvingItemsRepository::ReplaceOne(database, item);
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, item);
}
void Client::SendEvolvingPacket(const int8 action, const CharacterEvolvingItemsRepository::CharacterEvolvingItems &item)
{
auto out = std::make_unique<EQApplicationPacket>(OP_EvolveItem, sizeof(EvolveItemToggle));
const auto data = reinterpret_cast<EvolveItemToggle *>(out->pBuffer);
LogEvolveItemDetail(
"Character ID <green>[{}] requested info for evolving item with unique id <yellow>[{}] status <yellow>[{}] "
"percentage <yellow>[{}]",
CharacterID(),
item.id,
item.activated,
item.progression
);
data->action = action;
data->unique_id = item.id;
data->percentage = item.progression;
data->activated = item.activated;
QueuePacket(out.get());
LogEvolveItem(
"Sent evolve item with unique id <yellow>[{}] status <yellow>[{}] percentage <yellow>[{}] to Character ID "
"<green>[{}]",
data->unique_id,
data->activated,
data->percentage,
CharacterID()
);
}
void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
{
std::vector<const EQ::ItemInstance *> queue{};
for (auto &[key, inst]: GetInv().GetWorn()) {
LogEvolveItemDetail(
"CharacterID <green>[{}] found equipped item ID <yellow>[{}]", CharacterID(), inst->GetID());
if (!inst->IsEvolving() || !inst->GetEvolveActivated()) {
LogEvolveItemDetail(
"CharacterID <green>[{}], item ID <yellow>[{}] not an evolving item.", CharacterID(), inst->GetID());
continue;
}
if (inst->GetTimers().contains("evolve") && !inst->GetTimers().at("evolve").Check()) {
LogEvolveItemDetail(
"CharacterID <green>[{}], item ID <yellow>[{}] timer not yet expired. <red>[{}] secs remaining.",
CharacterID(),
inst->GetID(),
inst->GetTimers().at("evolve").GetRemainingTime() / 1000);
continue;
}
auto const type = evolving_items_manager.GetEvolvingItemsCache().at(inst->GetID()).type;
auto const sub_type = evolving_items_manager.GetEvolvingItemsCache().at(inst->GetID()).sub_type;
LogEvolveItemDetail(
"CharacterID <green>[{}] item id <green>[{}] type {} sub_type {} is Evolving. Continue processing...",
CharacterID(),
inst->GetID(),
type,
sub_type);
switch (type) {
case EvolvingItems::Types::AMOUNT_OF_EXP: {
LogEvolveItemDetail("Type <green>[{}] Processing sub_type", type);
if (sub_type == EvolvingItems::SubTypes::ALL_EXP ||
(sub_type == EvolvingItems::SubTypes::GROUP_EXP && IsGrouped())) {
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfGroupExperience) / 100);
}
else if (
sub_type == EvolvingItems::SubTypes::ALL_EXP ||
(sub_type == EvolvingItems::SubTypes::RAID_EXP && IsRaidGrouped())) {
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfRaidExperience) / 100);
}
else if (
sub_type == EvolvingItems::SubTypes::ALL_EXP || sub_type == EvolvingItems::SubTypes::SOLO_EXP) {
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfSoloExperience) / 100);
}
inst->CalculateEvolveProgression();
auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression(
database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), inst->GetEvolveProgression());
if (!e.id) {
break;
}
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
LogEvolveItem(
"Processing Complete for item id <green>[{1}] Type 1 Amount of EXP - SubType <yellow>[{0}] - "
"Assigned <yellow>[{2}] of exp to <green>[{1}]",
sub_type,
inst->GetID(),
exp * 0.001
);
if (inst->GetEvolveProgression() >= 100) {
queue.push_back(inst);
}
break;
}
case EvolvingItems::Types::SPECIFIC_MOB_RACE: {
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
if (mob && mob->GetRace() == sub_type) {
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
inst->SetEvolveAddToCurrentAmount(1);
inst->CalculateEvolveProgression();
auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression(
database,
inst->GetEvolveUniqueID(),
inst->GetEvolveCurrentAmount(),
inst->GetEvolveProgression());
if (!e.id) {
break;
}
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
LogEvolveItem(
"Processing Complete for item id <green>[{1}] Type 3 Specific Mob Race - SubType <yellow>[{0}] "
"- Increased count by 1 for <green>[{1}]",
sub_type,
inst->GetID()
);
}
if (inst->GetEvolveProgression() >= 100) {
queue.push_back(inst);
}
break;
}
case EvolvingItems::Types::SPECIFIC_ZONE_ID: {
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
if (mob && mob->GetZoneID() == sub_type) {
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
inst->SetEvolveAddToCurrentAmount(1);
inst->CalculateEvolveProgression();
auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression(
database,
inst->GetEvolveUniqueID(),
inst->GetEvolveCurrentAmount(),
inst->GetEvolveProgression());
if (!e.id) {
break;
}
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
LogEvolveItem(
"Processing Complete for item id <green>[{1}] Type 4 Specific Zone ID - SubType <yellow>[{0}] "
"- Increased count by 1 for <green>[{1}]",
sub_type,
inst->GetID()
);
}
if (inst->GetEvolveProgression() >= 100) {
queue.push_back(inst);
}
break;
}
default: {
}
}
}
if (!queue.empty()) {
for (auto const &i: queue) {
DoEvolveCheckProgression(*i);
}
}
}
void Client::DoEvolveItemDisplayFinalResult(const EQApplicationPacket *app)
{
const auto in = reinterpret_cast<EvolveItemToggle *>(app->pBuffer);
const uint32 item_id = static_cast<uint32>(in->unique_id & 0xFFFFFFFF);
if (item_id == 0) {
LogEvolveItem("Error - Item ID of final evolve item is blank.");
return;
}
std::unique_ptr<EQ::ItemInstance> const inst(database.CreateItem(item_id));
LogEvolveItemDetail(
"Character ID <green>[{}] requested to view final evolve item id <yellow>[{}] for evolve item id <yellow>[{}]",
CharacterID(),
item_id,
evolving_items_manager.GetFirstItemInLoreGroupByItemID(item_id));
inst->SetEvolveProgression(100);
if (inst) {
LogEvolveItemDetail(
"Sending final result for item id <yellow>[{}] to Character ID <green>[{}]", item_id, CharacterID());
SendItemPacket(0, inst.get(), ItemPacketViewLink);
}
}
bool Client::DoEvolveCheckProgression(const EQ::ItemInstance &inst)
{
if (inst.GetEvolveProgression() < 100 || inst.GetEvolveLvl() == inst.GetMaxEvolveLvl()) {
return false;
}
const auto new_item_id = evolving_items_manager.GetNextEvolveItemID(inst);
if (!new_item_id) {
return false;
}
std::unique_ptr<EQ::ItemInstance> const new_inst(database.CreateItem(new_item_id));
PlayerEvent::EvolveItem e{};
RemoveItemBySerialNumber(inst.GetSerialNumber());
evolving_items_manager.LoadPlayerEvent(inst, e);
e.status = "Evolved Item due to obtaining progression - Old Evolve Item removed from inventory.";
RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e);
PushItemOnCursor(*new_inst, true);
evolving_items_manager.LoadPlayerEvent(*new_inst, e);
e.status = "Evolved Item due to obtaining progression - New Evolve Item placed in inventory.";
RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e);
MessageString(Chat::Yellow, EVOLVE_ITEM_EVOLVED, inst.GetItem()->Name);
LogEvolveItem(
"Evolved item id <red>[{}] into item id <green>[{}] for Character ID <green>[{}]",
inst.GetID(),
new_inst->GetID(),
CharacterID());
return true;
}
void Client::SendEvolveXPTransferWindow()
{
auto out = std::make_unique<EQApplicationPacket>(OP_EvolveItem, sizeof(EvolveItemToggle));
const auto data = reinterpret_cast<EvolveItemToggle *>(out->pBuffer);
data->action = 1;
QueuePacket(out.get());
}
void Client::SendEvolveXPWindowDetails(const EQApplicationPacket *app)
{
const auto in = reinterpret_cast<EvolveXPWindowReceive *>(app->pBuffer);
const auto item_1_slot =
GetInv().HasEvolvingItem(in->item1_unique_id, 1, invWherePersonal | invWhereWorn | invWhereCursor);
const auto item_2_slot =
GetInv().HasEvolvingItem(in->item2_unique_id, 1, invWherePersonal | invWhereWorn | invWhereCursor);
if (item_1_slot == INVALID_INDEX || item_2_slot == INVALID_INDEX) {
return;
}
const auto inst_from = GetInv().GetItem(item_1_slot);
const auto inst_to = GetInv().GetItem(item_2_slot);
if (!inst_from || !inst_to) {
return;
}
const auto results = evolving_items_manager.DetermineTransferResults(*inst_from, *inst_to);
if (!results.item_from_id || !results.item_to_id) {
SendEvolveTransferResults(*inst_from, *inst_to, *inst_from, *inst_to, 0, 0);
return;
}
std::unique_ptr<EQ::ItemInstance> const inst_from_new(database.CreateItem(results.item_from_id));
std::unique_ptr<EQ::ItemInstance> const inst_to_new(database.CreateItem(results.item_to_id));
if (!inst_from_new || !inst_to_new) {
SendEvolveTransferResults(*inst_from, *inst_to, *inst_from, *inst_to, 0, 0);
return;
}
inst_from_new->SetEvolveCurrentAmount(results.item_from_current_amount);
inst_from_new->CalculateEvolveProgression();
inst_to_new->SetEvolveCurrentAmount(results.item_to_current_amount);
inst_to_new->CalculateEvolveProgression();
SendEvolveTransferResults(
*inst_from, *inst_to, *inst_from_new, *inst_to_new, results.compatibility, results.max_transfer_level);
}
void Client::DoEvolveTransferXP(const EQApplicationPacket *app)
{
const auto in = reinterpret_cast<EvolveXPWindowReceive *>(app->pBuffer);
const auto item_1_slot =
GetInv().HasEvolvingItem(in->item1_unique_id, 1, invWherePersonal | invWhereWorn | invWhereCursor);
const auto item_2_slot =
GetInv().HasEvolvingItem(in->item2_unique_id, 1, invWherePersonal | invWhereWorn | invWhereCursor);
if (item_1_slot == INVALID_INDEX || item_2_slot == INVALID_INDEX) {
return;
}
const auto inst_from = GetInv().GetItem(item_1_slot);
const auto inst_to = GetInv().GetItem(item_2_slot);
if (!inst_from || !inst_to) {
Message(Chat::Red, "Transfer Failed. Incompatible Items.");
LogEvolveItem("Transfer Failed for Character ID <green>[{}]", CharacterID());
return;
}
const auto results = evolving_items_manager.DetermineTransferResults(*inst_from, *inst_to);
if (!results.item_from_id || !results.item_to_id) {
Message(Chat::Red, "Transfer Failed. Incompatible Items.");
LogEvolveItem("Transfer Failed for Character ID <green>[{}]", CharacterID());
return;
}
std::unique_ptr<const EQ::ItemInstance> const inst_from_new(database.CreateItem(results.item_from_id));
std::unique_ptr<const EQ::ItemInstance> const inst_to_new(database.CreateItem(results.item_to_id));
if (!inst_from_new || !inst_to_new) {
Message(Chat::Red, "Transfer Failed. Incompatible Items.");
LogEvolveItem("Transfer Failed for Character ID <green>[{}]", CharacterID());
return;
}
inst_from_new->SetEvolveCurrentAmount(results.item_from_current_amount);
inst_from_new->CalculateEvolveProgression();
inst_to_new->SetEvolveCurrentAmount(results.item_to_current_amount);
inst_to_new->CalculateEvolveProgression();
PlayerEvent::EvolveItem e{};
RemoveItemBySerialNumber(inst_from->GetSerialNumber());
evolving_items_manager.LoadPlayerEvent(*inst_from, e);
e.status = "Transfer XP - Original FROM Evolve Item removed from inventory.";
RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e);
PushItemOnCursor(*inst_from_new, true);
evolving_items_manager.LoadPlayerEvent(*inst_from_new, e);
e.status = "Transfer XP - Updated FROM item placed in inventory.";
RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e);
RemoveItemBySerialNumber(inst_to->GetSerialNumber());
evolving_items_manager.LoadPlayerEvent(*inst_to, e);
e.status = "Transfer XP - Original TO Evolve Item removed from inventory.";
RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e);
PushItemOnCursor(*inst_to_new, true);
evolving_items_manager.LoadPlayerEvent(*inst_to_new, e);
e.status = "Transfer XP - Updated TO Evolve item placed in inventory.";
RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e);
LogEvolveItem(
"Evolve Transfer XP resulted in evolved item id <red>[{}] into item id <green>[{}] for Character ID "
"<green>[{}]",
inst_to->GetID(),
inst_to_new->GetID(),
CharacterID()
);
}
void Client::SendEvolveTransferResults(
const EQ::ItemInstance &inst_from,
const EQ::ItemInstance &inst_to,
const EQ::ItemInstance &inst_from_new,
const EQ::ItemInstance &inst_to_new,
const uint32 compatibility,
const uint32 max_transfer_level)
{
std::stringstream ss;
cereal::BinaryOutputArchive ar(ss);
EvolveXPWindowSend e{};
e.action = EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS;
e.compatibility = compatibility;
e.item1_unique_id = inst_from.GetEvolveUniqueID();
e.item2_unique_id = inst_to.GetEvolveUniqueID();
e.max_transfer_level = max_transfer_level;
e.item1_present = 1;
e.item2_present = 1;
e.serialize_item_1 = inst_from_new.Serialize(0);
e.serialize_item_2 = inst_to_new.Serialize(0);
{
ar(e);
}
uint32 packet_size = sizeof(EvolveItemMessaging) + ss.str().length();
std::unique_ptr<EQApplicationPacket> out(new EQApplicationPacket(OP_EvolveItem, packet_size));
const auto data = reinterpret_cast<EvolveItemMessaging *>(out->pBuffer);
data->action = EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS;
memcpy(data->serialized_data, ss.str().data(), ss.str().length());
QueuePacket(out.get());
ss.str("");
ss.clear();
}
+167 -27
View File
@@ -66,6 +66,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/repositories/character_corpses_repository.h"
#include "../common/repositories/guild_tributes_repository.h"
#include "../common/repositories/buyer_buy_lines_repository.h"
#include "../common/repositories/character_pet_name_repository.h"
#include "../common/events/player_event_logs.h"
#include "../common/repositories/character_stats_record_repository.h"
@@ -80,7 +81,6 @@ extern PetitionList petition_list;
extern EntityList entity_list;
typedef void (Client::*ClientPacketProc)(const EQApplicationPacket *app);
//Use a map for connecting opcodes since it dosent get used a lot and is sparse
std::map<uint32, ClientPacketProc> ConnectingOpcodes;
//Use a static array for connected, for speed
@@ -161,6 +161,7 @@ void MapOpcodes()
ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade;
ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell;
ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage;
ConnectedOpcodes[OP_ChangePetName] = &Client::Handle_OP_ChangePetName;
ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs;
ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks;
ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname;
@@ -208,6 +209,7 @@ void MapOpcodes()
ConnectedOpcodes[OP_Emote] = &Client::Handle_OP_Emote;
ConnectedOpcodes[OP_EndLootRequest] = &Client::Handle_OP_EndLootRequest;
ConnectedOpcodes[OP_EnvDamage] = &Client::Handle_OP_EnvDamage;
ConnectedOpcodes[OP_EvolveItem] = &Client::Handle_OP_EvolveItem;
ConnectedOpcodes[OP_FaceChange] = &Client::Handle_OP_FaceChange;
ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath;
ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest;
@@ -283,6 +285,7 @@ void MapOpcodes()
ConnectedOpcodes[OP_ItemLinkResponse] = &Client::Handle_OP_ItemLinkResponse;
ConnectedOpcodes[OP_ItemName] = &Client::Handle_OP_ItemName;
ConnectedOpcodes[OP_ItemPreview] = &Client::Handle_OP_ItemPreview;
ConnectedOpcodes[OP_ItemPreviewRequest] = &Client::Handle_OP_ItemPreviewRequest;
ConnectedOpcodes[OP_ItemVerifyRequest] = &Client::Handle_OP_ItemVerifyRequest;
ConnectedOpcodes[OP_ItemViewUnknown] = &Client::Handle_OP_Ignore;
ConnectedOpcodes[OP_Jump] = &Client::Handle_OP_Jump;
@@ -339,6 +342,7 @@ void MapOpcodes()
ConnectedOpcodes[OP_PlayerStateAdd] = &Client::Handle_OP_PlayerStateAdd;
ConnectedOpcodes[OP_PlayerStateRemove] = &Client::Handle_OP_PlayerStateRemove;
ConnectedOpcodes[OP_PickPocket] = &Client::Handle_OP_PickPocket;
ConnectedOpcodes[OP_PickZone] = &Client::Handle_OP_PickZone;
ConnectedOpcodes[OP_PopupResponse] = &Client::Handle_OP_PopupResponse;
ConnectedOpcodes[OP_PotionBelt] = &Client::Handle_OP_PotionBelt;
ConnectedOpcodes[OP_PurchaseLeadershipAA] = &Client::Handle_OP_PurchaseLeadershipAA;
@@ -818,6 +822,10 @@ void Client::CompleteConnect()
CharacterID()
)
);
if (IsPetNameChangeAllowed()) {
InvokeChangePetName(false);
}
}
if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
@@ -941,6 +949,11 @@ void Client::CompleteConnect()
ShowDevToolsMenu();
}
auto z = GetZone(GetZoneID(), GetInstanceVersion());
if (z && z->shard_at_player_count > 0 && !RuleB(Zone, ZoneShardQuestMenuOnly)) {
ShowZoneShardMenu();
}
// shared tasks memberlist
if (RuleB(TaskSystem, EnableTaskSystem) && GetTaskState()->HasActiveSharedTask()) {
@@ -964,6 +977,16 @@ void Client::CompleteConnect()
RecordStats();
AutoGrantAAPoints();
// set initial position for mob tracking
m_last_seen_mob_position.reserve(entity_list.GetMobList().size());
for (auto& mob : entity_list.GetMobList()) {
if (!mob.second->IsNPC()) {
continue;
}
m_last_seen_mob_position[mob.second->GetID()] = mob.second->GetPosition();
}
// enforce some rules..
if (!CanEnterZone()) {
LogInfo("Kicking character [{}] from zone, not allowed here (missing requirements)", GetCleanName());
@@ -1303,7 +1326,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
// set to full support in case they're a gm with items in disabled expansion slots...but, have their gm flag off...
// item loss will occur when they use the 'empty' slots, if this is not done
m_inv.SetGMInventory(true);
loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */
loaditems = database.GetInventory(this); /* Load Character Inventory */
database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */
database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */
database.LoadCharacterMaterialColor(cid, &m_pp); /* Load Character Material */
@@ -1858,8 +1881,9 @@ void Client::Handle_OP_AAAction(const EQApplicationPacket *app)
PurchaseAlternateAdvancementRank(action->ability);
}
else if (action->action == aaActionDisableEXP) { //Turn Off AA Exp
if (m_epp.perAA > 0)
if (m_epp.perAA > 0) {
MessageString(Chat::White, AA_OFF);
}
m_epp.perAA = 0;
SendAlternateAdvancementStats();
@@ -3228,11 +3252,11 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
if (!new_aug) { // Shouldn't get the OP code without the augment on the user's cursor, but maybe it's h4x.
LogError("AugmentItem OpCode with 'Insert' or 'Swap' action received, but no augment on client's cursor");
Message(Chat::Red, "Error: No augment found on cursor for inserting.");
return;
break;
} else {
if (!RuleB(Inventory, AllowMultipleOfSameAugment) && tobe_auged->ContainsAugmentByID(new_aug->GetID())) {
Message(Chat::Red, "Error: Cannot put multiple of the same augment in an item.");
return;
break;
}
if (
@@ -3316,7 +3340,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
in_augment->augment_index
).c_str()
);
return;
break;
}
item_one_to_push = tobe_auged->Clone();
@@ -3388,7 +3412,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
}
} else {
Message(Chat::Red, "Error: Could not find augmentation to remove at index %i. Aborting.", in_augment->augment_index);
return;
break;
}
old_aug = tobe_auged->RemoveAugment(in_augment->augment_index);
@@ -3421,7 +3445,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
if (!PutItemInInventory(EQ::invslot::slotCursor, *item_two_to_push, true)) {
LogError("Problem returning augment to player's cursor after safe removal");
Message(Chat::Yellow, "Error: Failed to return augment after removal from item!");
return;
break;
}
}
break;
@@ -3466,7 +3490,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
in_augment->augment_index
).c_str()
);
return;
break;
}
tobe_auged->DeleteAugment(in_augment->augment_index);
@@ -3487,6 +3511,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
if (material != EQ::textures::materialInvalid) {
SendWearChange(material);
}
break;
default: // Unknown
LogInventory(
@@ -3500,9 +3525,12 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
);
break;
}
safe_delete(item_one_to_push);
safe_delete(item_two_to_push);
} else {
Object::HandleAugmentation(this, in_augment, m_tradeskill_object); // Delegate to tradeskill object to perform combine
}
return;
}
@@ -4550,6 +4578,27 @@ void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app)
return;
}
void Client::Handle_OP_ChangePetName(const EQApplicationPacket *app) {
if (app->size != sizeof(ChangePetName_Struct)) {
LogError("Got OP_ChangePetName of incorrect size. Expected [{}], got [{}].", sizeof(ChangePetName_Struct), app->size);
return;
}
auto p = (ChangePetName_Struct *) app->pBuffer;
if (!IsPetNameChangeAllowed()) {
p->response_code = ChangePetNameResponse::NotEligible;
QueuePacket(app);
return;
}
p->response_code = ChangePetNameResponse::Denied;
if (ChangePetName(p->new_pet_name)) {
p->response_code = ChangePetNameResponse::Accepted;
}
QueuePacket(app);
}
void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app)
{
if (!RuleB(Spells, EnableBlockedBuffs))
@@ -4793,11 +4842,10 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
auto boat_delta = glm::vec4(ppu->delta_x, ppu->delta_y, ppu->delta_z, EQ10toFloat(ppu->delta_heading));
cmob->SetDelta(boat_delta);
auto outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
PlayerPositionUpdateServer_Struct *ppus = (PlayerPositionUpdateServer_Struct *) outapp->pBuffer;
static EQApplicationPacket outapp(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
auto *ppus = (PlayerPositionUpdateServer_Struct *) outapp.pBuffer;
cmob->MakeSpawnUpdate(ppus);
entity_list.QueueCloseClients(cmob, outapp, true, 300, this, false);
safe_delete(outapp);
entity_list.QueueCloseClients(cmob, &outapp, true, 300, this, false);
/* Update the boat's position on the server, without sending an update */
cmob->GMMove(ppu->x_pos, ppu->y_pos, ppu->z_pos, EQ12toFloat(ppu->heading));
@@ -4948,7 +4996,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
CheckScanCloseMobsMovingTimer();
}
CheckSendBulkClientPositionUpdate();
CheckSendBulkNpcPositions();
int32 new_animation = ppu->animation;
@@ -4971,15 +5019,15 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
m_Position.w = new_heading;
/* Broadcast update to other clients */
auto outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
PlayerPositionUpdateServer_Struct *position_update = (PlayerPositionUpdateServer_Struct *) outapp->pBuffer;
static EQApplicationPacket outapp(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
PlayerPositionUpdateServer_Struct *position_update = (PlayerPositionUpdateServer_Struct *) outapp.pBuffer;
MakeSpawnUpdate(position_update);
if (gm_hide_me) {
entity_list.QueueClientsStatus(this, outapp, true, Admin(), AccountStatus::Max);
entity_list.QueueClientsStatus(this, &outapp, true, Admin(), AccountStatus::Max);
} else {
entity_list.QueueCloseClients(this, outapp, true, RuleI(Range, ClientPositionUpdates), nullptr, true);
entity_list.QueueCloseClients(this, &outapp, true, RuleI(Range, ClientPositionUpdates), nullptr, true);
}
@@ -4988,12 +5036,10 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
Raid *raid = GetRaid();
if (raid) {
raid->QueueClients(this, outapp, true, true, (RuleI(Range, ClientPositionUpdates) * -1));
raid->QueueClients(this, &outapp, true, true, (RuleI(Range, ClientPositionUpdates) * -1));
} else if (group) {
group->QueueClients(this, outapp, true, true, (RuleI(Range, ClientPositionUpdates) * -1));
group->QueueClients(this, &outapp, true, true, (RuleI(Range, ClientPositionUpdates) * -1));
}
safe_delete(outapp);
}
if (zone->watermap) {
@@ -9293,6 +9339,30 @@ void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app)
return;
}
void Client::Handle_OP_ItemPreviewRequest(const EQApplicationPacket* app)
{
VERIFY_PACKET_LENGTH(OP_ItemPreviewRequest, app, ItemPreview_Struct);
auto ips = (ItemPreview_Struct*) app->pBuffer;
const EQ::ItemData* item = database.GetItem(ips->itemid);
if (item) {
EQ::ItemInstance* inst = database.CreateItem(item);
if (inst) {
std::string packet = inst->Serialize(-1);
auto outapp = new EQApplicationPacket(OP_ItemPreviewRequest, packet.length());
memcpy(outapp->pBuffer, packet.c_str(), packet.length());
#if EQDEBUG >= 9
DumpPacket(outapp);
#endif
QueuePacket(outapp);
safe_delete(outapp);
safe_delete(inst);
}
}
}
void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
{
using EQ::spells::CastingSlot;
@@ -10741,7 +10811,12 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app)
InterrogateInventory(this, true, false, true, error);
}
return;
for (int slot : {mi->to_slot, mi->from_slot}) {
auto item = GetInv().GetItem(slot);
if (item && item->IsEvolving()) {
CharacterEvolvingItemsRepository::UpdateOne(database, item->GetEvolvingDetails());
}
}
}
void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app)
@@ -10808,6 +10883,7 @@ void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app)
InterrogateInventory(this, true, false, true, error);
}
}
safe_delete(mi);
}
// This is the swap.
// Client behavior is just to move stacks without combining them
@@ -11839,6 +11915,17 @@ void Client::Handle_OP_PickPocket(const EQApplicationPacket *app)
SendPickPocketResponse(victim, 0, PickPocketFailed);
}
void Client::Handle_OP_PickZone(const EQApplicationPacket *app)
{
if (app->size != sizeof(PickZone_Struct)) {
LogDebug("Size mismatch in OP_PickZone expected [{}] got [{}]", sizeof(PickZone_Struct), app->size);
DumpPacket(app);
return;
}
// handle
}
void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app)
{
@@ -15512,6 +15599,26 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
// Client has elected to buy an item from a Trader
//
auto in = (TraderBuy_Struct *) app->pBuffer;
if (RuleB(Bazaar, UseAlternateBazaarSearch) && in->trader_id >= TraderRepository::TRADER_CONVERT_ID) {
auto trader = TraderRepository::GetTraderByInstanceAndSerialnumber(
database,
in->trader_id - TraderRepository::TRADER_CONVERT_ID,
in->serial_number
);
if (!trader.trader_id) {
LogTrading("Unable to convert trader id for {} and serial number {}. Trader Buy aborted.",
in->trader_id - TraderRepository::TRADER_CONVERT_ID,
in->serial_number
);
return;
}
in->trader_id = trader.trader_id;
strn0cpy(in->seller_name, trader.trader_name.c_str(), sizeof(in->seller_name));
}
auto trader = entity_list.GetClientByID(in->trader_id);
switch (in->method) {
@@ -16637,13 +16744,12 @@ bool Client::CanTradeFVNoDropItem()
void Client::SendMobPositions()
{
auto p = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
auto *s = (PlayerPositionUpdateServer_Struct *) p->pBuffer;
static EQApplicationPacket p(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
auto *s = (PlayerPositionUpdateServer_Struct *) p.pBuffer;
for (auto &m: entity_list.GetMobList()) {
m.second->MakeSpawnUpdate(s);
QueuePacket(p, false);
QueuePacket(&p, false);
}
safe_delete(p);
}
struct RecordKillCheck {
@@ -17147,3 +17253,37 @@ void Client::Handle_OP_ShopRetrieveParcel(const EQApplicationPacket *app)
auto parcel_in = (ParcelRetrieve_Struct *)app->pBuffer;
DoParcelRetrieve(*parcel_in);
}
void Client::Handle_OP_EvolveItem(const EQApplicationPacket *app)
{
if (app->size != sizeof(EvolveItemToggle)) {
LogError(
"Received Handle_OP_EvolveItem packet. Expected size {}, received size {}.",
sizeof(EvolveItemToggle),
app->size
);
return;
}
auto in = reinterpret_cast<EvolveItemToggle *>(app->pBuffer);
switch (in->action) {
case EvolvingItems::Actions::UPDATE_ITEMS: {
DoEvolveItemToggle(app);
break;
}
case EvolvingItems::Actions::FINAL_RESULT: {
DoEvolveItemDisplayFinalResult(app);
break;
}
case EvolvingItems::Actions::TRANSFER_XP: {
DoEvolveTransferXP(app);
break;
}
case EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS: {
SendEvolveXPWindowDetails(app);
}
default: {
}
}
}
+3
View File
@@ -114,6 +114,7 @@
void Handle_OP_Emote(const EQApplicationPacket *app);
void Handle_OP_EndLootRequest(const EQApplicationPacket *app);
void Handle_OP_EnvDamage(const EQApplicationPacket *app);
void Handle_OP_EvolveItem(const EQApplicationPacket *app);
void Handle_OP_FaceChange(const EQApplicationPacket *app);
void Handle_OP_FeignDeath(const EQApplicationPacket *app);
void Handle_OP_FindPersonRequest(const EQApplicationPacket *app);
@@ -188,6 +189,7 @@
void Handle_OP_ItemLinkResponse(const EQApplicationPacket *app);
void Handle_OP_ItemName(const EQApplicationPacket *app);
void Handle_OP_ItemPreview(const EQApplicationPacket *app);
void Handle_OP_ItemPreviewRequest(const EQApplicationPacket *app);
void Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app);
void Handle_OP_Jump(const EQApplicationPacket *app);
void Handle_OP_KeyRing(const EQApplicationPacket *app);
@@ -242,6 +244,7 @@
void Handle_OP_PlayerStateAdd(const EQApplicationPacket *app);
void Handle_OP_PlayerStateRemove(const EQApplicationPacket *app);
void Handle_OP_PickPocket(const EQApplicationPacket *app);
void Handle_OP_PickZone(const EQApplicationPacket *app);
void Handle_OP_PopupResponse(const EQApplicationPacket *app);
void Handle_OP_PotionBelt(const EQApplicationPacket *app);
void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app);
+2 -2
View File
@@ -122,7 +122,7 @@ bool Client::Process() {
/* I haven't naturally updated my position in 10 seconds, updating manually */
if (!IsMoving() && m_position_update_timer.Check()) {
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
BroadcastPositionUpdate();
}
if (mana_timer.Check())
@@ -764,7 +764,7 @@ void Client::BulkSendInventoryItems()
RemoveNoRent(false);
}
RemoveDuplicateLore(false);
RemoveDuplicateLore();
MoveSlotNotAllowed(false);
EQ::OutBuffer ob;
+4
View File
@@ -126,6 +126,7 @@ int command_init(void)
command_add("enablerecipe", "[Recipe ID] - Enables a Recipe", AccountStatus::QuestTroupe, command_enablerecipe) ||
command_add("entityvariable", "[clear|delete|set|view] - Modify entity variables for yourself or your target", AccountStatus::GMAdmin, command_entityvariable) ||
command_add("exptoggle", "[Toggle] - Toggle your or your target's experience gain.", AccountStatus::QuestTroupe, command_exptoggle) ||
command_add("evolve", "Evolving Item manipulation commands. Use argument help for more info.", AccountStatus::Steward, command_evolvingitems) ||
command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", AccountStatus::QuestTroupe, command_faction) ||
command_add("factionassociation", "[factionid] [amount] - triggers a faction hits via association", AccountStatus::GMLeadAdmin, command_faction_association) ||
command_add("feature", "Change your or your target's feature's temporarily", AccountStatus::QuestTroupe, command_feature) ||
@@ -243,6 +244,7 @@ int command_init(void)
command_add("zone", "[Zone ID|Zone Short Name] [X] [Y] [Z] - Teleport to specified Zone by ID or Short Name (coordinates are optional)", AccountStatus::Guide, command_zone) ||
command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", AccountStatus::GMLeadAdmin, command_zonebootup) ||
command_add("zoneinstance", "[Instance ID] [X] [Y] [Z] - Teleport to specified Instance by ID (coordinates are optional)", AccountStatus::Guide, command_zone_instance) ||
command_add("zoneshard", "[zone] [instance_id] - Teleport explicitly to a zone shard", AccountStatus::Player, command_zone_shard) ||
command_add("zoneshutdown", "[shortname] - Shut down a zone server", AccountStatus::GMLeadAdmin, command_zoneshutdown) ||
command_add("zsave", " Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave)
) {
@@ -825,6 +827,7 @@ void command_bot(Client *c, const Seperator *sep)
#include "gm_commands/entityvariable.cpp"
#include "gm_commands/exptoggle.cpp"
#include "gm_commands/faction.cpp"
#include "gm_commands/evolving_items.cpp"
#include "gm_commands/feature.cpp"
#include "gm_commands/find.cpp"
#include "gm_commands/fish.cpp"
@@ -936,4 +939,5 @@ void command_bot(Client *c, const Seperator *sep)
#include "gm_commands/zonebootup.cpp"
#include "gm_commands/zoneshutdown.cpp"
#include "gm_commands/zone_instance.cpp"
#include "gm_commands/zone_shard.cpp"
#include "gm_commands/zsave.cpp"
+3
View File
@@ -38,6 +38,7 @@ void SendShowInventorySubCommands(Client *c);
void SendFixMobSubCommands(Client *c);
void SendDataBucketsSubCommands(Client *c);
void SendParcelsSubCommands(Client *c);
void SendEvolvingItemsSubCommands(Client *c);
// Commands
void command_acceptrules(Client *c, const Seperator *sep);
@@ -80,6 +81,7 @@ void command_emptyinventory(Client *c, const Seperator *sep);
void command_enablerecipe(Client *c, const Seperator *sep);
void command_entityvariable(Client *c, const Seperator *sep);
void command_exptoggle(Client *c, const Seperator *sep);
void command_evolvingitems(Client *c, const Seperator *sep);
void command_faction(Client *c, const Seperator *sep);
void command_faction_association(Client *c, const Seperator *sep);
void command_feature(Client *c, const Seperator *sep);
@@ -195,6 +197,7 @@ void command_wpadd(Client *c, const Seperator *sep);
void command_worldwide(Client *c, const Seperator *sep);
void command_zone(Client *c, const Seperator *sep);
void command_zone_instance(Client *c, const Seperator *sep);
void command_zone_shard(Client *c, const Seperator *sep);
void command_zonebootup(Client *c, const Seperator *sep);
void command_zoneshutdown(Client *c, const Seperator *sep);
void command_zopp(Client *c, const Seperator *sep);
+153 -39
View File
@@ -1,12 +1,15 @@
#include "data_bucket.h"
#include "entity.h"
#include "zonedb.h"
#include "mob.h"
#include "worldserver.h"
#include <ctime>
#include <cctype>
#include "../common/json/json.hpp"
using json = nlohmann::json;
extern WorldServer worldserver;
const std::string NESTED_KEY_DELIMITER = ".";
std::vector<DataBucketsRepository::DataBuckets> g_data_bucket_cache = {};
@@ -16,6 +19,7 @@ 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
@@ -24,8 +28,13 @@ void DataBucket::SetData(const std::string &bucket_key, const std::string &bucke
DataBucket::SetData(k);
}
void DataBucket::SetData(const DataBucketKey &k)
void DataBucket::SetData(const DataBucketKey &k_)
{
DataBucketKey k = k_; // copy the key so we can modify it
if (k.key.find(NESTED_KEY_DELIMITER) != std::string::npos) {
k.key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
}
auto b = DataBucketsRepository::NewEntity();
auto r = GetData(k, true);
// if we have an entry, use it
@@ -37,6 +46,9 @@ void DataBucket::SetData(const DataBucketKey &k)
if (k.character_id > 0) {
b.character_id = k.character_id;
}
else if (k.account_id > 0) {
b.account_id = k.account_id;
}
else if (k.npc_id > 0) {
b.npc_id = k.npc_id;
}
@@ -56,9 +68,48 @@ void DataBucket::SetData(const DataBucketKey &k)
b.expires = expires_time_unix;
b.value = k.value;
b.key_ = k.key;
// Check for nested keys (keys with dots)
if (k_.key.find(NESTED_KEY_DELIMITER) != std::string::npos) {
// Retrieve existing JSON or create a new one
std::string existing_value = r.id > 0 ? r.value : "{}";
json json_value = json::object();
try {
json_value = json::parse(existing_value);
} catch (json::parse_error &e) {
LogError("Failed to parse JSON for key [{}]: {}", k_.key, e.what());
json_value = json::object(); // Reset to an empty object on error
}
// Recursively merge new key-value pair into the JSON object
auto nested_keys = Strings::Split(k_.key, NESTED_KEY_DELIMITER);
json *current = &json_value;
for (size_t i = 0; i < nested_keys.size(); ++i) {
const std::string &key_part = nested_keys[i];
if (i == nested_keys.size() - 1) {
// Set the value at the final key
(*current)[key_part] = k_.value;
} else {
// Traverse or create nested objects
if (!current->contains(key_part)) {
(*current)[key_part] = json::object();
} else if (!(*current)[key_part].is_object()) {
// If key exists but is not an object, reset to object to avoid conflicts
(*current)[key_part] = json::object();
}
current = &(*current)[key_part];
}
}
// Serialize JSON back to string
b.value = json_value.dump();
b.key_ = nested_keys.front(); // Use the top-level key
}
if (bucket_id) {
// update the cache if it exists
if (CanCache(k)) {
for (auto &e: g_data_bucket_cache) {
@@ -72,7 +123,6 @@ void DataBucket::SetData(const DataBucketKey &k)
DataBucketsRepository::UpdateOne(database, b);
}
else {
b.key_ = k.key;
b = DataBucketsRepository::InsertOne(database, b);
// add to cache if it doesn't exist
@@ -88,25 +138,70 @@ std::string DataBucket::GetData(const std::string &bucket_key)
return GetData(DataBucketKey{.key = bucket_key}).value;
}
DataBucketsRepository::DataBuckets DataBucket::ExtractNestedValue(
const DataBucketsRepository::DataBuckets &bucket,
const std::string &full_key)
{
auto nested_keys = Strings::Split(full_key, NESTED_KEY_DELIMITER);
json json_value;
try {
json_value = json::parse(bucket.value); // Parse the JSON
} catch (json::parse_error &ex) {
LogError("Failed to parse JSON for key [{}]: {}", bucket.key_, ex.what());
return DataBucketsRepository::NewEntity(); // Return empty entity on parse error
}
// Start from the top-level key (e.g., "progression")
json *current = &json_value;
// Traverse the JSON structure
for (const auto &key_part: nested_keys) {
LogDataBuckets("Looking for key part [{}] in JSON", key_part);
if (!current->contains(key_part)) {
LogDataBuckets("Key part [{}] not found in JSON for [{}]", key_part, full_key);
return DataBucketsRepository::NewEntity();
}
current = &(*current)[key_part];
}
// Create a new entity with the extracted value
DataBucketsRepository::DataBuckets result = bucket; // Copy the original bucket
result.value = current->is_string() ? current->get<std::string>() : current->dump();
return result;
}
// GetData fetches bucket data from the database or cache if it exists
// if the bucket doesn't exist, it will be added to the cache as a miss
// if ignore_misses_cache is true, the bucket will not be added to the cache as a miss
// the only place we should be ignoring the misses cache is on the initial read during SetData
DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, bool ignore_misses_cache)
DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k_, bool ignore_misses_cache)
{
DataBucketKey k = k_; // Copy the key so we can modify it
bool is_nested_key = k.key.find(NESTED_KEY_DELIMITER) != std::string::npos;
// Extract the top-level key for nested keys
if (is_nested_key) {
k.key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
}
LogDataBuckets(
"Getting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}]",
"Getting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id
);
bool can_cache = CanCache(k);
// check the cache first if we can cache
// Attempt to retrieve the value from the cache
if (can_cache) {
for (const auto &e: g_data_bucket_cache) {
for (const auto &e : g_data_bucket_cache) {
if (CheckBucketMatch(e, k)) {
if (e.expires > 0 && e.expires < std::time(nullptr)) {
LogDataBuckets("Attempted to read expired key [{}] removing from cache", e.key_);
@@ -114,43 +209,39 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
return DataBucketsRepository::NewEntity();
}
// this is a bucket miss, return empty entity
// we still cache bucket misses, so we don't have to hit the database
if (e.id == 0) {
return DataBucketsRepository::NewEntity();
LogDataBuckets("Returning key [{}] value [{}] from cache", e.key_, e.value);
if (is_nested_key) {
return ExtractNestedValue(e, k_.key);
}
LogDataBuckets("Returning key [{}] value [{}] from cache", e.key_, e.value);
return e;
}
}
}
// Fetch the value from the database
auto r = DataBucketsRepository::GetWhere(
database,
fmt::format(
"{} `key` = '{}' LIMIT 1",
" {} `key` = '{}' LIMIT 1",
DataBucket::GetScopedDbFilters(k),
k.key
)
);
if (r.empty()) {
// if we're ignoring the misses cache, don't add to the cache
// the only place this is ignored is during the initial read of SetData
bool add_to_misses_cache = !ignore_misses_cache && can_cache;
if (add_to_misses_cache) {
// Handle cache misses
if (!ignore_misses_cache && can_cache) {
size_t size_before = g_data_bucket_cache.size();
// cache bucket misses, so we don't have to hit the database
// when scripts try to read a bucket that doesn't exist
g_data_bucket_cache.emplace_back(
DataBucketsRepository::DataBuckets{
.id = 0,
.key_ = k.key,
.value = "",
.expires = 0,
.account_id = k.account_id,
.character_id = k.character_id,
.npc_id = k.npc_id,
.bot_id = k.bot_id
@@ -158,8 +249,9 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
);
LogDataBuckets(
"Key [{}] not found in database, adding to cache as a miss 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 [{}] cache size before [{}] after [{}]",
k.key,
k.account_id,
k.character_id,
k.npc_id,
k.bot_id,
@@ -168,22 +260,21 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
);
}
return {};
return DataBucketsRepository::NewEntity();
}
auto bucket = r.front();
// if the entry has expired, delete it
if (bucket.expires > 0 && bucket.expires < (long long) std::time(nullptr)) {
// If the entry has expired, delete it
if (bucket.expires > 0 && bucket.expires < static_cast<long long>(std::time(nullptr))) {
DeleteData(k);
return {};
return DataBucketsRepository::NewEntity();
}
// add to cache if it doesn't exist
// Add the value to the cache if it doesn't exist
if (can_cache) {
bool has_cache = false;
for (auto &e: g_data_bucket_cache) {
for (const auto &e : g_data_bucket_cache) {
if (e.id == bucket.id) {
has_cache = true;
break;
@@ -195,6 +286,11 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
}
}
// Handle nested key extraction
if (is_nested_key) {
return ExtractNestedValue(bucket, k_.key);
}
return bucket;
}
@@ -216,8 +312,6 @@ bool DataBucket::DeleteData(const std::string &bucket_key)
// GetDataBuckets bulk loads all data buckets for a mob
bool DataBucket::GetDataBuckets(Mob *mob)
{
DataBucketLoadType::Type t{};
const uint32 id = mob->GetMobTypeIdentifier();
if (!id) {
@@ -225,14 +319,13 @@ bool DataBucket::GetDataBuckets(Mob *mob)
}
if (mob->IsBot()) {
t = DataBucketLoadType::Bot;
BulkLoadEntitiesToCache(DataBucketLoadType::Bot, {id});
}
else if (mob->IsClient()) {
t = DataBucketLoadType::Client;
BulkLoadEntitiesToCache(DataBucketLoadType::Account, {id});
BulkLoadEntitiesToCache(DataBucketLoadType::Client, {id});
}
BulkLoadEntitiesToCache(t, {id});
return true;
}
@@ -254,9 +347,10 @@ bool DataBucket::DeleteData(const DataBucketKey &k)
);
LogDataBuckets(
"Deleting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}] cache size before [{}] after [{}]",
"Deleting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}] cache size before [{}] after [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id,
size_before,
@@ -277,9 +371,10 @@ bool DataBucket::DeleteData(const DataBucketKey &k)
std::string DataBucket::GetDataExpires(const DataBucketKey &k)
{
LogDataBuckets(
"Getting bucket expiration key [{}] bot_id [{}] character_id [{}] npc_id [{}]",
"Getting bucket expiration key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id
);
@@ -295,9 +390,10 @@ std::string DataBucket::GetDataExpires(const DataBucketKey &k)
std::string DataBucket::GetDataRemaining(const DataBucketKey &k)
{
LogDataBuckets(
"Getting bucket remaining key [{}] bot_id [{}] character_id [{}] npc_id [{}]",
"Getting bucket remaining key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id
);
@@ -320,6 +416,13 @@ std::string DataBucket::GetScopedDbFilters(const DataBucketKey &k)
query.emplace_back("character_id = 0");
}
if (k.account_id > 0) {
query.emplace_back(fmt::format("account_id = {}", k.account_id));
}
else {
query.emplace_back("account_id = 0");
}
if (k.npc_id > 0) {
query.emplace_back(fmt::format("npc_id = {}", k.npc_id));
}
@@ -346,6 +449,7 @@ bool DataBucket::CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe,
return (
dbe.key_ == k.key &&
dbe.bot_id == k.bot_id &&
dbe.account_id == k.account_id &&
dbe.character_id == k.character_id &&
dbe.npc_id == k.npc_id
);
@@ -364,6 +468,9 @@ void DataBucket::BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector
if (t == DataBucketLoadType::Bot) {
has_cache = e.bot_id == ids[0];
}
else if (t == DataBucketLoadType::Account) {
has_cache = e.account_id == ids[0];
}
else if (t == DataBucketLoadType::Client) {
has_cache = e.character_id == ids[0];
}
@@ -384,6 +491,9 @@ void DataBucket::BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector
case DataBucketLoadType::Client:
column = "character_id";
break;
case DataBucketLoadType::Account:
column = "account_id";
break;
default:
LogError("Incorrect LoadType [{}]", static_cast<int>(t));
break;
@@ -442,6 +552,7 @@ void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id)
[&](DataBucketsRepository::DataBuckets &e) {
return (
(type == DataBucketLoadType::Bot && e.bot_id == id) ||
(type == DataBucketLoadType::Account && e.account_id == id) ||
(type == DataBucketLoadType::Client && e.character_id == id)
);
}
@@ -481,6 +592,7 @@ void DataBucket::DeleteFromMissesCache(DataBucketsRepository::DataBuckets e)
g_data_bucket_cache.end(),
[&](DataBucketsRepository::DataBuckets &ce) {
return ce.id == 0 && ce.key_ == e.key_ &&
ce.account_id == e.account_id &&
ce.character_id == e.character_id &&
ce.npc_id == e.npc_id &&
ce.bot_id == e.bot_id;
@@ -516,6 +628,8 @@ void DataBucket::DeleteFromCache(uint64 id, DataBucketLoadType::Type type)
return e.bot_id == id;
case DataBucketLoadType::Client:
return e.character_id == id;
case DataBucketLoadType::Account:
return e.account_id == id;
default:
return false;
}
@@ -539,7 +653,7 @@ void DataBucket::DeleteFromCache(uint64 id, DataBucketLoadType::Type type)
// 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.bot_id > 0) {
if (key.character_id > 0 || key.account_id > 0 || key.bot_id > 0) {
return true;
}
+10 -5
View File
@@ -12,20 +12,23 @@ struct DataBucketKey {
std::string key;
std::string value;
std::string expires;
int64_t character_id;
int64_t npc_id;
int64_t bot_id;
int64_t account_id = 0;
int64_t character_id = 0;
int64_t npc_id = 0;
int64_t bot_id = 0;
};
namespace DataBucketLoadType {
enum Type : uint8 {
Bot,
Account,
Client,
MaxType
};
static const std::string Name[Type::MaxType] = {
"Bot",
"Account",
"Client",
};
}
@@ -42,9 +45,9 @@ public:
static bool GetDataBuckets(Mob *mob);
// scoped bucket methods
static void SetData(const DataBucketKey &k);
static void SetData(const DataBucketKey &k_);
static bool DeleteData(const DataBucketKey &k);
static DataBucketsRepository::DataBuckets GetData(const DataBucketKey &k, bool ignore_misses_cache = false);
static DataBucketsRepository::DataBuckets GetData(const DataBucketKey &k_, bool ignore_misses_cache = false);
static std::string GetDataExpires(const DataBucketKey &k);
static std::string GetDataRemaining(const DataBucketKey &k);
static std::string GetScopedDbFilters(const DataBucketKey &k);
@@ -60,6 +63,8 @@ public:
static void ClearCache();
static void DeleteFromCache(uint64 id, DataBucketLoadType::Type type);
static bool CanCache(const DataBucketKey &key);
static DataBucketsRepository::DataBuckets
ExtractNestedValue(const DataBucketsRepository::DataBuckets &bucket, const std::string &full_key);
};
#endif //EQEMU_DATABUCKET_H
+5
View File
@@ -30,6 +30,7 @@
#include "string_ids.h"
#include "worldserver.h"
#include "zonedb.h"
#include "../common/evolving_items.h"
#include "../common/repositories/criteria/content_filter_criteria.h"
#include <string.h>
@@ -610,6 +611,10 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
}
}
}
if (GetOpenType() == 40 && sender->GetZoneID() == Zones::CORATHUS) {
sender->SendEvolveXPTransferWindow();
}
}
void Doors::Open(Mob *sender, bool alt_mode)
+2
View File
@@ -77,6 +77,8 @@ public:
bool IsDoorBlacklisted();
const char* GetDoorZone() const { return m_zone_name; }
private:
bool GetIsDoorBlacklisted();
-12
View File
@@ -5653,16 +5653,6 @@ int Perl__GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version)
return zone_store.GetZoneNPCMaximumAggroDistance(zone_id, version);
}
uint32 Perl__GetZoneMaximumMovementUpdateRange(uint32 zone_id)
{
return zone_store.GetZoneMaximumMovementUpdateRange(zone_id);
}
uint32 Perl__GetZoneMaximumMovementUpdateRange(uint32 zone_id, int version)
{
return zone_store.GetZoneMaximumMovementUpdateRange(zone_id, version);
}
int8 Perl__GetZoneMinimumExpansion(uint32 zone_id)
{
return zone_store.GetZoneMinimumExpansion(zone_id);
@@ -6112,8 +6102,6 @@ void perl_register_quest()
package.add("GetZoneMaximumExpansion", (int8(*)(uint32, int))&Perl__GetZoneMaximumExpansion);
package.add("GetZoneMaximumLevel", (uint8(*)(uint32))&Perl__GetZoneMaximumLevel);
package.add("GetZoneMaximumLevel", (uint8(*)(uint32, int))&Perl__GetZoneMaximumLevel);
package.add("GetZoneMaximumMovementUpdateRange", (uint32(*)(uint32))&Perl__GetZoneMaximumMovementUpdateRange);
package.add("GetZoneMaximumMovementUpdateRange", (uint32(*)(uint32, int))&Perl__GetZoneMaximumMovementUpdateRange);
package.add("GetZoneMaximumPlayers", (int(*)(uint32))&Perl__GetZoneMaximumPlayers);
package.add("GetZoneMaximumPlayers", (int(*)(uint32, int))&Perl__GetZoneMaximumPlayers);
package.add("GetZoneMinimumClip", (float(*)(uint32))&Perl__GetZoneMinimumClip);
+41 -52
View File
@@ -1717,15 +1717,6 @@ void EntityList::QueueClientsByXTarget(Mob *sender, const EQApplicationPacket *a
}
}
/**
* @param sender
* @param app
* @param ignore_sender
* @param distance
* @param skipped_mob
* @param is_ack_required
* @param filter
*/
void EntityList::QueueCloseClients(
Mob *sender,
const EQApplicationPacket *app,
@@ -1742,7 +1733,7 @@ void EntityList::QueueCloseClients(
}
if (distance <= 0) {
distance = 600;
distance = zone->GetClientUpdateRange();
}
float distance_squared = distance * distance;
@@ -2878,6 +2869,8 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob)
);
it->second->m_close_mobs.erase(entity_id);
it->second->m_last_seen_mob_position.erase(entity_id);
++it;
}
@@ -2931,6 +2924,9 @@ void EntityList::RemoveAuraFromMobs(Mob *aura)
// All of the above makes a tremendous impact on the bottom line of cpu cycle performance because we run an order of magnitude
// less checks by focusing our hot path logic down to a very small subset of relevant entities instead of looping an entire
// entity list (zone wide)
BenchTimer g_scan_bench_timer;
void EntityList::ScanCloseMobs(Mob *scanning_mob)
{
if (!scanning_mob) {
@@ -2941,7 +2937,9 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
return;
}
float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance);
g_scan_bench_timer.reset();
float scan_range = RuleI(Range, MobCloseScanDistance);
// Reserve memory in m_close_mobs to avoid frequent re-allocations if not already reserved.
// Assuming mob_list.size() as an upper bound for reservation.
@@ -2957,7 +2955,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
continue;
}
float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition());
float distance = Distance(scanning_mob->GetPosition(), mob->GetPosition());
if (distance <= scan_range || mob->GetAggroRange() >= scan_range) {
// add mob to scanning_mob's close list and vice versa
// check if the mob is already in the close mobs list before inserting
@@ -2969,10 +2967,11 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
}
LogAIScanClose(
"[{}] Scanning close list > list_size [{}] moving [{}]",
"[{}] Scanning close list > list_size [{}] moving [{}] elapsed [{}] us",
scanning_mob->GetCleanName(),
scanning_mob->m_close_mobs.size(),
scanning_mob->IsMoving() ? "true" : "false"
scanning_mob->IsMoving() ? "true" : "false",
g_scan_bench_timer.elapsedMicroseconds()
);
}
@@ -5320,15 +5319,12 @@ void EntityList::SendFindableNPCList(Client *c)
return;
}
auto outapp = new EQApplicationPacket(OP_SendFindableNPCs, sizeof(FindableNPC_Struct));
FindableNPC_Struct *fnpcs = (FindableNPC_Struct *)outapp->pBuffer;
fnpcs->Unknown109 = 0x16;
fnpcs->Unknown110 = 0x06;
fnpcs->Unknown111 = 0x24;
fnpcs->Action = 0;
static EQApplicationPacket p(OP_SendFindableNPCs, sizeof(FindableNPC_Struct));
auto b = (FindableNPC_Struct*) p.pBuffer;
b->Unknown109 = 0x16;
b->Unknown110 = 0x06;
b->Unknown111 = 0x24;
b->Action = 0;
auto it = npc_list.begin();
while (it != npc_list.end()) {
@@ -5336,50 +5332,47 @@ void EntityList::SendFindableNPCList(Client *c)
NPC *n = it->second;
if (n->IsFindable()) {
fnpcs->EntityID = n->GetID();
strn0cpy(fnpcs->Name, n->GetCleanName(), sizeof(fnpcs->Name));
strn0cpy(fnpcs->LastName, n->GetLastName(), sizeof(fnpcs->LastName));
fnpcs->Race = n->GetRace();
fnpcs->Class = n->GetClass();
b->EntityID = n->GetID();
strn0cpy(b->Name, n->GetCleanName(), sizeof(b->Name));
strn0cpy(b->LastName, n->GetLastName(), sizeof(b->LastName));
b->Race = n->GetRace();
b->Class = n->GetClass();
c->QueuePacket(outapp);
c->QueuePacket(&p);
}
}
++it;
}
safe_delete(outapp);
}
void EntityList::UpdateFindableNPCState(NPC *n, bool Remove)
{
if (!n || !n->IsFindable())
if (!n || !n->IsFindable()) {
return;
}
auto outapp = new EQApplicationPacket(OP_SendFindableNPCs, sizeof(FindableNPC_Struct));
static EQApplicationPacket p(OP_SendFindableNPCs, sizeof(FindableNPC_Struct));
auto b = (FindableNPC_Struct *) p.pBuffer;
b->Unknown109 = 0x16;
b->Unknown110 = 0x06;
b->Unknown111 = 0x24;
FindableNPC_Struct *fnpcs = (FindableNPC_Struct *)outapp->pBuffer;
fnpcs->Unknown109 = 0x16;
fnpcs->Unknown110 = 0x06;
fnpcs->Unknown111 = 0x24;
fnpcs->Action = Remove ? 1: 0;
fnpcs->EntityID = n->GetID();
strn0cpy(fnpcs->Name, n->GetCleanName(), sizeof(fnpcs->Name));
strn0cpy(fnpcs->LastName, n->GetLastName(), sizeof(fnpcs->LastName));
fnpcs->Race = n->GetRace();
fnpcs->Class = n->GetClass();
b->Action = Remove ? 1 : 0;
b->EntityID = n->GetID();
strn0cpy(b->Name, n->GetCleanName(), sizeof(b->Name));
strn0cpy(b->LastName, n->GetLastName(), sizeof(b->LastName));
b->Race = n->GetRace();
b->Class = n->GetClass();
auto it = client_list.begin();
while (it != client_list.end()) {
Client *c = it->second;
if (c && (c->ClientVersion() >= EQ::versions::ClientVersion::SoD))
c->QueuePacket(outapp);
if (c && (c->ClientVersion() >= EQ::versions::ClientVersion::SoD)) {
c->QueuePacket(&p);
}
++it;
}
safe_delete(outapp);
}
void EntityList::HideCorpses(Client *c, uint8 CurrentMode, uint8 NewMode)
@@ -5759,10 +5752,6 @@ void EntityList::ReloadMerchants() {
* then we return the full list
*
* See comments @EntityList::ScanCloseMobs for system explanation
*
* @param mob
* @param distance
* @return
*/
std::unordered_map<uint16, Mob *> &EntityList::GetCloseMobList(Mob *mob, float distance)
{
-2
View File
@@ -631,8 +631,6 @@ private:
bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate
void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff
void ScanCloseClientMobs(std::unordered_map<uint16, Mob*>& close_mobs, Mob* scanning_mob);
void GetBotList(std::list<Bot*> &b_list);
private:
std::list<Bot*> bot_list;
+6 -5
View File
@@ -497,7 +497,7 @@ void Client::CalculateExp(uint64 in_add_exp, uint64 &add_exp, uint64 &add_aaxp,
add_exp = GetEXP() + add_exp;
}
void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, bool resexp) {
void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, bool resexp, NPC* npc) {
if (!IsEXPEnabled()) {
return;
}
@@ -574,10 +574,10 @@ void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, boo
}
// Now update our character's normal and AA xp
SetEXP(exp_source, exp, aaexp, resexp);
SetEXP(exp_source, exp, aaexp, resexp, npc);
}
void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool isrezzexp) {
void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool isrezzexp, NPC* npc) {
uint64 current_exp = GetEXP();
uint64 current_aa_exp = GetAAXP();
uint64 total_current_exp = current_exp + current_aa_exp;
@@ -677,6 +677,7 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
}
}
}
ProcessEvolvingItem(exp_gained, npc);
} else if(total_add_exp < total_current_exp) { //only loss message if you lose exp, no message if you gained/lost nothing.
uint64 exp_lost = current_exp - set_exp;
float exp_percent = (float)((float)exp_lost / (float)(GetEXPForLevel(GetLevel() + 1) - GetEXPForLevel(GetLevel())))*(float)100;
@@ -1205,7 +1206,7 @@ void Group::SplitExp(ExpSource exp_source, const uint64 exp, Mob* other) {
if (diff >= max_diff) {
const uint64 tmp = (m->GetLevel() + 3) * (m->GetLevel() + 3) * 75 * 35 / 10;
const uint64 tmp2 = group_experience / member_count;
m->CastToClient()->AddEXP(exp_source, tmp < tmp2 ? tmp : tmp2, consider_level);
m->CastToClient()->AddEXP(exp_source, tmp < tmp2 ? tmp : tmp2, consider_level, false, other->CastToNPC());
}
}
}
@@ -1256,7 +1257,7 @@ void Raid::SplitExp(ExpSource exp_source, const uint64 exp, Mob* other) {
if (diff >= max_diff) {
const uint64 tmp = (m.member->GetLevel() + 3) * (m.member->GetLevel() + 3) * 75 * 35 / 10;
const uint64 tmp2 = (raid_experience / member_modifier) + 1;
m.member->AddEXP(exp_source, tmp < tmp2 ? tmp : tmp2, consider_level);
m.member->AddEXP(exp_source, tmp < tmp2 ? tmp : tmp2, consider_level, false, other->CastToNPC());
}
}
}
+301
View File
@@ -0,0 +1,301 @@
#include "../client.h"
#include "../command.h"
#include "../../common/evolving_items.h"
void command_evolvingitems(Client *c, const Seperator *sep)
{
const uint16 arguments = sep->argnum;
if (!arguments) {
SendEvolvingItemsSubCommands(c);
return;
}
Client* t = c;
if (c->GetTarget() && c->GetTarget()->IsClient()) {
t = c->GetTarget()->CastToClient();
}
const bool is_item = !strcasecmp(sep->arg[1], "item");
const bool is_target = !strcasecmp(sep->arg[1], "target");
if (!is_item && !is_target) {
SendEvolvingItemsSubCommands(c);
return;
}
if (is_target) {
if (arguments > 1) {
c->Message(Chat::White, "Usage: #evolve target");
} else {
c->Message(Chat::Red, "Worn Items");
for (auto const &[key, value]: t->GetInv().GetWorn()) {
if (!value->IsEvolving()) {
continue;
}
auto item = evolving_items_manager.GetEvolvingItemsCache().at(value->GetID());
c->Message(
Chat::Yellow,
fmt::format(
"Evolving Items | {:0d}",
value->GetID()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Unique ID | {:0d}",
value->GetEvolveUniqueID()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Final Item ID | {:0d}",
value->GetEvolveFinalItemID()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Activated | {}",
value->GetEvolveActivated() ? "Yes" : "No"
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Equipped | {}",
value->GetEvolveEquipped() ? "Yes" : "No"
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Current Amount | {:0d}",
value->GetEvolveCurrentAmount()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Required Amount | {:0d}",
item.required_amount
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Progression (%) | {:.2f}",
value->GetEvolveProgression()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Type | {}",
item.type
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Subtype | {}",
item.sub_type
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Timer | {}",
value->GetTimers().at("evolve").Enabled()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Timer Remaining | {}",
value->GetTimers().at("evolve").GetRemainingTime()
).c_str()
);
}
c->Message(Chat::Red, "Personal Items");
for (auto const &[key, value]: t->GetInv().GetPersonal()) {
if (!value->IsEvolving()) {
continue;
}
auto item = evolving_items_manager.GetEvolvingItemsCache().at(value->GetID());
c->Message(
Chat::Yellow,
fmt::format(
"Evolving Items | {:0d}",
value->GetID()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Unique ID | {:0d}",
value->GetEvolveUniqueID()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Final Item ID | {:0d}",
value->GetEvolveFinalItemID()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Activated | {}",
value->GetEvolveActivated() ? "Yes" : "No"
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Equipped | {}",
value->GetEvolveEquipped() ? "Yes" : "No"
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Current Amount | {:0d}",
value->GetEvolveCurrentAmount()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Required Amount | {:0d}",
item.required_amount
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Progression (%) | {:.2f}",
value->GetEvolveProgression()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Type | {}",
item.type
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Subtype | {}",
item.sub_type
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Timer | {}",
value->GetTimers().at("evolve").Enabled()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Timer Remaining | {}",
value->GetTimers().at("evolve").GetRemainingTime()
).c_str()
);
}
}
} else if (is_item) {
if (arguments > 2) {
c->Message(Chat::White, "Usage: #evolve item item_id");
} else if (sep->IsNumber(2)) {
auto item_id = Strings::ToUnsignedInt(sep->arg[2]);
auto item = c->GetInv().GetItem(c->GetInv().HasItem(item_id));
if (item) {
c->Message(
Chat::Yellow,
fmt::format(
"Evolving Items | {:0d}",
item->GetID()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Slot | {:0d}",
c->GetInv().HasItem(item_id)
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Activated | {}",
item->GetEvolveActivated() ? "Yes" : "No"
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Equipped | {}",
item->GetEvolveEquipped() ? "Yes" : "No"
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Unique ID | {:0d}",
item->GetEvolveUniqueID()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Progression (%) | {:.2f}",
item->GetEvolveProgression()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Timer | {}",
item->GetTimers().at("evolve").Enabled()
).c_str()
);
c->Message(
Chat::Yellow,
fmt::format(
"Timer Remaining | {}",
item->GetTimers().at("evolve").GetRemainingTime()
).c_str()
);
} else {
c->Message(
Chat::Red,
fmt::format(
"Item {} could not be found in your inventory.",
item_id
).c_str()
);
}
} else {
SendEvolvingItemsSubCommands(c);
}
}
}
void SendEvolvingItemsSubCommands(Client *c)
{
c->Message(Chat::White, "#evolve item item_id (Shows evolve values within the iteminstance)");
c->Message(
Chat::White,
"#evolve target (Shows evolve values within the target's cache - Must have a player target selected.)"
);
}
+13
View File
@@ -9,6 +9,19 @@ void command_loc(Client *c, const Seperator *sep)
auto target_position = target->GetPosition();
// check los benchmark
BenchTimer timer;
for (int i = 0; i < 1000; i++) {
zone->zonemap->CheckLoS(c->GetPosition(), target_position);
}
c->Message(
Chat::White,
fmt::format(
"CheckLoS benchmark took [{}]",
timer.elapsed()
).c_str()
);
c->Message(
Chat::White,
fmt::format(
+2
View File
@@ -132,4 +132,6 @@ void command_summonitem(Client *c, const Seperator *sep)
item_link
).c_str()
);
safe_delete(new_item);
}
+137
View File
@@ -0,0 +1,137 @@
#include "../client.h"
void command_zone_shard(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
if (!arguments || !sep->IsNumber(1)) {
if (!RuleB(Zone, ZoneShardQuestMenuOnly)) {
c->ShowZoneShardMenu();
}
return;
}
if (c->GetAggroCount() > 0) {
c->Message(Chat::White, "You cannot request a shard change while in combat.");
return;
}
std::string zone_input = sep->arg[1];
uint32 zone_id = 0;
// if input is id
if (Strings::IsNumber(zone_input)) {
zone_id = Strings::ToInt(zone_input);
// validate
if (zone_id != 0 && !GetZone(zone_id)) {
c->Message(Chat::White, fmt::format("Could not find zone by id [{}]", zone_id).c_str());
return;
}
}
else {
// validate
if (!zone_store.GetZone(zone_input)) {
c->Message(Chat::White, fmt::format("Could not find zone by short_name [{}]", zone_input).c_str());
return;
}
// validate we got id
zone_id = ZoneID(zone_input);
if (zone_id == 0) {
c->Message(Chat::White, fmt::format("Could not find zone id by short_name [{}]", zone_input).c_str());
return;
}
}
auto z = GetZone(zone_id);
if (z && z->shard_at_player_count == 0) {
c->Message(Chat::White, "Zone does not have sharding enabled.");
return;
}
auto instance_id = sep->arg[2] ? Strings::ToBigInt(sep->arg[2]) : 0;
if (instance_id < -1) {
c->Message(Chat::White, "You must enter a valid Instance ID.");
return;
}
if (zone_id == c->GetZoneID() && c->GetInstanceID() == instance_id) {
c->Message(Chat::White, "You are already in this shard.");
return;
}
if (zone_id != c->GetZoneID()) {
c->Message(Chat::White, "You must request a shard change from the zone you are currently in.");
return;
}
auto results = CharacterDataRepository::GetInstanceZonePlayerCounts(database, c->GetZoneID());
if (results.size() <= 1) {
c->Message(Chat::White, "No shards found.");
return;
}
if (instance_id > 0) {
if (!database.CheckInstanceExists(instance_id)) {
c->Message(
Chat::White,
fmt::format(
"Instance ID {} does not exist.",
instance_id
).c_str()
);
return;
}
auto instance_zone_id = database.GetInstanceZoneID(instance_id);
if (!instance_zone_id) {
c->Message(
Chat::White,
fmt::format(
"Instance ID {} not found or zone is set to null.",
instance_id
).c_str()
);
return;
}
if (instance_zone_id != zone_id) {
c->Message(
Chat::White,
fmt::format(
"Instance Zone ID {} does not match zone ID {}.",
instance_id,
zone_id
).c_str()
);
return;
}
if (!database.CheckInstanceByCharID(instance_id, c->CharacterID())) {
database.AddClientToInstance(instance_id, c->CharacterID());
}
if (!database.VerifyInstanceAlive(instance_id, c->CharacterID())) {
c->Message(
Chat::White,
fmt::format(
"Instance ID {} expired.",
instance_id
).c_str()
);
return;
}
}
c->MovePC(
zone_id,
instance_id,
c->GetX(),
c->GetY(),
c->GetZ(),
c->GetHeading(),
0,
ZoneSolicited
);
}
+76 -93
View File
@@ -25,6 +25,7 @@
#include "zonedb.h"
#include "../common/events/player_event_logs.h"
#include "bot.h"
#include "../common/evolving_items.h"
#include "../common/repositories/character_corpse_items_repository.h"
extern WorldServer worldserver;
@@ -1091,7 +1092,11 @@ void Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_up
}
// end QS code
bool isDeleted = m_inv.DeleteItem(slot_id, quantity);
uint64 evolve_id = m_inv[slot_id]->GetEvolveUniqueID();
bool isDeleted = m_inv.DeleteItem(slot_id, quantity);
if (isDeleted && evolve_id && (slot_id > EQ::invslot::TRADE_END || slot_id < EQ::invslot::TRADE_BEGIN)) {
CharacterEvolvingItemsRepository::SoftDelete(database, evolve_id);
}
const EQ::ItemInstance* inst = nullptr;
if (slot_id == EQ::invslot::slotCursor) {
@@ -1143,6 +1148,8 @@ void Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_up
bool Client::PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update)
{
LogInventory("Putting item [{}] ([{}]) on the cursor", inst.GetItem()->Name, inst.GetItem()->ID);
evolving_items_manager.DoLootChecks(CharacterID(), EQ::invslot::slotCursor, inst);
m_inv.PushCursor(inst);
if (client_update) {
@@ -1163,9 +1170,9 @@ bool Client::PutItemInInventory(int16 slot_id, const EQ::ItemInstance& inst, boo
if (slot_id == EQ::invslot::slotCursor) { // don't trust macros before conditional statements...
return PushItemOnCursor(inst, client_update);
}
else {
m_inv.PutItem(slot_id, inst);
}
evolving_items_manager.DoLootChecks(CharacterID(), slot_id, inst);
m_inv.PutItem(slot_id, inst);
if (client_update)
{
@@ -1173,15 +1180,16 @@ bool Client::PutItemInInventory(int16 slot_id, const EQ::ItemInstance& inst, boo
//SendWearChange(EQ::InventoryProfile::CalcMaterialFromSlot(slot_id));
}
CalcBonuses();
if (slot_id == EQ::invslot::slotCursor) {
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
return database.SaveCursor(CharacterID(), s, e);
}
else {
return database.SaveInventory(CharacterID(), &inst, slot_id);
}
CalcBonuses();
return database.SaveInventory(CharacterID(), &inst, slot_id);
//CalcBonuses(); // this never fires??
// a lot of wasted checks and calls coded above...
}
@@ -1191,6 +1199,8 @@ void Client::PutLootInInventory(int16 slot_id, const EQ::ItemInstance &inst, Loo
bool cursor_empty = m_inv.CursorEmpty();
evolving_items_manager.DoLootChecks(CharacterID(), slot_id, inst);
if (slot_id == EQ::invslot::slotCursor) {
m_inv.PushCursor(inst);
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
@@ -2969,107 +2979,72 @@ void Client::RemoveNoRent(bool client_update)
}
// Two new methods to alleviate perpetual login desyncs
void Client::RemoveDuplicateLore(bool client_update)
void Client::RemoveDuplicateLore()
{
for (auto slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) {
if ((((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask) == 0)
for (auto slot_id : GetInventorySlots()) {
if ((((uint64) 1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask) == 0) {
continue;
auto inst = m_inv.PopItem(slot_id);
if (inst == nullptr) { continue; }
if(CheckLoreConflict(inst->GetItem())) {
LogInventory("Lore Duplication Error: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id);
database.SaveInventory(character_id, nullptr, slot_id);
}
else {
m_inv.PutItem(slot_id, *inst);
}
safe_delete(inst);
}
for (auto slot_id = EQ::invslot::GENERAL_BEGIN; slot_id <= EQ::invslot::GENERAL_END; ++slot_id) {
if ((((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask) == 0)
// ignore shared bank slots
if (slot_id >= EQ::invslot::SHARED_BANK_BEGIN && slot_id <= EQ::invslot::SHARED_BANK_END) {
continue;
}
if (slot_id >= EQ::invbag::SHARED_BANK_BAGS_BEGIN && slot_id <= EQ::invbag::SHARED_BANK_BAGS_END) {
continue;
}
// slot gets handled in a queue
if (slot_id == EQ::invslot::slotCursor) {
continue;
}
// temporarily move the item off of the slot
auto inst = m_inv.PopItem(slot_id);
if (inst == nullptr) { continue; }
if (!inst) {
continue;
}
if (CheckLoreConflict(inst->GetItem())) {
LogInventory("Lore Duplication Error: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id);
LogError(
"Lore Duplication Error | Deleting [{}] ({}) from slot [{}] client [{}]",
inst->GetItem()->Name,
inst->GetItem()->ID,
slot_id,
GetCleanName()
);
database.SaveInventory(character_id, nullptr, slot_id);
safe_delete(inst);
}
else {
m_inv.PutItem(slot_id, *inst);
}
safe_delete(inst);
// if no lore conflict, put the item back in the slot
m_inv.PushItem(slot_id, inst);
}
for (auto slot_id = EQ::invbag::GENERAL_BAGS_BEGIN; slot_id <= EQ::invbag::CURSOR_BAG_END; ++slot_id) {
auto temp_slot = EQ::invslot::GENERAL_BEGIN + ((slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT);
if ((((uint64)1 << temp_slot) & GetInv().GetLookup()->PossessionsBitmask) == 0)
continue;
auto inst = m_inv.PopItem(slot_id);
if (inst == nullptr) { continue; }
if(CheckLoreConflict(inst->GetItem())) {
LogInventory("Lore Duplication Error: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id);
database.SaveInventory(character_id, nullptr, slot_id);
}
else {
m_inv.PutItem(slot_id, *inst);
}
safe_delete(inst);
}
for (auto slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; ++slot_id) {
if ((slot_id - EQ::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize.Bank)
continue;
auto inst = m_inv.PopItem(slot_id);
if (inst == nullptr) { continue; }
if(CheckLoreConflict(inst->GetItem())) {
LogInventory("Lore Duplication Error: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id);
database.SaveInventory(character_id, nullptr, slot_id);
}
else {
m_inv.PutItem(slot_id, *inst);
}
safe_delete(inst);
}
for (auto slot_id = EQ::invbag::BANK_BAGS_BEGIN; slot_id <= EQ::invbag::BANK_BAGS_END; ++slot_id) {
auto temp_slot = (slot_id - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT;
if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize.Bank)
continue;
auto inst = m_inv.PopItem(slot_id);
if (inst == nullptr) { continue; }
if(CheckLoreConflict(inst->GetItem())) {
LogInventory("Lore Duplication Error: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id);
database.SaveInventory(character_id, nullptr, slot_id);
}
else {
m_inv.PutItem(slot_id, *inst);
}
safe_delete(inst);
}
// Shared Bank and Shared Bank Containers are not checked due to their allowing duplicate lore items
if (!m_inv.CursorEmpty()) {
std::list<EQ::ItemInstance*> local_1;
std::list<EQ::ItemInstance*> local_2;
while (!m_inv.CursorEmpty()) {
auto inst = m_inv.PopItem(EQ::invslot::slotCursor);
if (inst == nullptr) { continue; }
if (!inst) {
continue;
}
local_1.push_back(inst);
}
for (auto iter = local_1.begin(); iter != local_1.end(); ++iter) {
auto inst = *iter;
if (inst == nullptr) { continue; }
for (auto inst: local_1) {
if (!inst) {
continue;
}
if (CheckLoreConflict(inst->GetItem())) {
LogInventory("Lore Duplication Error: Deleting [{}] from `Limbo`", inst->GetItem()->Name);
LogError(
"Lore Duplication Error | Deleting [{}] ({}) from `Limbo` client [{}]",
inst->GetItem()->Name,
inst->GetItem()->ID,
GetCleanName()
);
safe_delete(inst);
}
else {
@@ -3078,17 +3053,25 @@ void Client::RemoveDuplicateLore(bool client_update)
}
local_1.clear();
for (auto iter = local_2.begin(); iter != local_2.end(); ++iter) {
auto inst = *iter;
if (inst == nullptr) { continue; }
for (auto inst: local_2) {
if (!inst) {
continue;
}
if (!inst->GetItem()->LoreFlag ||
((inst->GetItem()->LoreGroup == -1) && (m_inv.HasItem(inst->GetID(), 0, invWhereCursor) == INVALID_INDEX)) ||
(inst->GetItem()->LoreGroup && (~inst->GetItem()->LoreGroup) && (m_inv.HasItemByLoreGroup(inst->GetItem()->LoreGroup, invWhereCursor) == INVALID_INDEX))
((inst->GetItem()->LoreGroup == -1) &&
(m_inv.HasItem(inst->GetID(), 0, invWhereCursor) == INVALID_INDEX)) ||
(inst->GetItem()->LoreGroup && (~inst->GetItem()->LoreGroup) &&
(m_inv.HasItemByLoreGroup(inst->GetItem()->LoreGroup, invWhereCursor) == INVALID_INDEX))
) {
m_inv.PushCursor(*inst);
}
else {
LogInventory("Lore Duplication Error: Deleting [{}] from `Limbo`", inst->GetItem()->Name);
LogError(
"Lore Duplication Error | Deleting [{}] ({}) from `Limbo` client [{}]",
inst->GetItem()->Name,
inst->GetItem()->ID,
GetCleanName()
);
}
safe_delete(inst);
}
+5 -1
View File
@@ -687,6 +687,7 @@ void NPC::RemoveItem(uint32 item_id, uint16 quantity, uint16 slot)
LootItem *item = *cur;
if (item->item_id == item_id && slot <= 0 && quantity <= 0) {
m_loot_items.erase(cur);
safe_delete(item);
UpdateEquipmentLight();
if (UpdateActiveLight()) { SendAppearancePacket(AppearanceType::Light, GetActiveLightType()); }
return;
@@ -695,7 +696,10 @@ void NPC::RemoveItem(uint32 item_id, uint16 quantity, uint16 slot)
if (item->charges <= quantity) {
m_loot_items.erase(cur);
UpdateEquipmentLight();
if (UpdateActiveLight()) { SendAppearancePacket(AppearanceType::Light, GetActiveLightType()); }
if (UpdateActiveLight()) {
SendAppearancePacket(AppearanceType::Light, GetActiveLightType());
}
safe_delete(item);
}
else {
item->charges -= quantity;
+68 -4
View File
@@ -3436,13 +3436,14 @@ void Lua_Client::AreaTaunt(float range, int bonus_hate)
entity_list.AETaunt(self, range, bonus_hate);
}
luabind::object Lua_Client::GetInventorySlots(lua_State* L) {
luabind::object Lua_Client::GetInventorySlots(lua_State* L)
{
auto lua_table = luabind::newtable(L);
if (d_) {
auto self = reinterpret_cast<NativeType*>(d_);
int index = 1;
for (const int16& slot_id : self->GetInventorySlots()) {
auto self = reinterpret_cast<NativeType *>(d_);
int index = 1;
for (const int16 &slot_id: self->GetInventorySlots()) {
lua_table[index] = slot_id;
index++;
}
@@ -3451,6 +3452,60 @@ luabind::object Lua_Client::GetInventorySlots(lua_State* L) {
return lua_table;
}
void Lua_Client::ShowZoneShardMenu()
{
Lua_Safe_Call_Void();
self->ShowZoneShardMenu();
}
void Lua_Client::GrantPetNameChange()
{
Lua_Safe_Call_Void();
self->GrantPetNameChange();
}
void Lua_Client::SetAAEXPPercentage(uint8 percentage)
{
Lua_Safe_Call_Void();
self->SetAAEXPPercentage(percentage);
}
void Lua_Client::SetAccountBucket(std::string bucket_name, std::string bucket_value)
{
Lua_Safe_Call_Void();
self->SetAccountBucket(bucket_name, bucket_value);
}
void Lua_Client::SetAccountBucket(std::string bucket_name, std::string bucket_value, std::string expiration)
{
Lua_Safe_Call_Void();
self->SetAccountBucket(bucket_name, bucket_value, expiration);
}
void Lua_Client::DeleteAccountBucket(std::string bucket_name)
{
Lua_Safe_Call_Void();
self->DeleteAccountBucket(bucket_name);
}
std::string Lua_Client::GetAccountBucket(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetAccountBucket(bucket_name);
}
std::string Lua_Client::GetAccountBucketExpires(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetAccountBucketExpires(bucket_name);
}
std::string Lua_Client::GetAccountBucketRemaining(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetAccountBucketRemaining(bucket_name);
}
luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client")
.def(luabind::constructor<>())
@@ -3519,6 +3574,7 @@ luabind::scope lua_register_client() {
.def("CanHaveSkill", (bool(Lua_Client::*)(int))&Lua_Client::CanHaveSkill)
.def("CashReward", &Lua_Client::CashReward)
.def("ChangeLastName", (void(Lua_Client::*)(std::string))&Lua_Client::ChangeLastName)
.def("GrantPetNameChange", &Lua_Client::GrantPetNameChange)
.def("CharacterID", (uint32(Lua_Client::*)(void))&Lua_Client::CharacterID)
.def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob))&Lua_Client::CheckIncreaseSkill)
.def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob,int))&Lua_Client::CheckIncreaseSkill)
@@ -3539,6 +3595,7 @@ luabind::scope lua_register_client() {
.def("CreateTaskDynamicZone", &Lua_Client::CreateTaskDynamicZone)
.def("DecreaseByID", (bool(Lua_Client::*)(uint32,int))&Lua_Client::DecreaseByID)
.def("DescribeSpecialAbilities", (void(Lua_Client::*)(Lua_NPC))&Lua_Client::DescribeSpecialAbilities)
.def("DeleteAccountBucket", (void(Lua_Client::*)(std::string))&Lua_Client::DeleteAccountBucket)
.def("DeleteBucket", (void(Lua_Client::*)(std::string))&Lua_Client::DeleteBucket)
.def("DeleteItemInInventory", (void(Lua_Client::*)(int,int))&Lua_Client::DeleteItemInInventory)
.def("DeleteItemInInventory", (void(Lua_Client::*)(int,int,bool))&Lua_Client::DeleteItemInInventory)
@@ -3617,6 +3674,9 @@ luabind::scope lua_register_client() {
.def("GetBotRequiredLevel", (int(Lua_Client::*)(uint8))&Lua_Client::GetBotRequiredLevel)
.def("GetBotSpawnLimit", (int(Lua_Client::*)(void))&Lua_Client::GetBotSpawnLimit)
.def("GetBotSpawnLimit", (int(Lua_Client::*)(uint8))&Lua_Client::GetBotSpawnLimit)
.def("GetAccountBucket", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetAccountBucket)
.def("GetAccountBucketExpires", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetAccountBucketExpires)
.def("GetAccountBucketRemaining", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetAccountBucketRemaining)
.def("GetBucket", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucket)
.def("GetBucketExpires", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucketExpires)
.def("GetBucketRemaining", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucketRemaining)
@@ -3883,6 +3943,7 @@ luabind::scope lua_register_client() {
.def("SetAAEXPModifier", (void(Lua_Client::*)(float))&Lua_Client::SetAAEXPModifier)
.def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,float))&Lua_Client::SetAAEXPModifier)
.def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,float,int16))&Lua_Client::SetAAEXPModifier)
.def("SetAAEXPPercentage", (void(Lua_Client::*)(uint8))&Lua_Client::SetAAEXPPercentage)
.def("SetAAPoints", (void(Lua_Client::*)(int))&Lua_Client::SetAAPoints)
.def("SetAATitle", (void(Lua_Client::*)(std::string))&Lua_Client::SetAATitle)
.def("SetAATitle", (void(Lua_Client::*)(std::string,bool))&Lua_Client::SetAATitle)
@@ -3908,6 +3969,8 @@ luabind::scope lua_register_client() {
.def("SetBotRequiredLevel", (void(Lua_Client::*)(int,uint8))&Lua_Client::SetBotRequiredLevel)
.def("SetBotSpawnLimit", (void(Lua_Client::*)(int))&Lua_Client::SetBotSpawnLimit)
.def("SetBotSpawnLimit", (void(Lua_Client::*)(int,uint8))&Lua_Client::SetBotSpawnLimit)
.def("SetAccountBucket", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetAccountBucket)
.def("SetAccountBucket", (void(Lua_Client::*)(std::string,std::string,std::string))&Lua_Client::SetAccountBucket)
.def("SetBucket", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetBucket)
.def("SetBucket", (void(Lua_Client::*)(std::string,std::string,std::string))&Lua_Client::SetBucket)
.def("SetClientMaxLevel", (void(Lua_Client::*)(int))&Lua_Client::SetClientMaxLevel)
@@ -3970,6 +4033,7 @@ luabind::scope lua_register_client() {
.def("SetTint", (void(Lua_Client::*)(int,uint32))&Lua_Client::SetTint)
.def("SetTitleSuffix", (void(Lua_Client::*)(const char *))&Lua_Client::SetTitleSuffix)
.def("SetZoneFlag", (void(Lua_Client::*)(uint32))&Lua_Client::SetZoneFlag)
.def("ShowZoneShardMenu", (void(Lua_Client::*)(void))&Lua_Client::ShowZoneShardMenu)
.def("Signal", (void(Lua_Client::*)(int))&Lua_Client::Signal)
.def("Sit", (void(Lua_Client::*)(void))&Lua_Client::Sit)
.def("Stand", (void(Lua_Client::*)(void))&Lua_Client::Stand)
+11
View File
@@ -510,6 +510,15 @@ public:
void AreaTaunt(float range);
void AreaTaunt(float range, int bonus_hate);
luabind::object GetInventorySlots(lua_State* L);
void SetAAEXPPercentage(uint8 percentage);
// account data buckets
void SetAccountBucket(std::string bucket_name, std::string bucket_value);
void SetAccountBucket(std::string bucket_name, std::string bucket_value, std::string expiration = "");
void DeleteAccountBucket(std::string bucket_name);
std::string GetAccountBucket(std::string bucket_name);
std::string GetAccountBucketExpires(std::string bucket_name);
std::string GetAccountBucketRemaining(std::string bucket_name);
void ApplySpell(int spell_id);
void ApplySpell(int spell_id, int duration);
@@ -587,6 +596,8 @@ public:
void DialogueWindow(std::string markdown);
bool ReloadDataBuckets();
void ShowZoneShardMenu();
void GrantPetNameChange();
Lua_Expedition CreateExpedition(luabind::object expedition_info);
Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players);
-12
View File
@@ -4668,16 +4668,6 @@ int lua_get_zone_npc_maximum_aggro_distance(uint32 zone_id, int version)
return zone_store.GetZoneNPCMaximumAggroDistance(zone_id, version);
}
uint32 lua_get_zone_maximum_movement_update_range(uint32 zone_id)
{
return zone_store.GetZoneMaximumMovementUpdateRange(zone_id);
}
uint32 lua_get_zone_maximum_movement_update_range(uint32 zone_id, int version)
{
return zone_store.GetZoneMaximumMovementUpdateRange(zone_id, version);
}
int8 lua_get_zone_minimum_expansion(uint32 zone_id)
{
return zone_store.GetZoneMinimumExpansion(zone_id);
@@ -6287,8 +6277,6 @@ luabind::scope lua_register_general() {
luabind::def("get_zone_fast_regen_endurance", (int(*)(uint32,int))&lua_get_zone_fast_regen_endurance),
luabind::def("get_zone_npc_maximum_aggro_distance", (int(*)(uint32))&lua_get_zone_npc_maximum_aggro_distance),
luabind::def("get_zone_npc_maximum_aggro_distance", (int(*)(uint32,int))&lua_get_zone_npc_maximum_aggro_distance),
luabind::def("get_zone_npc_maximum_movement_update_range", (uint32(*)(uint32))&lua_get_zone_maximum_movement_update_range),
luabind::def("get_zone_npc_maximum_movement_update_range", (uint32(*)(uint32,int))&lua_get_zone_maximum_movement_update_range),
luabind::def("get_zone_minimum_expansion", (int8(*)(uint32))&lua_get_zone_minimum_expansion),
luabind::def("get_zone_minimum_expansion", (int8(*)(uint32,int))&lua_get_zone_minimum_expansion),
luabind::def("get_zone_maximum_expansion", (int8(*)(uint32))&lua_get_zone_maximum_expansion),
+105 -6
View File
@@ -255,11 +255,6 @@ int8 Lua_ItemInst::GetMaxEvolveLvl() {
return self->GetMaxEvolveLvl();
}
uint32 Lua_ItemInst::GetKillsNeeded(uint8 current_level) {
Lua_Safe_Call_Int();
return self->GetKillsNeeded(current_level);
}
Lua_ItemInst Lua_ItemInst::Clone() {
Lua_Safe_Call_Class(Lua_ItemInst);
return Lua_ItemInst(self->Clone(), true);
@@ -348,6 +343,96 @@ std::string Lua_ItemInst::GetItemLink()
return linker.GenerateLink();
}
void Lua_ItemInst::AddEvolveAmount(uint64 amount)
{
Lua_Safe_Call_Void();
self->SetEvolveAddToCurrentAmount(amount);
}
uint32 Lua_ItemInst::GetAugmentEvolveUniqueID(uint8 slot_id)
{
Lua_Safe_Call_Int();
return self->GetAugmentEvolveUniqueID(slot_id);
}
bool Lua_ItemInst::GetEvolveActivated()
{
Lua_Safe_Call_Bool();
return self->GetEvolveActivated();
}
uint64 Lua_ItemInst::GetEvolveAmount()
{
Lua_Safe_Call_Int();
return self->GetEvolveCurrentAmount();
}
uint32 Lua_ItemInst::GetEvolveCharacterID()
{
Lua_Safe_Call_Int();
return self->GetEvolveCharID();
}
bool Lua_ItemInst::GetEvolveEquipped()
{
Lua_Safe_Call_Bool();
return self->GetEvolveEquipped();
}
uint32 Lua_ItemInst::GetEvolveFinalItemID()
{
Lua_Safe_Call_Int();
return self->GetEvolveFinalItemID();
}
uint32 Lua_ItemInst::GetEvolveItemID()
{
Lua_Safe_Call_Int();
return self->GetEvolveItemID();
}
int8 Lua_ItemInst::GetEvolveLevel()
{
Lua_Safe_Call_Int();
return self->GetEvolveLvl();
}
uint32 Lua_ItemInst::GetEvolveLoreID()
{
Lua_Safe_Call_Int();
return self->GetEvolveLoreID();
}
float Lua_ItemInst::GetEvolveProgression()
{
Lua_Safe_Call_Real();
return self->GetEvolveProgression();
}
uint64 Lua_ItemInst::GetEvolveUniqueID()
{
Lua_Safe_Call_Int();
return self->GetEvolveUniqueID();
}
bool Lua_ItemInst::IsEvolving()
{
Lua_Safe_Call_Bool();
return self->IsEvolving();
}
void Lua_ItemInst::SetEvolveAmount(uint64 amount)
{
Lua_Safe_Call_Void();
self->SetEvolveCurrentAmount(amount);
}
void Lua_ItemInst::SetEvolveProgression(float amount)
{
Lua_Safe_Call_Void();
self->SetEvolveProgression(amount);
}
luabind::scope lua_register_iteminst() {
return luabind::class_<Lua_ItemInst>("ItemInst")
.def(luabind::constructor<>())
@@ -356,12 +441,14 @@ luabind::scope lua_register_iteminst() {
.property("null", &Lua_ItemInst::Null)
.property("valid", &Lua_ItemInst::Valid)
.def("AddExp", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::AddExp)
.def("AddEvolveAmount", (void(Lua_ItemInst::*)(uint64))&Lua_ItemInst::AddEvolveAmount)
.def("ClearTimers", (void(Lua_ItemInst::*)(void))&Lua_ItemInst::ClearTimers)
.def("Clone", (Lua_ItemInst(Lua_ItemInst::*)(void))&Lua_ItemInst::Clone)
.def("ContainsAugmentByID", (bool(Lua_ItemInst::*)(uint32))&Lua_ItemInst::ContainsAugmentByID)
.def("CountAugmentByID", (int(Lua_ItemInst::*)(uint32))&Lua_ItemInst::CountAugmentByID)
.def("DeleteCustomData", (void(Lua_ItemInst::*)(const std::string &))&Lua_ItemInst::DeleteCustomData)
.def("GetAugment", (Lua_ItemInst(Lua_ItemInst::*)(int))&Lua_ItemInst::GetAugment)
.def("GetAugmentEvolveUniqueID", (uint32(Lua_ItemInst::*)(uint8))&Lua_ItemInst::GetAugmentEvolveUniqueID)
.def("GetAugmentIDs", (luabind::object(Lua_ItemInst::*)(lua_State*))&Lua_ItemInst::GetAugmentIDs)
.def("GetAugmentItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetAugmentItemID)
.def("GetAugmentType", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetAugmentType)
@@ -370,13 +457,22 @@ luabind::scope lua_register_iteminst() {
.def("GetCustomData", (std::string(Lua_ItemInst::*)(const std::string &))&Lua_ItemInst::GetCustomData)
.def("GetCustomDataString", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetCustomDataString)
.def("GetExp", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetExp)
.def("GetEvolveActivated", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveActivated)
.def("GetEvolveAmount", (uint64(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveAmount)
.def("GetEvolveCharacterID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveCharacterID)
.def("GetEvolveEquipped", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveEquipped)
.def("GetEvolveFinalItemID", (uint64(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveFinalItemID)
.def("GetEvolveItemID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveItemID)
.def("GetEvolveLevel", (int8(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveLevel)
.def("GetEvolveLoreID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveLoreID)
.def("GetEvolveProgression", (float(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveProgression)
.def("GetEvolveUniqueID", (uint64(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveUniqueID)
.def("GetID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetID)
.def("GetItem", (Lua_Item(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItem)
.def("GetItem", (Lua_ItemInst(Lua_ItemInst::*)(uint8))&Lua_ItemInst::GetItem)
.def("GetItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetItemID)
.def("GetItemLink", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemLink)
.def("GetItemScriptID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemScriptID)
.def("GetKillsNeeded", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetKillsNeeded)
.def("GetMaxEvolveLvl", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetMaxEvolveLvl)
.def("GetName", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetName)
.def("GetPrice", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetPrice)
@@ -389,6 +485,7 @@ luabind::scope lua_register_iteminst() {
.def("IsAugmented", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsAugmented)
.def("IsEquipable", (bool(Lua_ItemInst::*)(int16))&Lua_ItemInst::IsEquipable)
.def("IsEquipable", (bool(Lua_ItemInst::*)(uint16,uint16))&Lua_ItemInst::IsEquipable)
.def("IsEvolving", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsEvolving)
.def("IsExpendable", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsExpendable)
.def("IsInstNoDrop", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsInstNoDrop)
.def("IsStackable", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsStackable)
@@ -404,6 +501,8 @@ luabind::scope lua_register_iteminst() {
.def("SetCustomData", (void(Lua_ItemInst::*)(const std::string&,float))&Lua_ItemInst::SetCustomData)
.def("SetCustomData", (void(Lua_ItemInst::*)(const std::string&,int))&Lua_ItemInst::SetCustomData)
.def("SetCustomData", (void(Lua_ItemInst::*)(const std::string&,const std::string&))&Lua_ItemInst::SetCustomData)
.def("SetEvolveAmount", (void(Lua_ItemInst::*)(uint64))&Lua_ItemInst::SetEvolveAmount)
.def("SetEvolveProgression", (void(Lua_ItemInst::*)(float))&Lua_ItemInst::SetEvolveProgression)
.def("SetExp", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::SetExp)
.def("SetInstNoDrop", (void(Lua_ItemInst::*)(bool))&Lua_ItemInst::SetInstNoDrop)
.def("SetPrice", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::SetPrice)
+15 -1
View File
@@ -77,7 +77,6 @@ public:
void SetExp(uint32 exp);
void AddExp(uint32 exp);
int8 GetMaxEvolveLvl();
uint32 GetKillsNeeded(uint8 current_level);
Lua_ItemInst Clone();
void SetTimer(std::string name, uint32 time);
void StopTimer(std::string name);
@@ -91,6 +90,21 @@ public:
void ItemSay(const char* text, uint8 language_id);
luabind::object GetAugmentIDs(lua_State* L);
std::string GetItemLink();
void AddEvolveAmount(uint64 amount);
uint32 GetAugmentEvolveUniqueID(uint8 slot_id);
bool GetEvolveActivated();
uint64 GetEvolveAmount();
uint32 GetEvolveCharacterID();
bool GetEvolveEquipped();
uint32 GetEvolveFinalItemID();
uint32 GetEvolveItemID();
int8 GetEvolveLevel();
uint32 GetEvolveLoreID();
float GetEvolveProgression();
uint64 GetEvolveUniqueID();
bool IsEvolving();
void SetEvolveAmount(uint64 amount);
void SetEvolveProgression(float amount);
private:
bool cloned_;
+3 -1
View File
@@ -896,6 +896,7 @@ luabind::scope lua_register_packet_opcodes() {
luabind::value("Weblink", static_cast<int>(OP_Weblink)),
luabind::value("InspectMessageUpdate", static_cast<int>(OP_InspectMessageUpdate)),
luabind::value("ItemPreview", static_cast<int>(OP_ItemPreview)),
luabind::value("ItemPreviewRequest", static_cast<int>(OP_ItemPreviewRequest)),
luabind::value("MercenaryDataRequest", static_cast<int>(OP_MercenaryDataRequest)),
luabind::value("MercenaryDataResponse", static_cast<int>(OP_MercenaryDataResponse)),
luabind::value("MercenaryHire", static_cast<int>(OP_MercenaryHire)),
@@ -915,7 +916,8 @@ luabind::scope lua_register_packet_opcodes() {
luabind::value("Marquee", static_cast<int>(OP_Marquee)),
luabind::value("ClientTimeStamp", static_cast<int>(OP_ClientTimeStamp)),
luabind::value("GuildPromote", static_cast<int>(OP_GuildPromote)),
luabind::value("Fling", static_cast<int>(OP_Fling))
luabind::value("Fling", static_cast<int>(OP_Fling)),
luabind::value("PickZoneWindow", static_cast<int>(OP_PickZoneWindow))
)];
}
+7
View File
@@ -52,6 +52,7 @@
#include "task_manager.h"
#include "quest_parser_collection.h"
#include "embparser.h"
#include "../common/evolving_items.h"
#include "lua_parser.h"
#include "questmgr.h"
#include "npc_scale_manager.h"
@@ -110,6 +111,7 @@ PathManager path;
PlayerEventLogs player_event_logs;
DatabaseUpdate database_update;
SkillCaps skill_caps;
EvolvingItemsManager evolving_items_manager;
const SPDat_Spell_Struct* spells;
int32 SPDAT_RECORDS = -1;
@@ -387,6 +389,11 @@ int main(int argc, char **argv)
title_manager.LoadTitles();
content_db.LoadTributes();
// Load evolving item data
evolving_items_manager.SetDatabase(&database);
evolving_items_manager.SetContentDatabase(&content_db);
evolving_items_manager.LoadEvolvingItems();
database.GetDecayTimes(npcCorpseDecayTimes);
if (!EQ::ProfanityManager::LoadProfanityList(&database)) {
+2
View File
@@ -7,6 +7,7 @@
#include "raycast_mesh.h"
#include "zone.h"
#include "../common/file.h"
#include "../common/memory/ksm.hpp"
#include <algorithm>
#include <map>
@@ -953,6 +954,7 @@ bool Map::LoadV2(FILE *f) {
return true;
}
void Map::RotateVertex(glm::vec3 &v, float rx, float ry, float rz) {
glm::vec3 nv = v;
+26 -34
View File
@@ -125,10 +125,11 @@ Mob::Mob(
tmHidden(-1),
mitigation_ac(0),
m_specialattacks(eSpecialAttacks::None),
attack_anim_timer(500),
attack_anim_timer(100),
position_update_melee_push_timer(500),
hate_list_cleanup_timer(6000),
m_scan_close_mobs_timer(6000),
m_see_close_mobs_timer(1000),
m_mob_check_moving_timer(1000),
bot_attack_flag_timer(10000)
{
@@ -1521,16 +1522,12 @@ void Mob::SendHPUpdate(bool force_update_all)
last_hp
);
auto client_packet = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct));
auto *hp_packet_client = (SpawnHPUpdate_Struct *) client_packet->pBuffer;
hp_packet_client->cur_hp = static_cast<uint32>(CastToClient()->GetHP() - itembonuses.HP);
hp_packet_client->spawn_id = GetID();
hp_packet_client->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP;
CastToClient()->QueuePacket(client_packet);
safe_delete(client_packet);
static EQApplicationPacket p(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct));
auto b = (SpawnHPUpdate_Struct*) p.pBuffer;
b->cur_hp = static_cast<uint32>(CastToClient()->GetHP() - itembonuses.HP);
b->spawn_id = GetID();
b->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP;
CastToClient()->QueuePacket(&p);
ResetHPUpdateTimer();
@@ -3539,24 +3536,21 @@ void Mob::DoAnim(const int animation_id, int animation_speed, bool ackreq, eqFil
return;
}
auto outapp = new EQApplicationPacket(OP_Animation, sizeof(Animation_Struct));
auto *a = (Animation_Struct *) outapp->pBuffer;
static EQApplicationPacket p(OP_Animation, sizeof(Animation_Struct));
auto a = (Animation_Struct*) p.pBuffer;
a->spawnid = GetID();
a->action = animation_id;
a->speed = animation_speed ? animation_speed : 10;
entity_list.QueueCloseClients(
this, /* Sender */
outapp, /* Packet */
&p, /* Packet */
false, /* Ignore Sender */
RuleI(Range, Anims),
0, /* Skip this mob */
ackreq, /* Packet ACK */
filter /* eqFilterType filter */
);
safe_delete(outapp);
}
void Mob::ShowBuffs(Client* c) {
@@ -7716,28 +7710,26 @@ bool Mob::CanRaceEquipItem(uint32 item_id)
void Mob::SendAddPlayerState(PlayerState new_state)
{
auto app = new EQApplicationPacket(OP_PlayerStateAdd, sizeof(PlayerState_Struct));
auto ps = (PlayerState_Struct *)app->pBuffer;
static EQApplicationPacket p(OP_PlayerStateAdd, sizeof(PlayerState_Struct));
auto b = (PlayerState_Struct *) p.pBuffer;
ps->spawn_id = GetID();
ps->state = static_cast<uint32>(new_state);
b->spawn_id = GetID();
b->state = static_cast<uint32>(new_state);
AddPlayerState(ps->state);
entity_list.QueueClients(nullptr, app);
safe_delete(app);
AddPlayerState(b->state);
entity_list.QueueClients(nullptr, &p);
}
void Mob::SendRemovePlayerState(PlayerState old_state)
{
auto app = new EQApplicationPacket(OP_PlayerStateRemove, sizeof(PlayerState_Struct));
auto ps = (PlayerState_Struct *)app->pBuffer;
static EQApplicationPacket p(OP_PlayerStateRemove, sizeof(PlayerState_Struct));
auto b = (PlayerState_Struct *) p.pBuffer;
ps->spawn_id = GetID();
ps->state = static_cast<uint32>(old_state);
b->spawn_id = GetID();
b->state = static_cast<uint32>(old_state);
RemovePlayerState(ps->state);
entity_list.QueueClients(nullptr, app);
safe_delete(app);
RemovePlayerState(b->state);
entity_list.QueueClients(nullptr, &p);
}
int32 Mob::GetMeleeMitigation() {
@@ -8612,15 +8604,15 @@ std::unordered_map<uint16, Mob *> &Mob::GetCloseMobList(float distance)
void Mob::ClearDataBucketCache()
{
if (IsOfClientBot()) {
uint64 id = 0;
uint64 id = 0;
DataBucketLoadType::Type t{};
if (IsBot()) {
id = CastToBot()->GetBotID();
t = DataBucketLoadType::Bot;
t = DataBucketLoadType::Bot;
}
else if (IsClient()) {
id = CastToClient()->CharacterID();
t = DataBucketLoadType::Client;
t = DataBucketLoadType::Client;
}
DataBucket::DeleteFromCache(id, t);

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