mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-24 09:28:21 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c966f26ac1 | |||
| fe66c24352 | |||
| bdcded7d45 | |||
| f939f25aa1 | |||
| 401f1038f3 | |||
| 95c6560e7c | |||
| 59f645b5c3 | |||
| bbfed8300c | |||
| 07c762068f | |||
| 6525051d2d | |||
| b7f94e8315 | |||
| d1f368ab7f | |||
| 1f3ac2dc4f | |||
| 7a226ca4ef | |||
| 119151c0e3 | |||
| faa8a492f7 | |||
| 2926b4df78 | |||
| 986eda44aa | |||
| b2f71f16fc | |||
| 861eac3660 | |||
| a376bc4471 | |||
| e83d0942ad | |||
| 31abaf8016 | |||
| 0acad18067 | |||
| 90c37390f1 | |||
| dba494cd8e | |||
| 37ced4b003 | |||
| d13c725a74 | |||
| 25826c6686 | |||
| 1a27127c39 |
@@ -68,3 +68,4 @@ compile_flags.txt
|
|||||||
|
|
||||||
# CMake Files
|
# CMake Files
|
||||||
cmake-build-relwithdebinfo/*
|
cmake-build-relwithdebinfo/*
|
||||||
|
skill-caps.diff
|
||||||
|
|||||||
@@ -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
|
## [22.61.0] 1/6/2025
|
||||||
|
|
||||||
### Bots
|
### Bots
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ SET(common_sources
|
|||||||
json/json.hpp
|
json/json.hpp
|
||||||
json/jsoncpp.cpp
|
json/jsoncpp.cpp
|
||||||
zone_store.cpp
|
zone_store.cpp
|
||||||
|
memory/ksm.hpp
|
||||||
net/console_server.cpp
|
net/console_server.cpp
|
||||||
net/console_server_connection.cpp
|
net/console_server_connection.cpp
|
||||||
net/crc32.cpp
|
net/crc32.cpp
|
||||||
|
|||||||
+219
-280
@@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
std::vector<BazaarSearchResultsFromDB_Struct>
|
std::vector<BazaarSearchResultsFromDB_Struct>
|
||||||
Bazaar::GetSearchResults(
|
Bazaar::GetSearchResults(
|
||||||
SharedDatabase &db,
|
Database &db,
|
||||||
|
Database &content_db,
|
||||||
BazaarSearchCriteria_Struct search,
|
BazaarSearchCriteria_Struct search,
|
||||||
uint32 char_zone_id,
|
uint32 char_zone_id,
|
||||||
int32 char_zone_instance_id
|
int32 char_zone_instance_id
|
||||||
@@ -31,8 +32,132 @@ Bazaar::GetSearchResults(
|
|||||||
char_zone_instance_id
|
char_zone_instance_id
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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;
|
bool convert = false;
|
||||||
std::string search_criteria_trader("TRUE");
|
std::string search_criteria_trader("TRUE");
|
||||||
|
std::string field_criteria_items("FALSE");
|
||||||
|
std::string where_criteria_items(" TRUE ");
|
||||||
|
|
||||||
if (search.search_scope == NonRoFBazaarSearchScope) {
|
if (search.search_scope == NonRoFBazaarSearchScope) {
|
||||||
search_criteria_trader.append(
|
search_criteria_trader.append(
|
||||||
@@ -77,301 +202,115 @@ Bazaar::GetSearchResults(
|
|||||||
search_criteria_trader.append(fmt::format(" AND trader.item_cost <= {}", (uint64) search.max_cost * 1000));
|
search_criteria_trader.append(fmt::format(" AND trader.item_cost <= {}", (uint64) search.max_cost * 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
// not yet implemented
|
if (search.slot != std::numeric_limits<uint32>::max()) {
|
||||||
// if (search.prestige != 0) {
|
if (item_slot_searches_new.contains(search.slot)) {
|
||||||
// 0xffffffff prestige only, 0xfffffffe non-prestige, 0 all
|
where_criteria_items.append(
|
||||||
// search_criteria.append(fmt::format(" AND items.type = {} ", search.prestige));
|
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()
|
|
||||||
);
|
|
||||||
|
|
||||||
std::vector<BazaarSearchResultsFromDB_Struct> all_entries;
|
|
||||||
|
|
||||||
auto results = db.QueryDatabase(query);
|
|
||||||
|
|
||||||
if (!results.Success()) {
|
|
||||||
return all_entries;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ItemSearchType {
|
if (search.type != std::numeric_limits<uint32>::max()) {
|
||||||
EQ::item::ItemType type;
|
for (auto const &[type, condition]: item_search_types_new) {
|
||||||
bool condition;
|
if (type == search.type) {
|
||||||
};
|
where_criteria_items.append(condition);
|
||||||
|
|
||||||
struct AddititiveSearchCriteria {
|
|
||||||
bool should_check;
|
|
||||||
bool condition;
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto row: results) {
|
|
||||||
BazaarSearchResultsFromDB_Struct r{};
|
|
||||||
|
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (has_filter && !met_filter) {
|
|
||||||
continue;
|
if (search.race != std::numeric_limits<uint32>::max()) {
|
||||||
|
where_criteria_items.append(
|
||||||
|
fmt::format(" AND items.races & {0} = {0}", GetPlayerRaceBit(GetRaceIDFromPlayerRaceValue(search.race))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add catch-all item type filter for specific item types
|
if (search._class != std::numeric_limits<uint32>::max()) {
|
||||||
|
where_criteria_items.append(fmt::format(" AND items.classes & {0} = {0}", GetPlayerClassBit(search._class)));
|
||||||
|
}
|
||||||
|
|
||||||
// item additive searches
|
if (search.item_stat != std::numeric_limits<uint32>::max()) {
|
||||||
std::vector<AddititiveSearchCriteria> item_additive_searches = {
|
if (item_stat_searches_new.contains(search.item_stat)) {
|
||||||
{
|
field_criteria_items = fmt::format("{}", item_stat_searches_new[search.item_stat].query_string);
|
||||||
.should_check = search.min_level != 1 && inst->GetItemRequiredLevel(true) > 0,
|
if (item_stat_searches_new[search.item_stat].skill_type) {
|
||||||
.condition = inst->GetItemRequiredLevel(true) >= search.min_level
|
where_criteria_items.append(
|
||||||
},
|
fmt::format(" AND items.skillmodtype = {} ", item_stat_searches_new[search.item_stat].skill_type));
|
||||||
{
|
}
|
||||||
.should_check = search.max_level != 1 && inst->GetItemRequiredLevel(true) > 0,
|
else {
|
||||||
.condition = inst->GetItemRequiredLevel(true) <= search.max_level
|
where_criteria_items.append(
|
||||||
},
|
fmt::format(" AND {} > 0 ", item_stat_searches_new[search.item_stat].query_string));
|
||||||
{
|
}
|
||||||
.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;
|
if (search.augment) {
|
||||||
|
where_criteria_items.append(fmt::format(
|
||||||
for (auto &i: item_additive_searches) {
|
" AND (items.augslot1type = {0} OR "
|
||||||
LogTradingDetail(
|
"items.augslot2type = {0} OR "
|
||||||
"Checking item [{}] for search criteria - should_check [{}] condition [{}]",
|
"items.augslot3type = {0} OR "
|
||||||
item->Name,
|
"items.augslot4type = {0} OR "
|
||||||
i.should_check,
|
"items.augslot5type = {0} OR "
|
||||||
i.condition
|
"items.augslot6type = {0})",
|
||||||
|
search.augment)
|
||||||
);
|
);
|
||||||
if (i.should_check && !i.condition) {
|
|
||||||
should_add = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!should_add) {
|
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 const trader_results = TraderRepository::GetBazaarTraderDetails(db, search_criteria_trader);
|
||||||
|
if (trader_results.empty()) {
|
||||||
|
LogTradingDetail("Bazaar - No traders found in bazaar search.");
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const &i: trader_results) {
|
||||||
|
trader_items_ids.push_back(std::to_string(i.trader.item_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogTradingDetail("Found item [{}] meeting search criteria.", r.item_name);
|
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;
|
||||||
|
|
||||||
if (RuleB(Bazaar, UseAlternateBazaarSearch)) {
|
if (RuleB(Bazaar, UseAlternateBazaarSearch)) {
|
||||||
if (convert || (r.trader_zone_id == Zones::BAZAAR && r.trader_zone_instance_id != char_zone_instance_id)) {
|
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;
|
r.trader_id = TraderRepository::TRADER_CONVERT_ID + r.trader_zone_instance_id;
|
||||||
|
|||||||
+3
-1
@@ -3,11 +3,13 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "shareddb.h"
|
#include "shareddb.h"
|
||||||
|
#include "../../common/item_instance.h"
|
||||||
|
|
||||||
class Bazaar {
|
class Bazaar {
|
||||||
public:
|
public:
|
||||||
static std::vector<BazaarSearchResultsFromDB_Struct>
|
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);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
#include "../common/repositories/raid_members_repository.h"
|
#include "../common/repositories/raid_members_repository.h"
|
||||||
#include "../common/repositories/reports_repository.h"
|
#include "../common/repositories/reports_repository.h"
|
||||||
#include "../common/repositories/variables_repository.h"
|
#include "../common/repositories/variables_repository.h"
|
||||||
|
#include "../common/repositories/character_pet_name_repository.h"
|
||||||
#include "../common/events/player_event_logs.h"
|
#include "../common/events/player_event_logs.h"
|
||||||
|
|
||||||
// Disgrace: for windows compile
|
// Disgrace: for windows compile
|
||||||
@@ -313,6 +314,12 @@ bool Database::ReserveName(uint32 account_id, const std::string& name)
|
|||||||
return false;
|
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();
|
auto e = CharacterDataRepository::NewEntity();
|
||||||
|
|
||||||
e.account_id = account_id;
|
e.account_id = account_id;
|
||||||
|
|||||||
@@ -169,7 +169,10 @@ bool DatabaseUpdate::UpdateManifest(
|
|||||||
LogSys.EnableMySQLErrorLogs();
|
LogSys.EnableMySQLErrorLogs();
|
||||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
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("Automatically backing up database before applying updates");
|
||||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||||
auto s = DatabaseDumpService();
|
auto s = DatabaseDumpService();
|
||||||
@@ -271,6 +274,13 @@ DatabaseUpdate *DatabaseUpdate::SetContentDatabase(Database *db)
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DatabaseUpdate *DatabaseUpdate::SetSkipBackup(bool skip)
|
||||||
|
{
|
||||||
|
m_skip_backup = skip;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b)
|
bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b)
|
||||||
{
|
{
|
||||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||||
|
|||||||
@@ -29,12 +29,15 @@ public:
|
|||||||
|
|
||||||
DatabaseUpdate *SetDatabase(Database *db);
|
DatabaseUpdate *SetDatabase(Database *db);
|
||||||
DatabaseUpdate *SetContentDatabase(Database *db);
|
DatabaseUpdate *SetContentDatabase(Database *db);
|
||||||
|
DatabaseUpdate *SetSkipBackup(bool skip);
|
||||||
bool HasPendingUpdates();
|
bool HasPendingUpdates();
|
||||||
private:
|
private:
|
||||||
|
bool m_skip_backup = false;
|
||||||
Database *m_database;
|
Database *m_database;
|
||||||
Database *m_content_database;
|
Database *m_content_database;
|
||||||
static bool CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b);
|
static bool CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b);
|
||||||
void InjectBotsVersionColumn();
|
void InjectBotsVersionColumn();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_DATABASE_UPDATE_H
|
#endif //EQEMU_DATABASE_UPDATE_H
|
||||||
|
|||||||
@@ -6289,7 +6289,96 @@ INSERT INTO `items_evolving_details` VALUES
|
|||||||
|
|
||||||
)",
|
)",
|
||||||
.content_schema_update = true
|
.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
|
// -- template; copy/paste this when you need to create a new entry
|
||||||
// ManifestEntry{
|
// ManifestEntry{
|
||||||
// .version = 9228,
|
// .version = 9228,
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ namespace DatabaseSchema {
|
|||||||
{"character_pet_buffs", "char_id"},
|
{"character_pet_buffs", "char_id"},
|
||||||
{"character_pet_info", "char_id"},
|
{"character_pet_info", "char_id"},
|
||||||
{"character_pet_inventory", "char_id"},
|
{"character_pet_inventory", "char_id"},
|
||||||
|
{"character_pet_name", "character_id"},
|
||||||
{"character_peqzone_flags", "id"},
|
{"character_peqzone_flags", "id"},
|
||||||
{"character_potionbelt", "id"},
|
{"character_potionbelt", "id"},
|
||||||
{"character_skills", "id"},
|
{"character_skills", "id"},
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ N(OP_CashReward),
|
|||||||
N(OP_CastSpell),
|
N(OP_CastSpell),
|
||||||
N(OP_ChangeSize),
|
N(OP_ChangeSize),
|
||||||
N(OP_ChannelMessage),
|
N(OP_ChannelMessage),
|
||||||
|
N(OP_ChangePetName),
|
||||||
N(OP_CharacterCreate),
|
N(OP_CharacterCreate),
|
||||||
N(OP_CharacterCreateRequest),
|
N(OP_CharacterCreateRequest),
|
||||||
N(OP_CharInventory),
|
N(OP_CharInventory),
|
||||||
@@ -284,6 +285,8 @@ N(OP_InspectMessageUpdate),
|
|||||||
N(OP_InspectRequest),
|
N(OP_InspectRequest),
|
||||||
N(OP_InstillDoubt),
|
N(OP_InstillDoubt),
|
||||||
N(OP_InterruptCast),
|
N(OP_InterruptCast),
|
||||||
|
N(OP_InvokeChangePetName),
|
||||||
|
N(OP_InvokeChangePetNameImmediate),
|
||||||
N(OP_ItemLinkClick),
|
N(OP_ItemLinkClick),
|
||||||
N(OP_ItemLinkResponse),
|
N(OP_ItemLinkResponse),
|
||||||
N(OP_ItemLinkText),
|
N(OP_ItemLinkText),
|
||||||
|
|||||||
@@ -5819,6 +5819,21 @@ struct ChangeSize_Struct
|
|||||||
/*16*/
|
/*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+
|
// New OpCode/Struct for SoD+
|
||||||
struct GroupMakeLeader_Struct
|
struct GroupMakeLeader_Struct
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -143,6 +143,8 @@ namespace Logs {
|
|||||||
Corpses,
|
Corpses,
|
||||||
XTargets,
|
XTargets,
|
||||||
EvolveItem,
|
EvolveItem,
|
||||||
|
PositionUpdate,
|
||||||
|
KSM,
|
||||||
MaxCategoryID /* Don't Remove this */
|
MaxCategoryID /* Don't Remove this */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -244,7 +246,9 @@ namespace Logs {
|
|||||||
"EqTime",
|
"EqTime",
|
||||||
"Corpses",
|
"Corpses",
|
||||||
"XTargets",
|
"XTargets",
|
||||||
"EvolveItem"
|
"EvolveItem",
|
||||||
|
"PositionUpdate",
|
||||||
|
"KSM" // Kernel Samepage Merging
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -854,6 +854,26 @@
|
|||||||
OutF(LogSys, Logs::Detail, Logs::XTargets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
OutF(LogSys, Logs::Detail, Logs::XTargets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
} while (0)
|
} 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 {\
|
#define Log(debug_level, log_category, message, ...) do {\
|
||||||
if (LogSys.IsLogEnabled(debug_level, log_category))\
|
if (LogSys.IsLogEnabled(debug_level, log_category))\
|
||||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
|||||||
@@ -218,6 +218,8 @@ namespace EQ
|
|||||||
std::map<int16, ItemInstance*>& GetPersonal() { return m_inv; }
|
std::map<int16, ItemInstance*>& GetPersonal() { return m_inv; }
|
||||||
int16 HasEvolvingItem(uint64 evolve_unique_id, uint8 quantity, uint8 where);
|
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:
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
// Protected Methods
|
// Protected Methods
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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 key_;
|
||||||
std::string value;
|
std::string value;
|
||||||
uint32_t expires;
|
uint32_t expires;
|
||||||
|
int64_t account_id;
|
||||||
int64_t character_id;
|
int64_t character_id;
|
||||||
int64_t npc_id;
|
int64_t npc_id;
|
||||||
int64_t bot_id;
|
int64_t bot_id;
|
||||||
@@ -36,6 +37,7 @@ public:
|
|||||||
CEREAL_NVP(key_),
|
CEREAL_NVP(key_),
|
||||||
CEREAL_NVP(value),
|
CEREAL_NVP(value),
|
||||||
CEREAL_NVP(expires),
|
CEREAL_NVP(expires),
|
||||||
|
CEREAL_NVP(account_id),
|
||||||
CEREAL_NVP(character_id),
|
CEREAL_NVP(character_id),
|
||||||
CEREAL_NVP(npc_id),
|
CEREAL_NVP(npc_id),
|
||||||
CEREAL_NVP(bot_id)
|
CEREAL_NVP(bot_id)
|
||||||
@@ -55,6 +57,7 @@ public:
|
|||||||
"`key`",
|
"`key`",
|
||||||
"value",
|
"value",
|
||||||
"expires",
|
"expires",
|
||||||
|
"account_id",
|
||||||
"character_id",
|
"character_id",
|
||||||
"npc_id",
|
"npc_id",
|
||||||
"bot_id",
|
"bot_id",
|
||||||
@@ -68,6 +71,7 @@ public:
|
|||||||
"`key`",
|
"`key`",
|
||||||
"value",
|
"value",
|
||||||
"expires",
|
"expires",
|
||||||
|
"account_id",
|
||||||
"character_id",
|
"character_id",
|
||||||
"npc_id",
|
"npc_id",
|
||||||
"bot_id",
|
"bot_id",
|
||||||
@@ -115,6 +119,7 @@ public:
|
|||||||
e.key_ = "";
|
e.key_ = "";
|
||||||
e.value = "";
|
e.value = "";
|
||||||
e.expires = 0;
|
e.expires = 0;
|
||||||
|
e.account_id = 0;
|
||||||
e.character_id = 0;
|
e.character_id = 0;
|
||||||
e.npc_id = 0;
|
e.npc_id = 0;
|
||||||
e.bot_id = 0;
|
e.bot_id = 0;
|
||||||
@@ -158,9 +163,10 @@ public:
|
|||||||
e.key_ = row[1] ? row[1] : "";
|
e.key_ = row[1] ? row[1] : "";
|
||||||
e.value = row[2] ? row[2] : "";
|
e.value = row[2] ? row[2] : "";
|
||||||
e.expires = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
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.account_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
|
||||||
e.npc_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
|
e.character_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
|
||||||
e.bot_id = row[6] ? strtoll(row[6], 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;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -197,9 +203,10 @@ public:
|
|||||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.key_) + "'");
|
v.push_back(columns[1] + " = '" + Strings::Escape(e.key_) + "'");
|
||||||
v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'");
|
v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'");
|
||||||
v.push_back(columns[3] + " = " + std::to_string(e.expires));
|
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[4] + " = " + std::to_string(e.account_id));
|
||||||
v.push_back(columns[5] + " = " + std::to_string(e.npc_id));
|
v.push_back(columns[5] + " = " + std::to_string(e.character_id));
|
||||||
v.push_back(columns[6] + " = " + std::to_string(e.bot_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(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -225,6 +232,7 @@ public:
|
|||||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||||
v.push_back(std::to_string(e.expires));
|
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.character_id));
|
||||||
v.push_back(std::to_string(e.npc_id));
|
v.push_back(std::to_string(e.npc_id));
|
||||||
v.push_back(std::to_string(e.bot_id));
|
v.push_back(std::to_string(e.bot_id));
|
||||||
@@ -261,6 +269,7 @@ public:
|
|||||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||||
v.push_back(std::to_string(e.expires));
|
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.character_id));
|
||||||
v.push_back(std::to_string(e.npc_id));
|
v.push_back(std::to_string(e.npc_id));
|
||||||
v.push_back(std::to_string(e.bot_id));
|
v.push_back(std::to_string(e.bot_id));
|
||||||
@@ -301,9 +310,10 @@ public:
|
|||||||
e.key_ = row[1] ? row[1] : "";
|
e.key_ = row[1] ? row[1] : "";
|
||||||
e.value = row[2] ? row[2] : "";
|
e.value = row[2] ? row[2] : "";
|
||||||
e.expires = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
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.account_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
|
||||||
e.npc_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
|
e.character_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
|
||||||
e.bot_id = row[6] ? strtoll(row[6], 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);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -332,9 +342,10 @@ public:
|
|||||||
e.key_ = row[1] ? row[1] : "";
|
e.key_ = row[1] ? row[1] : "";
|
||||||
e.value = row[2] ? row[2] : "";
|
e.value = row[2] ? row[2] : "";
|
||||||
e.expires = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
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.account_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
|
||||||
e.npc_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
|
e.character_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
|
||||||
e.bot_id = row[6] ? strtoll(row[6], 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);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -413,6 +424,7 @@ public:
|
|||||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||||
v.push_back(std::to_string(e.expires));
|
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.character_id));
|
||||||
v.push_back(std::to_string(e.npc_id));
|
v.push_back(std::to_string(e.npc_id));
|
||||||
v.push_back(std::to_string(e.bot_id));
|
v.push_back(std::to_string(e.bot_id));
|
||||||
@@ -442,6 +454,7 @@ public:
|
|||||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||||
v.push_back(std::to_string(e.expires));
|
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.character_id));
|
||||||
v.push_back(std::to_string(e.npc_id));
|
v.push_back(std::to_string(e.npc_id));
|
||||||
v.push_back(std::to_string(e.bot_id));
|
v.push_back(std::to_string(e.bot_id));
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public:
|
|||||||
int32_t fast_regen_mana;
|
int32_t fast_regen_mana;
|
||||||
int32_t fast_regen_endurance;
|
int32_t fast_regen_endurance;
|
||||||
int32_t npc_max_aggro_dist;
|
int32_t npc_max_aggro_dist;
|
||||||
uint32_t max_movement_update_range;
|
uint32_t client_update_range;
|
||||||
int32_t underworld_teleport_index;
|
int32_t underworld_teleport_index;
|
||||||
int32_t lava_damage;
|
int32_t lava_damage;
|
||||||
int32_t min_lava_damage;
|
int32_t min_lava_damage;
|
||||||
@@ -220,7 +220,7 @@ public:
|
|||||||
"fast_regen_mana",
|
"fast_regen_mana",
|
||||||
"fast_regen_endurance",
|
"fast_regen_endurance",
|
||||||
"npc_max_aggro_dist",
|
"npc_max_aggro_dist",
|
||||||
"max_movement_update_range",
|
"client_update_range",
|
||||||
"underworld_teleport_index",
|
"underworld_teleport_index",
|
||||||
"lava_damage",
|
"lava_damage",
|
||||||
"min_lava_damage",
|
"min_lava_damage",
|
||||||
@@ -325,7 +325,7 @@ public:
|
|||||||
"fast_regen_mana",
|
"fast_regen_mana",
|
||||||
"fast_regen_endurance",
|
"fast_regen_endurance",
|
||||||
"npc_max_aggro_dist",
|
"npc_max_aggro_dist",
|
||||||
"max_movement_update_range",
|
"client_update_range",
|
||||||
"underworld_teleport_index",
|
"underworld_teleport_index",
|
||||||
"lava_damage",
|
"lava_damage",
|
||||||
"min_lava_damage",
|
"min_lava_damage",
|
||||||
@@ -464,7 +464,7 @@ public:
|
|||||||
e.fast_regen_mana = 180;
|
e.fast_regen_mana = 180;
|
||||||
e.fast_regen_endurance = 180;
|
e.fast_regen_endurance = 180;
|
||||||
e.npc_max_aggro_dist = 600;
|
e.npc_max_aggro_dist = 600;
|
||||||
e.max_movement_update_range = 600;
|
e.client_update_range = 600;
|
||||||
e.underworld_teleport_index = 0;
|
e.underworld_teleport_index = 0;
|
||||||
e.lava_damage = 50;
|
e.lava_damage = 50;
|
||||||
e.min_lava_damage = 10;
|
e.min_lava_damage = 10;
|
||||||
@@ -599,7 +599,7 @@ public:
|
|||||||
e.fast_regen_mana = row[89] ? static_cast<int32_t>(atoi(row[89])) : 180;
|
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.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.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.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.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.min_lava_damage = row[95] ? static_cast<int32_t>(atoi(row[95])) : 10;
|
||||||
@@ -730,7 +730,7 @@ public:
|
|||||||
v.push_back(columns[89] + " = " + std::to_string(e.fast_regen_mana));
|
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[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[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[93] + " = " + std::to_string(e.underworld_teleport_index));
|
||||||
v.push_back(columns[94] + " = " + std::to_string(e.lava_damage));
|
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[95] + " = " + std::to_string(e.min_lava_damage));
|
||||||
@@ -850,7 +850,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.fast_regen_mana));
|
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.fast_regen_endurance));
|
||||||
v.push_back(std::to_string(e.npc_max_aggro_dist));
|
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.underworld_teleport_index));
|
||||||
v.push_back(std::to_string(e.lava_damage));
|
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.min_lava_damage));
|
||||||
@@ -978,7 +978,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.fast_regen_mana));
|
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.fast_regen_endurance));
|
||||||
v.push_back(std::to_string(e.npc_max_aggro_dist));
|
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.underworld_teleport_index));
|
||||||
v.push_back(std::to_string(e.lava_damage));
|
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.min_lava_damage));
|
||||||
@@ -1110,7 +1110,7 @@ public:
|
|||||||
e.fast_regen_mana = row[89] ? static_cast<int32_t>(atoi(row[89])) : 180;
|
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.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.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.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.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.min_lava_damage = row[95] ? static_cast<int32_t>(atoi(row[95])) : 10;
|
||||||
@@ -1233,7 +1233,7 @@ public:
|
|||||||
e.fast_regen_mana = row[89] ? static_cast<int32_t>(atoi(row[89])) : 180;
|
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.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.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.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.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.min_lava_damage = row[95] ? static_cast<int32_t>(atoi(row[95])) : 10;
|
||||||
@@ -1406,7 +1406,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.fast_regen_mana));
|
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.fast_regen_endurance));
|
||||||
v.push_back(std::to_string(e.npc_max_aggro_dist));
|
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.underworld_teleport_index));
|
||||||
v.push_back(std::to_string(e.lava_damage));
|
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.min_lava_damage));
|
||||||
@@ -1527,7 +1527,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.fast_regen_mana));
|
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.fast_regen_endurance));
|
||||||
v.push_back(std::to_string(e.npc_max_aggro_dist));
|
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.underworld_teleport_index));
|
||||||
v.push_back(std::to_string(e.lava_damage));
|
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.min_lava_damage));
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -7,6 +7,14 @@
|
|||||||
|
|
||||||
class ItemsRepository: public BaseItemsRepository {
|
class ItemsRepository: public BaseItemsRepository {
|
||||||
public:
|
public:
|
||||||
|
struct Bazaar_Results {
|
||||||
|
uint32 item_id;
|
||||||
|
std::string name;
|
||||||
|
bool stackable;
|
||||||
|
uint32 icon;
|
||||||
|
uint32 stats;
|
||||||
|
};
|
||||||
|
|
||||||
static std::vector<int32> GetItemIDsBySearchCriteria(
|
static std::vector<int32> GetItemIDsBySearchCriteria(
|
||||||
Database& db,
|
Database& db,
|
||||||
std::string search_string,
|
std::string search_string,
|
||||||
@@ -37,6 +45,51 @@ public:
|
|||||||
return item_id_list;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ public:
|
|||||||
std::vector<DistinctTraders_Struct> traders{};
|
std::vector<DistinctTraders_Struct> traders{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BazaarTraderSearch_Struct {
|
||||||
|
Trader trader;
|
||||||
|
std::string trader_name;
|
||||||
|
};
|
||||||
|
|
||||||
struct WelcomeData_Struct {
|
struct WelcomeData_Struct {
|
||||||
uint32 count_of_traders;
|
uint32 count_of_traders;
|
||||||
uint32 count_of_items;
|
uint32 count_of_items;
|
||||||
@@ -265,6 +270,54 @@ public:
|
|||||||
|
|
||||||
return trader;
|
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
|
#endif //EQEMU_TRADER_REPOSITORY_H
|
||||||
|
|||||||
+1
-1
@@ -661,7 +661,7 @@ bool SharedDatabase::GetInventory(Client *c)
|
|||||||
// Retrieve character inventory
|
// 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(
|
auto e_results = CharacterEvolvingItemsRepository::GetWhere(
|
||||||
*this, fmt::format("`char_id` = '{}' AND `deleted_at` IS NULL", char_id)
|
*this, fmt::format("`character_id` = '{}' AND `deleted_at` IS NULL", char_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (results.empty()) {
|
if (results.empty()) {
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ struct BenchTimer
|
|||||||
void reset() { start_time = clock::now(); }
|
void reset() { start_time = clock::now(); }
|
||||||
// this is seconds
|
// this is seconds
|
||||||
double elapsed() { return std::chrono::duration<double> (clock::now() - start_time).count(); }
|
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:
|
private:
|
||||||
std::chrono::time_point<std::chrono::high_resolution_clock> start_time;
|
std::chrono::time_point<std::chrono::high_resolution_clock> start_time;
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-2
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
// Build variables
|
// Build variables
|
||||||
// these get injected during the build pipeline
|
// these get injected during the build pipeline
|
||||||
#define CURRENT_VERSION "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 LOGIN_VERSION "0.8.0"
|
||||||
#define COMPILE_DATE __DATE__
|
#define COMPILE_DATE __DATE__
|
||||||
#define COMPILE_TIME __TIME__
|
#define COMPILE_TIME __TIME__
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define CURRENT_BINARY_DATABASE_VERSION 9290
|
#define CURRENT_BINARY_DATABASE_VERSION 9295
|
||||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045
|
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -676,12 +676,6 @@ int ZoneStore::GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version)
|
|||||||
return z ? z->npc_max_aggro_dist : DEFAULT_ZONE_MAX_AGGRO_DISTANCE;
|
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)
|
int8 ZoneStore::GetZoneMinimumExpansion(uint32 zone_id, int version)
|
||||||
{
|
{
|
||||||
const auto& z = GetZoneVersionWithFallback(zone_id, version);
|
const auto& z = GetZoneVersionWithFallback(zone_id, version);
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ public:
|
|||||||
int GetZoneFastRegenMana(uint32 zone_id, int version = 0);
|
int GetZoneFastRegenMana(uint32 zone_id, int version = 0);
|
||||||
int GetZoneFastRegenEndurance(uint32 zone_id, int version = 0);
|
int GetZoneFastRegenEndurance(uint32 zone_id, int version = 0);
|
||||||
int GetZoneNPCMaximumAggroDistance(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 GetZoneMinimumExpansion(uint32 zone_id, int version = 0);
|
||||||
int8 GetZoneMaximumExpansion(uint32 zone_id, int version = 0);
|
int8 GetZoneMaximumExpansion(uint32 zone_id, int version = 0);
|
||||||
const std::string GetZoneContentFlags(uint32 zone_id, int version = 0);
|
const std::string GetZoneContentFlags(uint32 zone_id, int version = 0);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "eqemu-server",
|
"name": "eqemu-server",
|
||||||
"version": "22.61.0",
|
"version": "22.62.1",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/EQEmu/Server.git"
|
"url": "https://github.com/EQEmu/Server.git"
|
||||||
|
|||||||
@@ -735,3 +735,12 @@ OP_PickZone=0xaaba
|
|||||||
|
|
||||||
#evolve item related
|
#evolve item related
|
||||||
OP_EvolveItem=0x7cfb
|
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
|
||||||
@@ -11,6 +11,6 @@ require (
|
|||||||
github.com/golang/protobuf v1.3.2 // indirect
|
github.com/golang/protobuf v1.3.2 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.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
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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/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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
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 h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ void WorldserverCLI::CopyCharacter(int argc, char **argv, argh::parser &cmd, std
|
|||||||
};
|
};
|
||||||
std::vector<std::string> options = {};
|
std::vector<std::string> options = {};
|
||||||
|
|
||||||
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
|
|
||||||
|
|
||||||
if (cmd[{"-h", "--help"}]) {
|
if (cmd[{"-h", "--help"}]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
|
||||||
|
|
||||||
std::string source_character_name = cmd(2).str();
|
std::string source_character_name = cmd(2).str();
|
||||||
std::string destination_character_name = cmd(3).str();
|
std::string destination_character_name = cmd(3).str();
|
||||||
std::string destination_account_name = cmd(4).str();
|
std::string destination_account_name = cmd(4).str();
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ void WorldserverCLI::DatabaseDump(int argc, char **argv, argh::parser &cmd, std:
|
|||||||
"--compress"
|
"--compress"
|
||||||
};
|
};
|
||||||
|
|
||||||
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
|
|
||||||
|
|
||||||
if (cmd[{"-h", "--help"}]) {
|
if (cmd[{"-h", "--help"}]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
|
||||||
|
|
||||||
auto s = new DatabaseDumpService();
|
auto s = new DatabaseDumpService();
|
||||||
bool dump_all = cmd[{"-a", "--all"}];
|
bool dump_all = cmd[{"-a", "--all"}];
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,17 @@ void WorldserverCLI::DatabaseUpdates(int argc, char **argv, argh::parser &cmd, s
|
|||||||
{
|
{
|
||||||
description = "Runs database updates manually";
|
description = "Runs database updates manually";
|
||||||
|
|
||||||
|
std::vector<std::string> options = {
|
||||||
|
"--skip-backup",
|
||||||
|
};
|
||||||
|
|
||||||
if (cmd[{"-h", "--help"}]) {
|
if (cmd[{"-h", "--help"}]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseUpdate update;
|
DatabaseUpdate update;
|
||||||
update.SetDatabase(&database)
|
update.SetDatabase(&database)
|
||||||
|
->SetSkipBackup(cmd[{"--skip-backup"}] )
|
||||||
->SetContentDatabase(&content_db)
|
->SetContentDatabase(&content_db)
|
||||||
->CheckDbUpdates();
|
->CheckDbUpdates();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include <cereal/archives/json.hpp>
|
#include <cereal/archives/json.hpp>
|
||||||
#include <cereal/types/vector.hpp>
|
#include <cereal/types/vector.hpp>
|
||||||
|
#include <iomanip>
|
||||||
#include "../../common/events/player_events.h"
|
#include "../../common/events/player_events.h"
|
||||||
|
#include "../../common/memory/ksm.hpp"
|
||||||
|
|
||||||
void WorldserverCLI::TestCommand(int argc, char **argv, argh::parser &cmd, std::string &description)
|
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;
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-26
@@ -2559,16 +2559,12 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
|||||||
const uint8 killed_level = GetLevel();
|
const uint8 killed_level = GetLevel();
|
||||||
|
|
||||||
if (GetClass() == Class::LDoNTreasure) { // open chest
|
if (GetClass() == Class::LDoNTreasure) { // open chest
|
||||||
auto outapp = new EQApplicationPacket(OP_Animation, sizeof(Animation_Struct));
|
static EQApplicationPacket p(OP_Animation, sizeof(Animation_Struct));
|
||||||
|
auto a = (Animation_Struct*) p.pBuffer;
|
||||||
auto a = (Animation_Struct*) outapp->pBuffer;
|
|
||||||
|
|
||||||
a->spawnid = GetID();
|
a->spawnid = GetID();
|
||||||
a->action = 0x0F;
|
a->action = 0x0F;
|
||||||
a->speed = 10;
|
a->speed = 10;
|
||||||
|
entity_list.QueueCloseClients(this, &p);
|
||||||
entity_list.QueueCloseClients(this, outapp);
|
|
||||||
safe_delete(outapp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto app = new EQApplicationPacket(OP_Death, sizeof(Death_Struct));
|
auto app = new EQApplicationPacket(OP_Death, sizeof(Death_Struct));
|
||||||
@@ -3415,15 +3411,15 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) {
|
|||||||
|
|
||||||
attacker->Damage(this, -DS, spellid, EQ::skills::SkillAbjuration/*hackish*/, false);
|
attacker->Damage(this, -DS, spellid, EQ::skills::SkillAbjuration/*hackish*/, false);
|
||||||
//we can assume there is a spell now
|
//we can assume there is a spell now
|
||||||
auto outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct));
|
|
||||||
CombatDamage_Struct* cds = (CombatDamage_Struct*)outapp->pBuffer;
|
static EQApplicationPacket p(OP_Damage, sizeof(CombatDamage_Struct));
|
||||||
cds->target = attacker->GetID();
|
auto b = (CombatDamage_Struct *) p.pBuffer;
|
||||||
cds->source = GetID();
|
b->target = attacker->GetID();
|
||||||
cds->type = spellbonuses.DamageShieldType;
|
b->source = GetID();
|
||||||
cds->spellid = 0x0;
|
b->type = spellbonuses.DamageShieldType;
|
||||||
cds->damage = DS;
|
b->spellid = 0x0;
|
||||||
entity_list.QueueCloseClients(this, outapp);
|
b->damage = DS;
|
||||||
safe_delete(outapp);
|
entity_list.QueueCloseClients(this, &p);
|
||||||
}
|
}
|
||||||
else if (DS > 0 && !spell_ds) {
|
else if (DS > 0 && !spell_ds) {
|
||||||
//we are healing the attacker...
|
//we are healing the attacker...
|
||||||
@@ -4540,8 +4536,8 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
|||||||
|
|
||||||
//send damage packet...
|
//send damage packet...
|
||||||
if (!iBuffTic) { //buff ticks do not send damage, instead they just call SendHPUpdate(), which is done above
|
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));
|
static EQApplicationPacket p(OP_Damage, sizeof(CombatDamage_Struct));
|
||||||
CombatDamage_Struct* a = (CombatDamage_Struct*)outapp->pBuffer;
|
auto a = (CombatDamage_Struct *) p.pBuffer;
|
||||||
a->target = GetID();
|
a->target = GetID();
|
||||||
|
|
||||||
if (!attacker) {
|
if (!attacker) {
|
||||||
@@ -4622,7 +4618,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
|||||||
if (!FromDamageShield) {
|
if (!FromDamageShield) {
|
||||||
entity_list.QueueCloseClients(
|
entity_list.QueueCloseClients(
|
||||||
attacker, /* Sender */
|
attacker, /* Sender */
|
||||||
outapp, /* packet */
|
&p, /* packet */
|
||||||
false, /* Skip Sender */
|
false, /* Skip Sender */
|
||||||
((IsValidSpell(spell_id)) ? RuleI(Range, SpellMessages) : RuleI(Range, DamageMessages)),
|
((IsValidSpell(spell_id)) ? RuleI(Range, SpellMessages) : RuleI(Range, DamageMessages)),
|
||||||
0, /* don't skip anyone on spell */
|
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;
|
filter = FilterMyMisses;
|
||||||
|
|
||||||
if (attacker->IsClient()) {
|
if (attacker->IsClient()) {
|
||||||
attacker->CastToClient()->QueuePacket(outapp, true, CLIENT_CONNECTED, filter);
|
attacker->CastToClient()->QueuePacket(&p, true, CLIENT_CONNECTED, filter);
|
||||||
} else {
|
} else {
|
||||||
entity_list.QueueCloseClients(
|
entity_list.QueueCloseClients(
|
||||||
attacker, /* Sender */
|
attacker, /* Sender */
|
||||||
outapp, /* packet */
|
&p, /* packet */
|
||||||
false, /* Skip Sender */
|
false, /* Skip Sender */
|
||||||
(
|
(
|
||||||
IsValidSpell(spell_id) ?
|
IsValidSpell(spell_id) ?
|
||||||
@@ -4755,7 +4751,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
|||||||
a->type = DamageTypeSpell;
|
a->type = DamageTypeSpell;
|
||||||
entity_list.QueueCloseClients(
|
entity_list.QueueCloseClients(
|
||||||
this, /* Sender */
|
this, /* Sender */
|
||||||
outapp, /* packet */
|
&p, /* packet */
|
||||||
false, /* Skip Sender */
|
false, /* Skip Sender */
|
||||||
range, /* distance packet travels at the speed of sound */
|
range, /* distance packet travels at the speed of sound */
|
||||||
0, /* don't skip anyone on spell */
|
0, /* don't skip anyone on spell */
|
||||||
@@ -4766,7 +4762,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
|||||||
else {
|
else {
|
||||||
//I dont think any filters apply to damage affecting us
|
//I dont think any filters apply to damage affecting us
|
||||||
if (IsClient()) {
|
if (IsClient()) {
|
||||||
CastToClient()->QueuePacket(outapp);
|
CastToClient()->QueuePacket(&p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send normal message to observers
|
// 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())) {
|
if (!owner || (owner && !owner->IsClient())) {
|
||||||
entity_list.QueueCloseClients(
|
entity_list.QueueCloseClients(
|
||||||
this, /* Sender */
|
this, /* Sender */
|
||||||
outapp, /* packet */
|
&p, /* packet */
|
||||||
true, /* Skip Sender */
|
true, /* Skip Sender */
|
||||||
range, /* distance packet travels at the speed of sound */
|
range, /* distance packet travels at the speed of sound */
|
||||||
(IsValidSpell(spell_id) && skill_used != EQ::skills::SkillTigerClaw) ? 0 : skip,
|
(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 {
|
||||||
//else, it is a buff tic...
|
//else, it is a buff tic...
|
||||||
|
|||||||
+5
-4
@@ -735,21 +735,22 @@ bool Aura::Process()
|
|||||||
|
|
||||||
if (movement_type == AuraMovement::Follow && GetPosition() != owner->GetPosition() && movement_timer.Check()) {
|
if (movement_type == AuraMovement::Follow && GetPosition() != owner->GetPosition() && movement_timer.Check()) {
|
||||||
m_Position = owner->GetPosition();
|
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);
|
MakeSpawnUpdate(spu);
|
||||||
auto it = spawned_for.begin();
|
auto it = spawned_for.begin();
|
||||||
while (it != spawned_for.end()) {
|
while (it != spawned_for.end()) {
|
||||||
auto client = entity_list.GetClientByID(*it);
|
auto client = entity_list.GetClientByID(*it);
|
||||||
if (client) {
|
if (client) {
|
||||||
client->QueuePacket(app);
|
client->QueuePacket(&packet);
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
it = spawned_for.erase(it);
|
it = spawned_for.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
safe_delete(app);
|
|
||||||
}
|
}
|
||||||
// TODO: waypoints?
|
// TODO: waypoints?
|
||||||
|
|
||||||
|
|||||||
@@ -7390,39 +7390,6 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) {
|
|||||||
return;
|
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 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid) {
|
||||||
|
|
||||||
uint8 need_healed = 0;
|
uint8 need_healed = 0;
|
||||||
|
|||||||
+205
-23
@@ -66,6 +66,7 @@ extern volatile bool RunLoops;
|
|||||||
#include "../common/repositories/character_spells_repository.h"
|
#include "../common/repositories/character_spells_repository.h"
|
||||||
#include "../common/repositories/character_disciplines_repository.h"
|
#include "../common/repositories/character_disciplines_repository.h"
|
||||||
#include "../common/repositories/character_data_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/discovered_items_repository.h"
|
||||||
#include "../common/repositories/inventory_repository.h"
|
#include "../common/repositories/inventory_repository.h"
|
||||||
#include "../common/repositories/keyring_repository.h"
|
#include "../common/repositories/keyring_repository.h"
|
||||||
@@ -157,7 +158,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
|||||||
endupkeep_timer(1000),
|
endupkeep_timer(1000),
|
||||||
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
|
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
|
||||||
m_client_npc_aggro_scan_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)),
|
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),
|
tribute_timer(Tribute_duration),
|
||||||
proximity_timer(ClientProximity_interval),
|
proximity_timer(ClientProximity_interval),
|
||||||
TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000),
|
TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000),
|
||||||
@@ -402,6 +403,7 @@ Client::~Client() {
|
|||||||
|
|
||||||
mMovementManager->RemoveClient(this);
|
mMovementManager->RemoveClient(this);
|
||||||
|
|
||||||
|
DataBucket::DeleteCachedBuckets(DataBucketLoadType::Account, AccountID());
|
||||||
DataBucket::DeleteCachedBuckets(DataBucketLoadType::Client, CharacterID());
|
DataBucket::DeleteCachedBuckets(DataBucketLoadType::Client, CharacterID());
|
||||||
|
|
||||||
if (RuleB(Bots, Enabled)) {
|
if (RuleB(Bots, Enabled)) {
|
||||||
@@ -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) {
|
bool Client::IsDiscovered(uint32 item_id) {
|
||||||
const auto& l = DiscoveredItemsRepository::GetWhere(
|
const auto& l = DiscoveredItemsRepository::GetWhere(
|
||||||
database,
|
database,
|
||||||
@@ -12939,50 +13018,77 @@ void Client::SendTopLevelInventory()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// On a normal basis we limit mob movement updates based on distance
|
void Client::CheckSendBulkNpcPositions()
|
||||||
// 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()
|
|
||||||
{
|
{
|
||||||
float distance_moved = DistanceNoZ(m_last_position_before_bulk_update, GetPosition());
|
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 = (
|
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) {
|
int updated_count = 0;
|
||||||
LogDebug("[[{}]] Client Zone Wide Position Update NPCs", GetCleanName());
|
int skipped_count = 0;
|
||||||
|
if (is_ready_to_update) {
|
||||||
auto &mob_movement_manager = MobMovementManager::Get();
|
auto &mob_movement_manager = MobMovementManager::Get();
|
||||||
auto &mob_list = entity_list.GetMobList();
|
|
||||||
|
|
||||||
for (auto &it : mob_list) {
|
for (auto &e: entity_list.GetMobList()) {
|
||||||
Mob *entity = it.second;
|
Mob *mob = e.second;
|
||||||
if (!entity->IsNPC()) {
|
if (!mob->IsNPC()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int animation_speed = 0;
|
int animation_speed = 0;
|
||||||
if (entity->IsMoving()) {
|
if (mob->IsMoving()) {
|
||||||
if (entity->IsRunning()) {
|
if (mob->IsRunning()) {
|
||||||
animation_speed = (entity->IsFeared() ? entity->GetFearSpeed() : entity->GetRunspeed());
|
animation_speed = (mob->IsFeared() ? mob->GetFearSpeed() : mob->GetRunspeed());
|
||||||
}
|
}
|
||||||
else {
|
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();
|
m_last_position_before_bulk_update = GetPosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const uint16 scan_npc_aggro_timer_idle = RuleI(Aggro, ClientAggroCheckIdleInterval);
|
const uint16 scan_npc_aggro_timer_idle = RuleI(Aggro, ClientAggroCheckIdleInterval);
|
||||||
const uint16 scan_npc_aggro_timer_moving = RuleI(Aggro, ClientAggroCheckMovingInterval);
|
const uint16 scan_npc_aggro_timer_moving = RuleI(Aggro, ClientAggroCheckMovingInterval);
|
||||||
|
|
||||||
@@ -13123,3 +13229,79 @@ void Client::SetAAEXPPercentage(uint8 percentage)
|
|||||||
SendAlternateAdvancementStats();
|
SendAlternateAdvancementStats();
|
||||||
SendAlternateAdvancementTable();
|
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);
|
||||||
|
}
|
||||||
|
|||||||
+17
-4
@@ -307,6 +307,11 @@ public:
|
|||||||
void KeyRingAdd(uint32 item_id);
|
void KeyRingAdd(uint32 item_id);
|
||||||
bool KeyRingCheck(uint32 item_id);
|
bool KeyRingCheck(uint32 item_id);
|
||||||
void KeyRingList();
|
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 IsClient() const override { return true; }
|
||||||
bool IsOfClientBot() const override { return true; }
|
bool IsOfClientBot() const override { return true; }
|
||||||
bool IsOfClientBotMerc() const override { return true; }
|
bool IsOfClientBotMerc() const override { return true; }
|
||||||
@@ -334,7 +339,6 @@ public:
|
|||||||
const char *message9 = nullptr);
|
const char *message9 = nullptr);
|
||||||
void Tell_StringID(uint32 string_id, const char *who, const char *message);
|
void Tell_StringID(uint32 string_id, const char *who, const char *message);
|
||||||
void SendColoredText(uint32 color, std::string 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 SendTraderItem(uint32 item_id,uint16 quantity, TraderRepository::Trader &trader);
|
||||||
void DoBazaarSearch(BazaarSearchCriteria_Struct search_criteria);
|
void DoBazaarSearch(BazaarSearchCriteria_Struct search_criteria);
|
||||||
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);
|
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);
|
||||||
@@ -1170,7 +1174,7 @@ public:
|
|||||||
void Escape(); //keep or quest function
|
void Escape(); //keep or quest function
|
||||||
void DisenchantSummonedBags(bool client_update = true);
|
void DisenchantSummonedBags(bool client_update = true);
|
||||||
void RemoveNoRent(bool client_update = true);
|
void RemoveNoRent(bool client_update = true);
|
||||||
void RemoveDuplicateLore(bool client_update = true);
|
void RemoveDuplicateLore();
|
||||||
void MoveSlotNotAllowed(bool client_update = true);
|
void MoveSlotNotAllowed(bool client_update = true);
|
||||||
virtual bool RangedAttack(Mob* other, bool CanDoubleAttack = false);
|
virtual bool RangedAttack(Mob* other, bool CanDoubleAttack = false);
|
||||||
virtual void ThrowingAttack(Mob* other, bool CanDoubleAttack = false);
|
virtual void ThrowingAttack(Mob* other, bool CanDoubleAttack = false);
|
||||||
@@ -1829,6 +1833,13 @@ public:
|
|||||||
void SendEvolveXPTransferWindow();
|
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);
|
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:
|
protected:
|
||||||
friend class Mob;
|
friend class Mob;
|
||||||
void CalcEdibleBonuses(StatBonuses* newbon);
|
void CalcEdibleBonuses(StatBonuses* newbon);
|
||||||
@@ -2087,12 +2098,13 @@ private:
|
|||||||
Timer m_client_npc_aggro_scan_timer;
|
Timer m_client_npc_aggro_scan_timer;
|
||||||
void CheckClientToNpcAggroTimer();
|
void CheckClientToNpcAggroTimer();
|
||||||
void ClientToNpcAggroProcess();
|
void ClientToNpcAggroProcess();
|
||||||
|
void BroadcastPositionUpdate();
|
||||||
|
|
||||||
// bulk position updates
|
// bulk position updates
|
||||||
glm::vec4 m_last_position_before_bulk_update;
|
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;
|
Timer m_position_update_timer;
|
||||||
void CheckSendBulkClientPositionUpdate();
|
void CheckSendBulkNpcPositions();
|
||||||
|
|
||||||
void BulkSendInventoryItems();
|
void BulkSendInventoryItems();
|
||||||
|
|
||||||
@@ -2259,6 +2271,7 @@ public:
|
|||||||
const std::string &GetMailKeyFull() const;
|
const std::string &GetMailKeyFull() const;
|
||||||
const std::string &GetMailKey() const;
|
const std::string &GetMailKey() const;
|
||||||
void ShowZoneShardMenu();
|
void ShowZoneShardMenu();
|
||||||
|
void Handle_OP_ChangePetName(const EQApplicationPacket *app);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+61
-23
@@ -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/character_corpses_repository.h"
|
||||||
#include "../common/repositories/guild_tributes_repository.h"
|
#include "../common/repositories/guild_tributes_repository.h"
|
||||||
#include "../common/repositories/buyer_buy_lines_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/events/player_event_logs.h"
|
||||||
#include "../common/repositories/character_stats_record_repository.h"
|
#include "../common/repositories/character_stats_record_repository.h"
|
||||||
@@ -160,6 +161,7 @@ void MapOpcodes()
|
|||||||
ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade;
|
ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade;
|
||||||
ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell;
|
ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell;
|
||||||
ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage;
|
ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage;
|
||||||
|
ConnectedOpcodes[OP_ChangePetName] = &Client::Handle_OP_ChangePetName;
|
||||||
ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs;
|
ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs;
|
||||||
ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks;
|
ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks;
|
||||||
ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname;
|
ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname;
|
||||||
@@ -820,6 +822,10 @@ void Client::CompleteConnect()
|
|||||||
CharacterID()
|
CharacterID()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (IsPetNameChangeAllowed()) {
|
||||||
|
InvokeChangePetName(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
|
if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
|
||||||
@@ -971,6 +977,16 @@ void Client::CompleteConnect()
|
|||||||
RecordStats();
|
RecordStats();
|
||||||
AutoGrantAAPoints();
|
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..
|
// enforce some rules..
|
||||||
if (!CanEnterZone()) {
|
if (!CanEnterZone()) {
|
||||||
LogInfo("Kicking character [{}] from zone, not allowed here (missing requirements)", GetCleanName());
|
LogInfo("Kicking character [{}] from zone, not allowed here (missing requirements)", GetCleanName());
|
||||||
@@ -3236,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.
|
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");
|
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.");
|
Message(Chat::Red, "Error: No augment found on cursor for inserting.");
|
||||||
return;
|
break;
|
||||||
} else {
|
} else {
|
||||||
if (!RuleB(Inventory, AllowMultipleOfSameAugment) && tobe_auged->ContainsAugmentByID(new_aug->GetID())) {
|
if (!RuleB(Inventory, AllowMultipleOfSameAugment) && tobe_auged->ContainsAugmentByID(new_aug->GetID())) {
|
||||||
Message(Chat::Red, "Error: Cannot put multiple of the same augment in an item.");
|
Message(Chat::Red, "Error: Cannot put multiple of the same augment in an item.");
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -3324,7 +3340,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
|
|||||||
in_augment->augment_index
|
in_augment->augment_index
|
||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
item_one_to_push = tobe_auged->Clone();
|
item_one_to_push = tobe_auged->Clone();
|
||||||
@@ -3396,7 +3412,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Message(Chat::Red, "Error: Could not find augmentation to remove at index %i. Aborting.", in_augment->augment_index);
|
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);
|
old_aug = tobe_auged->RemoveAugment(in_augment->augment_index);
|
||||||
@@ -3429,7 +3445,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
|
|||||||
if (!PutItemInInventory(EQ::invslot::slotCursor, *item_two_to_push, true)) {
|
if (!PutItemInInventory(EQ::invslot::slotCursor, *item_two_to_push, true)) {
|
||||||
LogError("Problem returning augment to player's cursor after safe removal");
|
LogError("Problem returning augment to player's cursor after safe removal");
|
||||||
Message(Chat::Yellow, "Error: Failed to return augment after removal from item!");
|
Message(Chat::Yellow, "Error: Failed to return augment after removal from item!");
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -3474,7 +3490,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
|
|||||||
in_augment->augment_index
|
in_augment->augment_index
|
||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tobe_auged->DeleteAugment(in_augment->augment_index);
|
tobe_auged->DeleteAugment(in_augment->augment_index);
|
||||||
@@ -3495,6 +3511,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
|
|||||||
if (material != EQ::textures::materialInvalid) {
|
if (material != EQ::textures::materialInvalid) {
|
||||||
SendWearChange(material);
|
SendWearChange(material);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default: // Unknown
|
default: // Unknown
|
||||||
LogInventory(
|
LogInventory(
|
||||||
@@ -3508,9 +3525,12 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
safe_delete(item_one_to_push);
|
||||||
|
safe_delete(item_two_to_push);
|
||||||
} else {
|
} else {
|
||||||
Object::HandleAugmentation(this, in_augment, m_tradeskill_object); // Delegate to tradeskill object to perform combine
|
Object::HandleAugmentation(this, in_augment, m_tradeskill_object); // Delegate to tradeskill object to perform combine
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4558,6 +4578,27 @@ void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app)
|
|||||||
return;
|
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)
|
void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
if (!RuleB(Spells, EnableBlockedBuffs))
|
if (!RuleB(Spells, EnableBlockedBuffs))
|
||||||
@@ -4801,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));
|
auto boat_delta = glm::vec4(ppu->delta_x, ppu->delta_y, ppu->delta_z, EQ10toFloat(ppu->delta_heading));
|
||||||
cmob->SetDelta(boat_delta);
|
cmob->SetDelta(boat_delta);
|
||||||
|
|
||||||
auto outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
static EQApplicationPacket outapp(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||||
PlayerPositionUpdateServer_Struct *ppus = (PlayerPositionUpdateServer_Struct *) outapp->pBuffer;
|
auto *ppus = (PlayerPositionUpdateServer_Struct *) outapp.pBuffer;
|
||||||
cmob->MakeSpawnUpdate(ppus);
|
cmob->MakeSpawnUpdate(ppus);
|
||||||
entity_list.QueueCloseClients(cmob, outapp, true, 300, this, false);
|
entity_list.QueueCloseClients(cmob, &outapp, true, 300, this, false);
|
||||||
safe_delete(outapp);
|
|
||||||
|
|
||||||
/* Update the boat's position on the server, without sending an update */
|
/* 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));
|
cmob->GMMove(ppu->x_pos, ppu->y_pos, ppu->z_pos, EQ12toFloat(ppu->heading));
|
||||||
@@ -4956,7 +4996,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
|
|||||||
CheckScanCloseMobsMovingTimer();
|
CheckScanCloseMobsMovingTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckSendBulkClientPositionUpdate();
|
CheckSendBulkNpcPositions();
|
||||||
|
|
||||||
int32 new_animation = ppu->animation;
|
int32 new_animation = ppu->animation;
|
||||||
|
|
||||||
@@ -4979,15 +5019,15 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
|
|||||||
m_Position.w = new_heading;
|
m_Position.w = new_heading;
|
||||||
|
|
||||||
/* Broadcast update to other clients */
|
/* Broadcast update to other clients */
|
||||||
auto outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
static EQApplicationPacket outapp(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||||
PlayerPositionUpdateServer_Struct *position_update = (PlayerPositionUpdateServer_Struct *) outapp->pBuffer;
|
PlayerPositionUpdateServer_Struct *position_update = (PlayerPositionUpdateServer_Struct *) outapp.pBuffer;
|
||||||
|
|
||||||
MakeSpawnUpdate(position_update);
|
MakeSpawnUpdate(position_update);
|
||||||
|
|
||||||
if (gm_hide_me) {
|
if (gm_hide_me) {
|
||||||
entity_list.QueueClientsStatus(this, outapp, true, Admin(), AccountStatus::Max);
|
entity_list.QueueClientsStatus(this, &outapp, true, Admin(), AccountStatus::Max);
|
||||||
} else {
|
} else {
|
||||||
entity_list.QueueCloseClients(this, outapp, true, RuleI(Range, ClientPositionUpdates), nullptr, true);
|
entity_list.QueueCloseClients(this, &outapp, true, RuleI(Range, ClientPositionUpdates), nullptr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -4996,12 +5036,10 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
|
|||||||
Raid *raid = GetRaid();
|
Raid *raid = GetRaid();
|
||||||
|
|
||||||
if (raid) {
|
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) {
|
} 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) {
|
if (zone->watermap) {
|
||||||
@@ -10845,6 +10883,7 @@ void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app)
|
|||||||
InterrogateInventory(this, true, false, true, error);
|
InterrogateInventory(this, true, false, true, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
safe_delete(mi);
|
||||||
}
|
}
|
||||||
// This is the swap.
|
// This is the swap.
|
||||||
// Client behavior is just to move stacks without combining them
|
// Client behavior is just to move stacks without combining them
|
||||||
@@ -16705,13 +16744,12 @@ bool Client::CanTradeFVNoDropItem()
|
|||||||
|
|
||||||
void Client::SendMobPositions()
|
void Client::SendMobPositions()
|
||||||
{
|
{
|
||||||
auto p = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
static EQApplicationPacket p(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||||
auto *s = (PlayerPositionUpdateServer_Struct *) p->pBuffer;
|
auto *s = (PlayerPositionUpdateServer_Struct *) p.pBuffer;
|
||||||
for (auto &m: entity_list.GetMobList()) {
|
for (auto &m: entity_list.GetMobList()) {
|
||||||
m.second->MakeSpawnUpdate(s);
|
m.second->MakeSpawnUpdate(s);
|
||||||
QueuePacket(p, false);
|
QueuePacket(&p, false);
|
||||||
}
|
}
|
||||||
safe_delete(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RecordKillCheck {
|
struct RecordKillCheck {
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ bool Client::Process() {
|
|||||||
|
|
||||||
/* I haven't naturally updated my position in 10 seconds, updating manually */
|
/* I haven't naturally updated my position in 10 seconds, updating manually */
|
||||||
if (!IsMoving() && m_position_update_timer.Check()) {
|
if (!IsMoving() && m_position_update_timer.Check()) {
|
||||||
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
|
BroadcastPositionUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mana_timer.Check())
|
if (mana_timer.Check())
|
||||||
@@ -764,7 +764,7 @@ void Client::BulkSendInventoryItems()
|
|||||||
RemoveNoRent(false);
|
RemoveNoRent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveDuplicateLore(false);
|
RemoveDuplicateLore();
|
||||||
MoveSlotNotAllowed(false);
|
MoveSlotNotAllowed(false);
|
||||||
|
|
||||||
EQ::OutBuffer ob;
|
EQ::OutBuffer ob;
|
||||||
|
|||||||
+151
-37
@@ -1,12 +1,15 @@
|
|||||||
#include "data_bucket.h"
|
#include "data_bucket.h"
|
||||||
#include "entity.h"
|
|
||||||
#include "zonedb.h"
|
#include "zonedb.h"
|
||||||
#include "mob.h"
|
#include "mob.h"
|
||||||
#include "worldserver.h"
|
#include "worldserver.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include "../common/json/json.hpp"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
extern WorldServer worldserver;
|
extern WorldServer worldserver;
|
||||||
|
const std::string NESTED_KEY_DELIMITER = ".";
|
||||||
|
|
||||||
std::vector<DataBucketsRepository::DataBuckets> g_data_bucket_cache = {};
|
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,
|
.key = bucket_key,
|
||||||
.value = bucket_value,
|
.value = bucket_value,
|
||||||
.expires = expires_time,
|
.expires = expires_time,
|
||||||
|
.account_id = 0,
|
||||||
.character_id = 0,
|
.character_id = 0,
|
||||||
.npc_id = 0,
|
.npc_id = 0,
|
||||||
.bot_id = 0
|
.bot_id = 0
|
||||||
@@ -24,8 +28,13 @@ void DataBucket::SetData(const std::string &bucket_key, const std::string &bucke
|
|||||||
DataBucket::SetData(k);
|
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 b = DataBucketsRepository::NewEntity();
|
||||||
auto r = GetData(k, true);
|
auto r = GetData(k, true);
|
||||||
// if we have an entry, use it
|
// if we have an entry, use it
|
||||||
@@ -37,6 +46,9 @@ void DataBucket::SetData(const DataBucketKey &k)
|
|||||||
if (k.character_id > 0) {
|
if (k.character_id > 0) {
|
||||||
b.character_id = k.character_id;
|
b.character_id = k.character_id;
|
||||||
}
|
}
|
||||||
|
else if (k.account_id > 0) {
|
||||||
|
b.account_id = k.account_id;
|
||||||
|
}
|
||||||
else if (k.npc_id > 0) {
|
else if (k.npc_id > 0) {
|
||||||
b.npc_id = k.npc_id;
|
b.npc_id = k.npc_id;
|
||||||
}
|
}
|
||||||
@@ -56,9 +68,48 @@ void DataBucket::SetData(const DataBucketKey &k)
|
|||||||
|
|
||||||
b.expires = expires_time_unix;
|
b.expires = expires_time_unix;
|
||||||
b.value = k.value;
|
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) {
|
if (bucket_id) {
|
||||||
|
|
||||||
// update the cache if it exists
|
// update the cache if it exists
|
||||||
if (CanCache(k)) {
|
if (CanCache(k)) {
|
||||||
for (auto &e: g_data_bucket_cache) {
|
for (auto &e: g_data_bucket_cache) {
|
||||||
@@ -72,7 +123,6 @@ void DataBucket::SetData(const DataBucketKey &k)
|
|||||||
DataBucketsRepository::UpdateOne(database, b);
|
DataBucketsRepository::UpdateOne(database, b);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
b.key_ = k.key;
|
|
||||||
b = DataBucketsRepository::InsertOne(database, b);
|
b = DataBucketsRepository::InsertOne(database, b);
|
||||||
|
|
||||||
// add to cache if it doesn't exist
|
// add to cache if it doesn't exist
|
||||||
@@ -88,23 +138,68 @@ std::string DataBucket::GetData(const std::string &bucket_key)
|
|||||||
return GetData(DataBucketKey{.key = bucket_key}).value;
|
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
|
// 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 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
|
// 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
|
// 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(
|
LogDataBuckets(
|
||||||
"Getting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}]",
|
"Getting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]",
|
||||||
k.key,
|
k.key,
|
||||||
k.bot_id,
|
k.bot_id,
|
||||||
|
k.account_id,
|
||||||
k.character_id,
|
k.character_id,
|
||||||
k.npc_id
|
k.npc_id
|
||||||
);
|
);
|
||||||
|
|
||||||
bool can_cache = CanCache(k);
|
bool can_cache = CanCache(k);
|
||||||
|
|
||||||
// check the cache first if we can cache
|
// Attempt to retrieve the value from the cache
|
||||||
if (can_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 (CheckBucketMatch(e, k)) {
|
||||||
@@ -114,18 +209,18 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
|
|||||||
return DataBucketsRepository::NewEntity();
|
return DataBucketsRepository::NewEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is a bucket miss, return empty entity
|
LogDataBuckets("Returning key [{}] value [{}] from cache", e.key_, e.value);
|
||||||
// we still cache bucket misses, so we don't have to hit the database
|
|
||||||
if (e.id == 0) {
|
if (is_nested_key) {
|
||||||
return DataBucketsRepository::NewEntity();
|
return ExtractNestedValue(e, k_.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDataBuckets("Returning key [{}] value [{}] from cache", e.key_, e.value);
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch the value from the database
|
||||||
auto r = DataBucketsRepository::GetWhere(
|
auto r = DataBucketsRepository::GetWhere(
|
||||||
database,
|
database,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -136,21 +231,17 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (r.empty()) {
|
if (r.empty()) {
|
||||||
|
// Handle cache misses
|
||||||
// if we're ignoring the misses cache, don't add to the cache
|
if (!ignore_misses_cache && can_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) {
|
|
||||||
size_t size_before = g_data_bucket_cache.size();
|
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(
|
g_data_bucket_cache.emplace_back(
|
||||||
DataBucketsRepository::DataBuckets{
|
DataBucketsRepository::DataBuckets{
|
||||||
.id = 0,
|
.id = 0,
|
||||||
.key_ = k.key,
|
.key_ = k.key,
|
||||||
.value = "",
|
.value = "",
|
||||||
.expires = 0,
|
.expires = 0,
|
||||||
|
.account_id = k.account_id,
|
||||||
.character_id = k.character_id,
|
.character_id = k.character_id,
|
||||||
.npc_id = k.npc_id,
|
.npc_id = k.npc_id,
|
||||||
.bot_id = k.bot_id
|
.bot_id = k.bot_id
|
||||||
@@ -158,8 +249,9 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
|
|||||||
);
|
);
|
||||||
|
|
||||||
LogDataBuckets(
|
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.key,
|
||||||
|
k.account_id,
|
||||||
k.character_id,
|
k.character_id,
|
||||||
k.npc_id,
|
k.npc_id,
|
||||||
k.bot_id,
|
k.bot_id,
|
||||||
@@ -168,22 +260,21 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return DataBucketsRepository::NewEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bucket = r.front();
|
auto bucket = r.front();
|
||||||
|
|
||||||
// if the entry has expired, delete it
|
// If the entry has expired, delete it
|
||||||
if (bucket.expires > 0 && bucket.expires < (long long) std::time(nullptr)) {
|
if (bucket.expires > 0 && bucket.expires < static_cast<long long>(std::time(nullptr))) {
|
||||||
DeleteData(k);
|
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) {
|
if (can_cache) {
|
||||||
bool has_cache = false;
|
bool has_cache = false;
|
||||||
|
for (const auto &e : g_data_bucket_cache) {
|
||||||
for (auto &e: g_data_bucket_cache) {
|
|
||||||
if (e.id == bucket.id) {
|
if (e.id == bucket.id) {
|
||||||
has_cache = true;
|
has_cache = true;
|
||||||
break;
|
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;
|
return bucket;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,8 +312,6 @@ bool DataBucket::DeleteData(const std::string &bucket_key)
|
|||||||
// GetDataBuckets bulk loads all data buckets for a mob
|
// GetDataBuckets bulk loads all data buckets for a mob
|
||||||
bool DataBucket::GetDataBuckets(Mob *mob)
|
bool DataBucket::GetDataBuckets(Mob *mob)
|
||||||
{
|
{
|
||||||
DataBucketLoadType::Type t{};
|
|
||||||
|
|
||||||
const uint32 id = mob->GetMobTypeIdentifier();
|
const uint32 id = mob->GetMobTypeIdentifier();
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
@@ -225,14 +319,13 @@ bool DataBucket::GetDataBuckets(Mob *mob)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mob->IsBot()) {
|
if (mob->IsBot()) {
|
||||||
t = DataBucketLoadType::Bot;
|
BulkLoadEntitiesToCache(DataBucketLoadType::Bot, {id});
|
||||||
}
|
}
|
||||||
else if (mob->IsClient()) {
|
else if (mob->IsClient()) {
|
||||||
t = DataBucketLoadType::Client;
|
BulkLoadEntitiesToCache(DataBucketLoadType::Account, {id});
|
||||||
|
BulkLoadEntitiesToCache(DataBucketLoadType::Client, {id});
|
||||||
}
|
}
|
||||||
|
|
||||||
BulkLoadEntitiesToCache(t, {id});
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,9 +347,10 @@ bool DataBucket::DeleteData(const DataBucketKey &k)
|
|||||||
);
|
);
|
||||||
|
|
||||||
LogDataBuckets(
|
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.key,
|
||||||
k.bot_id,
|
k.bot_id,
|
||||||
|
k.account_id,
|
||||||
k.character_id,
|
k.character_id,
|
||||||
k.npc_id,
|
k.npc_id,
|
||||||
size_before,
|
size_before,
|
||||||
@@ -277,9 +371,10 @@ bool DataBucket::DeleteData(const DataBucketKey &k)
|
|||||||
std::string DataBucket::GetDataExpires(const DataBucketKey &k)
|
std::string DataBucket::GetDataExpires(const DataBucketKey &k)
|
||||||
{
|
{
|
||||||
LogDataBuckets(
|
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.key,
|
||||||
k.bot_id,
|
k.bot_id,
|
||||||
|
k.account_id,
|
||||||
k.character_id,
|
k.character_id,
|
||||||
k.npc_id
|
k.npc_id
|
||||||
);
|
);
|
||||||
@@ -295,9 +390,10 @@ std::string DataBucket::GetDataExpires(const DataBucketKey &k)
|
|||||||
std::string DataBucket::GetDataRemaining(const DataBucketKey &k)
|
std::string DataBucket::GetDataRemaining(const DataBucketKey &k)
|
||||||
{
|
{
|
||||||
LogDataBuckets(
|
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.key,
|
||||||
k.bot_id,
|
k.bot_id,
|
||||||
|
k.account_id,
|
||||||
k.character_id,
|
k.character_id,
|
||||||
k.npc_id
|
k.npc_id
|
||||||
);
|
);
|
||||||
@@ -320,6 +416,13 @@ std::string DataBucket::GetScopedDbFilters(const DataBucketKey &k)
|
|||||||
query.emplace_back("character_id = 0");
|
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) {
|
if (k.npc_id > 0) {
|
||||||
query.emplace_back(fmt::format("npc_id = {}", k.npc_id));
|
query.emplace_back(fmt::format("npc_id = {}", k.npc_id));
|
||||||
}
|
}
|
||||||
@@ -346,6 +449,7 @@ bool DataBucket::CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe,
|
|||||||
return (
|
return (
|
||||||
dbe.key_ == k.key &&
|
dbe.key_ == k.key &&
|
||||||
dbe.bot_id == k.bot_id &&
|
dbe.bot_id == k.bot_id &&
|
||||||
|
dbe.account_id == k.account_id &&
|
||||||
dbe.character_id == k.character_id &&
|
dbe.character_id == k.character_id &&
|
||||||
dbe.npc_id == k.npc_id
|
dbe.npc_id == k.npc_id
|
||||||
);
|
);
|
||||||
@@ -364,6 +468,9 @@ void DataBucket::BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector
|
|||||||
if (t == DataBucketLoadType::Bot) {
|
if (t == DataBucketLoadType::Bot) {
|
||||||
has_cache = e.bot_id == ids[0];
|
has_cache = e.bot_id == ids[0];
|
||||||
}
|
}
|
||||||
|
else if (t == DataBucketLoadType::Account) {
|
||||||
|
has_cache = e.account_id == ids[0];
|
||||||
|
}
|
||||||
else if (t == DataBucketLoadType::Client) {
|
else if (t == DataBucketLoadType::Client) {
|
||||||
has_cache = e.character_id == ids[0];
|
has_cache = e.character_id == ids[0];
|
||||||
}
|
}
|
||||||
@@ -384,6 +491,9 @@ void DataBucket::BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector
|
|||||||
case DataBucketLoadType::Client:
|
case DataBucketLoadType::Client:
|
||||||
column = "character_id";
|
column = "character_id";
|
||||||
break;
|
break;
|
||||||
|
case DataBucketLoadType::Account:
|
||||||
|
column = "account_id";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LogError("Incorrect LoadType [{}]", static_cast<int>(t));
|
LogError("Incorrect LoadType [{}]", static_cast<int>(t));
|
||||||
break;
|
break;
|
||||||
@@ -442,6 +552,7 @@ void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id)
|
|||||||
[&](DataBucketsRepository::DataBuckets &e) {
|
[&](DataBucketsRepository::DataBuckets &e) {
|
||||||
return (
|
return (
|
||||||
(type == DataBucketLoadType::Bot && e.bot_id == id) ||
|
(type == DataBucketLoadType::Bot && e.bot_id == id) ||
|
||||||
|
(type == DataBucketLoadType::Account && e.account_id == id) ||
|
||||||
(type == DataBucketLoadType::Client && e.character_id == id)
|
(type == DataBucketLoadType::Client && e.character_id == id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -481,6 +592,7 @@ void DataBucket::DeleteFromMissesCache(DataBucketsRepository::DataBuckets e)
|
|||||||
g_data_bucket_cache.end(),
|
g_data_bucket_cache.end(),
|
||||||
[&](DataBucketsRepository::DataBuckets &ce) {
|
[&](DataBucketsRepository::DataBuckets &ce) {
|
||||||
return ce.id == 0 && ce.key_ == e.key_ &&
|
return ce.id == 0 && ce.key_ == e.key_ &&
|
||||||
|
ce.account_id == e.account_id &&
|
||||||
ce.character_id == e.character_id &&
|
ce.character_id == e.character_id &&
|
||||||
ce.npc_id == e.npc_id &&
|
ce.npc_id == e.npc_id &&
|
||||||
ce.bot_id == e.bot_id;
|
ce.bot_id == e.bot_id;
|
||||||
@@ -516,6 +628,8 @@ void DataBucket::DeleteFromCache(uint64 id, DataBucketLoadType::Type type)
|
|||||||
return e.bot_id == id;
|
return e.bot_id == id;
|
||||||
case DataBucketLoadType::Client:
|
case DataBucketLoadType::Client:
|
||||||
return e.character_id == id;
|
return e.character_id == id;
|
||||||
|
case DataBucketLoadType::Account:
|
||||||
|
return e.account_id == id;
|
||||||
default:
|
default:
|
||||||
return false;
|
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
|
// npcs (ids) can be in multiple zones so we can't cache locally to the zone
|
||||||
bool DataBucket::CanCache(const DataBucketKey &key)
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-5
@@ -12,20 +12,23 @@ struct DataBucketKey {
|
|||||||
std::string key;
|
std::string key;
|
||||||
std::string value;
|
std::string value;
|
||||||
std::string expires;
|
std::string expires;
|
||||||
int64_t character_id;
|
int64_t account_id = 0;
|
||||||
int64_t npc_id;
|
int64_t character_id = 0;
|
||||||
int64_t bot_id;
|
int64_t npc_id = 0;
|
||||||
|
int64_t bot_id = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace DataBucketLoadType {
|
namespace DataBucketLoadType {
|
||||||
enum Type : uint8 {
|
enum Type : uint8 {
|
||||||
Bot,
|
Bot,
|
||||||
|
Account,
|
||||||
Client,
|
Client,
|
||||||
MaxType
|
MaxType
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::string Name[Type::MaxType] = {
|
static const std::string Name[Type::MaxType] = {
|
||||||
"Bot",
|
"Bot",
|
||||||
|
"Account",
|
||||||
"Client",
|
"Client",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -42,9 +45,9 @@ public:
|
|||||||
static bool GetDataBuckets(Mob *mob);
|
static bool GetDataBuckets(Mob *mob);
|
||||||
|
|
||||||
// scoped bucket methods
|
// scoped bucket methods
|
||||||
static void SetData(const DataBucketKey &k);
|
static void SetData(const DataBucketKey &k_);
|
||||||
static bool DeleteData(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 GetDataExpires(const DataBucketKey &k);
|
||||||
static std::string GetDataRemaining(const DataBucketKey &k);
|
static std::string GetDataRemaining(const DataBucketKey &k);
|
||||||
static std::string GetScopedDbFilters(const DataBucketKey &k);
|
static std::string GetScopedDbFilters(const DataBucketKey &k);
|
||||||
@@ -60,6 +63,8 @@ public:
|
|||||||
static void ClearCache();
|
static void ClearCache();
|
||||||
static void DeleteFromCache(uint64 id, DataBucketLoadType::Type type);
|
static void DeleteFromCache(uint64 id, DataBucketLoadType::Type type);
|
||||||
static bool CanCache(const DataBucketKey &key);
|
static bool CanCache(const DataBucketKey &key);
|
||||||
|
static DataBucketsRepository::DataBuckets
|
||||||
|
ExtractNestedValue(const DataBucketsRepository::DataBuckets &bucket, const std::string &full_key);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_DATABUCKET_H
|
#endif //EQEMU_DATABUCKET_H
|
||||||
|
|||||||
+1
-1
@@ -612,7 +612,7 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetOpenType() == 40 && GetZone(GetDoorZone(),0)) {
|
if (GetOpenType() == 40 && sender->GetZoneID() == Zones::CORATHUS) {
|
||||||
sender->SendEvolveXPTransferWindow();
|
sender->SendEvolveXPTransferWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5653,16 +5653,6 @@ int Perl__GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version)
|
|||||||
return zone_store.GetZoneNPCMaximumAggroDistance(zone_id, 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)
|
int8 Perl__GetZoneMinimumExpansion(uint32 zone_id)
|
||||||
{
|
{
|
||||||
return zone_store.GetZoneMinimumExpansion(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("GetZoneMaximumExpansion", (int8(*)(uint32, int))&Perl__GetZoneMaximumExpansion);
|
||||||
package.add("GetZoneMaximumLevel", (uint8(*)(uint32))&Perl__GetZoneMaximumLevel);
|
package.add("GetZoneMaximumLevel", (uint8(*)(uint32))&Perl__GetZoneMaximumLevel);
|
||||||
package.add("GetZoneMaximumLevel", (uint8(*)(uint32, int))&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))&Perl__GetZoneMaximumPlayers);
|
||||||
package.add("GetZoneMaximumPlayers", (int(*)(uint32, int))&Perl__GetZoneMaximumPlayers);
|
package.add("GetZoneMaximumPlayers", (int(*)(uint32, int))&Perl__GetZoneMaximumPlayers);
|
||||||
package.add("GetZoneMinimumClip", (float(*)(uint32))&Perl__GetZoneMinimumClip);
|
package.add("GetZoneMinimumClip", (float(*)(uint32))&Perl__GetZoneMinimumClip);
|
||||||
|
|||||||
+41
-52
@@ -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(
|
void EntityList::QueueCloseClients(
|
||||||
Mob *sender,
|
Mob *sender,
|
||||||
const EQApplicationPacket *app,
|
const EQApplicationPacket *app,
|
||||||
@@ -1742,7 +1733,7 @@ void EntityList::QueueCloseClients(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (distance <= 0) {
|
if (distance <= 0) {
|
||||||
distance = 600;
|
distance = zone->GetClientUpdateRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
float distance_squared = distance * distance;
|
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_close_mobs.erase(entity_id);
|
||||||
|
it->second->m_last_seen_mob_position.erase(entity_id);
|
||||||
|
|
||||||
++it;
|
++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
|
// 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
|
// 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)
|
// entity list (zone wide)
|
||||||
|
|
||||||
|
BenchTimer g_scan_bench_timer;
|
||||||
|
|
||||||
void EntityList::ScanCloseMobs(Mob *scanning_mob)
|
void EntityList::ScanCloseMobs(Mob *scanning_mob)
|
||||||
{
|
{
|
||||||
if (!scanning_mob) {
|
if (!scanning_mob) {
|
||||||
@@ -2941,7 +2937,9 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
|
|||||||
return;
|
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.
|
// 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.
|
// Assuming mob_list.size() as an upper bound for reservation.
|
||||||
@@ -2957,7 +2955,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
|
|||||||
continue;
|
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) {
|
if (distance <= scan_range || mob->GetAggroRange() >= scan_range) {
|
||||||
// add mob to scanning_mob's close list and vice versa
|
// add mob to scanning_mob's close list and vice versa
|
||||||
// check if the mob is already in the close mobs list before inserting
|
// check if the mob is already in the close mobs list before inserting
|
||||||
@@ -2969,10 +2967,11 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
|
|||||||
}
|
}
|
||||||
|
|
||||||
LogAIScanClose(
|
LogAIScanClose(
|
||||||
"[{}] Scanning close list > list_size [{}] moving [{}]",
|
"[{}] Scanning close list > list_size [{}] moving [{}] elapsed [{}] us",
|
||||||
scanning_mob->GetCleanName(),
|
scanning_mob->GetCleanName(),
|
||||||
scanning_mob->m_close_mobs.size(),
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto outapp = new EQApplicationPacket(OP_SendFindableNPCs, sizeof(FindableNPC_Struct));
|
static EQApplicationPacket p(OP_SendFindableNPCs, sizeof(FindableNPC_Struct));
|
||||||
|
auto b = (FindableNPC_Struct*) p.pBuffer;
|
||||||
FindableNPC_Struct *fnpcs = (FindableNPC_Struct *)outapp->pBuffer;
|
b->Unknown109 = 0x16;
|
||||||
|
b->Unknown110 = 0x06;
|
||||||
fnpcs->Unknown109 = 0x16;
|
b->Unknown111 = 0x24;
|
||||||
fnpcs->Unknown110 = 0x06;
|
b->Action = 0;
|
||||||
fnpcs->Unknown111 = 0x24;
|
|
||||||
|
|
||||||
fnpcs->Action = 0;
|
|
||||||
|
|
||||||
auto it = npc_list.begin();
|
auto it = npc_list.begin();
|
||||||
while (it != npc_list.end()) {
|
while (it != npc_list.end()) {
|
||||||
@@ -5336,50 +5332,47 @@ void EntityList::SendFindableNPCList(Client *c)
|
|||||||
NPC *n = it->second;
|
NPC *n = it->second;
|
||||||
|
|
||||||
if (n->IsFindable()) {
|
if (n->IsFindable()) {
|
||||||
fnpcs->EntityID = n->GetID();
|
b->EntityID = n->GetID();
|
||||||
strn0cpy(fnpcs->Name, n->GetCleanName(), sizeof(fnpcs->Name));
|
strn0cpy(b->Name, n->GetCleanName(), sizeof(b->Name));
|
||||||
strn0cpy(fnpcs->LastName, n->GetLastName(), sizeof(fnpcs->LastName));
|
strn0cpy(b->LastName, n->GetLastName(), sizeof(b->LastName));
|
||||||
fnpcs->Race = n->GetRace();
|
b->Race = n->GetRace();
|
||||||
fnpcs->Class = n->GetClass();
|
b->Class = n->GetClass();
|
||||||
|
|
||||||
c->QueuePacket(outapp);
|
c->QueuePacket(&p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
safe_delete(outapp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityList::UpdateFindableNPCState(NPC *n, bool Remove)
|
void EntityList::UpdateFindableNPCState(NPC *n, bool Remove)
|
||||||
{
|
{
|
||||||
if (!n || !n->IsFindable())
|
if (!n || !n->IsFindable()) {
|
||||||
return;
|
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;
|
b->Action = Remove ? 1 : 0;
|
||||||
|
b->EntityID = n->GetID();
|
||||||
fnpcs->Unknown109 = 0x16;
|
strn0cpy(b->Name, n->GetCleanName(), sizeof(b->Name));
|
||||||
fnpcs->Unknown110 = 0x06;
|
strn0cpy(b->LastName, n->GetLastName(), sizeof(b->LastName));
|
||||||
fnpcs->Unknown111 = 0x24;
|
b->Race = n->GetRace();
|
||||||
|
b->Class = n->GetClass();
|
||||||
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();
|
|
||||||
|
|
||||||
auto it = client_list.begin();
|
auto it = client_list.begin();
|
||||||
while (it != client_list.end()) {
|
while (it != client_list.end()) {
|
||||||
Client *c = it->second;
|
Client *c = it->second;
|
||||||
if (c && (c->ClientVersion() >= EQ::versions::ClientVersion::SoD))
|
if (c && (c->ClientVersion() >= EQ::versions::ClientVersion::SoD)) {
|
||||||
c->QueuePacket(outapp);
|
c->QueuePacket(&p);
|
||||||
|
}
|
||||||
|
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
safe_delete(outapp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityList::HideCorpses(Client *c, uint8 CurrentMode, uint8 NewMode)
|
void EntityList::HideCorpses(Client *c, uint8 CurrentMode, uint8 NewMode)
|
||||||
@@ -5759,10 +5752,6 @@ void EntityList::ReloadMerchants() {
|
|||||||
* then we return the full list
|
* then we return the full list
|
||||||
*
|
*
|
||||||
* See comments @EntityList::ScanCloseMobs for system explanation
|
* See comments @EntityList::ScanCloseMobs for system explanation
|
||||||
*
|
|
||||||
* @param mob
|
|
||||||
* @param distance
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
std::unordered_map<uint16, Mob *> &EntityList::GetCloseMobList(Mob *mob, float distance)
|
std::unordered_map<uint16, Mob *> &EntityList::GetCloseMobList(Mob *mob, float distance)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -631,8 +631,6 @@ private:
|
|||||||
bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate
|
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 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);
|
void GetBotList(std::list<Bot*> &b_list);
|
||||||
private:
|
private:
|
||||||
std::list<Bot*> bot_list;
|
std::list<Bot*> bot_list;
|
||||||
|
|||||||
@@ -9,6 +9,19 @@ void command_loc(Client *c, const Seperator *sep)
|
|||||||
|
|
||||||
auto target_position = target->GetPosition();
|
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(
|
c->Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
|
|||||||
+59
-86
@@ -2979,91 +2979,48 @@ void Client::RemoveNoRent(bool client_update)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Two new methods to alleviate perpetual login desyncs
|
// 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) {
|
for (auto slot_id : GetInventorySlots()) {
|
||||||
if ((((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask) == 0)
|
if ((((uint64) 1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask) == 0) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
auto inst = m_inv.PopItem(slot_id);
|
||||||
if (inst == nullptr) { continue; }
|
if (!inst) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (CheckLoreConflict(inst->GetItem())) {
|
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);
|
database.SaveInventory(character_id, nullptr, slot_id);
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_inv.PutItem(slot_id, *inst);
|
|
||||||
}
|
|
||||||
safe_delete(inst);
|
safe_delete(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto slot_id = EQ::invslot::GENERAL_BEGIN; slot_id <= EQ::invslot::GENERAL_END; ++slot_id) {
|
// if no lore conflict, put the item back in the slot
|
||||||
if ((((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask) == 0)
|
m_inv.PushItem(slot_id, inst);
|
||||||
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::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()) {
|
if (!m_inv.CursorEmpty()) {
|
||||||
std::list<EQ::ItemInstance*> local_1;
|
std::list<EQ::ItemInstance*> local_1;
|
||||||
@@ -3071,15 +3028,23 @@ void Client::RemoveDuplicateLore(bool client_update)
|
|||||||
|
|
||||||
while (!m_inv.CursorEmpty()) {
|
while (!m_inv.CursorEmpty()) {
|
||||||
auto inst = m_inv.PopItem(EQ::invslot::slotCursor);
|
auto inst = m_inv.PopItem(EQ::invslot::slotCursor);
|
||||||
if (inst == nullptr) { continue; }
|
if (!inst) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
local_1.push_back(inst);
|
local_1.push_back(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto iter = local_1.begin(); iter != local_1.end(); ++iter) {
|
for (auto inst: local_1) {
|
||||||
auto inst = *iter;
|
if (!inst) {
|
||||||
if (inst == nullptr) { continue; }
|
continue;
|
||||||
|
}
|
||||||
if (CheckLoreConflict(inst->GetItem())) {
|
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);
|
safe_delete(inst);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -3088,17 +3053,25 @@ void Client::RemoveDuplicateLore(bool client_update)
|
|||||||
}
|
}
|
||||||
local_1.clear();
|
local_1.clear();
|
||||||
|
|
||||||
for (auto iter = local_2.begin(); iter != local_2.end(); ++iter) {
|
for (auto inst: local_2) {
|
||||||
auto inst = *iter;
|
if (!inst) {
|
||||||
if (inst == nullptr) { continue; }
|
continue;
|
||||||
|
}
|
||||||
if (!inst->GetItem()->LoreFlag ||
|
if (!inst->GetItem()->LoreFlag ||
|
||||||
((inst->GetItem()->LoreGroup == -1) && (m_inv.HasItem(inst->GetID(), 0, invWhereCursor) == INVALID_INDEX)) ||
|
((inst->GetItem()->LoreGroup == -1) &&
|
||||||
(inst->GetItem()->LoreGroup && (~inst->GetItem()->LoreGroup) && (m_inv.HasItemByLoreGroup(inst->GetItem()->LoreGroup, invWhereCursor) == INVALID_INDEX))
|
(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);
|
m_inv.PushCursor(*inst);
|
||||||
}
|
}
|
||||||
else {
|
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);
|
safe_delete(inst);
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -687,6 +687,7 @@ void NPC::RemoveItem(uint32 item_id, uint16 quantity, uint16 slot)
|
|||||||
LootItem *item = *cur;
|
LootItem *item = *cur;
|
||||||
if (item->item_id == item_id && slot <= 0 && quantity <= 0) {
|
if (item->item_id == item_id && slot <= 0 && quantity <= 0) {
|
||||||
m_loot_items.erase(cur);
|
m_loot_items.erase(cur);
|
||||||
|
safe_delete(item);
|
||||||
UpdateEquipmentLight();
|
UpdateEquipmentLight();
|
||||||
if (UpdateActiveLight()) { SendAppearancePacket(AppearanceType::Light, GetActiveLightType()); }
|
if (UpdateActiveLight()) { SendAppearancePacket(AppearanceType::Light, GetActiveLightType()); }
|
||||||
return;
|
return;
|
||||||
@@ -695,7 +696,10 @@ void NPC::RemoveItem(uint32 item_id, uint16 quantity, uint16 slot)
|
|||||||
if (item->charges <= quantity) {
|
if (item->charges <= quantity) {
|
||||||
m_loot_items.erase(cur);
|
m_loot_items.erase(cur);
|
||||||
UpdateEquipmentLight();
|
UpdateEquipmentLight();
|
||||||
if (UpdateActiveLight()) { SendAppearancePacket(AppearanceType::Light, GetActiveLightType()); }
|
if (UpdateActiveLight()) {
|
||||||
|
SendAppearancePacket(AppearanceType::Light, GetActiveLightType());
|
||||||
|
}
|
||||||
|
safe_delete(item);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
item->charges -= quantity;
|
item->charges -= quantity;
|
||||||
|
|||||||
@@ -3458,12 +3458,54 @@ void Lua_Client::ShowZoneShardMenu()
|
|||||||
self->ShowZoneShardMenu();
|
self->ShowZoneShardMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Lua_Client::GrantPetNameChange()
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->GrantPetNameChange();
|
||||||
|
}
|
||||||
|
|
||||||
void Lua_Client::SetAAEXPPercentage(uint8 percentage)
|
void Lua_Client::SetAAEXPPercentage(uint8 percentage)
|
||||||
{
|
{
|
||||||
Lua_Safe_Call_Void();
|
Lua_Safe_Call_Void();
|
||||||
self->SetAAEXPPercentage(percentage);
|
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() {
|
luabind::scope lua_register_client() {
|
||||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||||
.def(luabind::constructor<>())
|
.def(luabind::constructor<>())
|
||||||
@@ -3532,6 +3574,7 @@ luabind::scope lua_register_client() {
|
|||||||
.def("CanHaveSkill", (bool(Lua_Client::*)(int))&Lua_Client::CanHaveSkill)
|
.def("CanHaveSkill", (bool(Lua_Client::*)(int))&Lua_Client::CanHaveSkill)
|
||||||
.def("CashReward", &Lua_Client::CashReward)
|
.def("CashReward", &Lua_Client::CashReward)
|
||||||
.def("ChangeLastName", (void(Lua_Client::*)(std::string))&Lua_Client::ChangeLastName)
|
.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("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))&Lua_Client::CheckIncreaseSkill)
|
||||||
.def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob,int))&Lua_Client::CheckIncreaseSkill)
|
.def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob,int))&Lua_Client::CheckIncreaseSkill)
|
||||||
@@ -3552,6 +3595,7 @@ luabind::scope lua_register_client() {
|
|||||||
.def("CreateTaskDynamicZone", &Lua_Client::CreateTaskDynamicZone)
|
.def("CreateTaskDynamicZone", &Lua_Client::CreateTaskDynamicZone)
|
||||||
.def("DecreaseByID", (bool(Lua_Client::*)(uint32,int))&Lua_Client::DecreaseByID)
|
.def("DecreaseByID", (bool(Lua_Client::*)(uint32,int))&Lua_Client::DecreaseByID)
|
||||||
.def("DescribeSpecialAbilities", (void(Lua_Client::*)(Lua_NPC))&Lua_Client::DescribeSpecialAbilities)
|
.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("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))&Lua_Client::DeleteItemInInventory)
|
||||||
.def("DeleteItemInInventory", (void(Lua_Client::*)(int,int,bool))&Lua_Client::DeleteItemInInventory)
|
.def("DeleteItemInInventory", (void(Lua_Client::*)(int,int,bool))&Lua_Client::DeleteItemInInventory)
|
||||||
@@ -3630,6 +3674,9 @@ luabind::scope lua_register_client() {
|
|||||||
.def("GetBotRequiredLevel", (int(Lua_Client::*)(uint8))&Lua_Client::GetBotRequiredLevel)
|
.def("GetBotRequiredLevel", (int(Lua_Client::*)(uint8))&Lua_Client::GetBotRequiredLevel)
|
||||||
.def("GetBotSpawnLimit", (int(Lua_Client::*)(void))&Lua_Client::GetBotSpawnLimit)
|
.def("GetBotSpawnLimit", (int(Lua_Client::*)(void))&Lua_Client::GetBotSpawnLimit)
|
||||||
.def("GetBotSpawnLimit", (int(Lua_Client::*)(uint8))&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("GetBucket", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucket)
|
||||||
.def("GetBucketExpires", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucketExpires)
|
.def("GetBucketExpires", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucketExpires)
|
||||||
.def("GetBucketRemaining", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucketRemaining)
|
.def("GetBucketRemaining", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucketRemaining)
|
||||||
@@ -3922,6 +3969,8 @@ luabind::scope lua_register_client() {
|
|||||||
.def("SetBotRequiredLevel", (void(Lua_Client::*)(int,uint8))&Lua_Client::SetBotRequiredLevel)
|
.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))&Lua_Client::SetBotSpawnLimit)
|
||||||
.def("SetBotSpawnLimit", (void(Lua_Client::*)(int,uint8))&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))&Lua_Client::SetBucket)
|
||||||
.def("SetBucket", (void(Lua_Client::*)(std::string,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)
|
.def("SetClientMaxLevel", (void(Lua_Client::*)(int))&Lua_Client::SetClientMaxLevel)
|
||||||
|
|||||||
@@ -512,6 +512,14 @@ public:
|
|||||||
luabind::object GetInventorySlots(lua_State* L);
|
luabind::object GetInventorySlots(lua_State* L);
|
||||||
void SetAAEXPPercentage(uint8 percentage);
|
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);
|
||||||
void ApplySpell(int spell_id, int duration);
|
void ApplySpell(int spell_id, int duration);
|
||||||
void ApplySpell(int spell_id, int duration, int level);
|
void ApplySpell(int spell_id, int duration, int level);
|
||||||
@@ -589,6 +597,7 @@ public:
|
|||||||
|
|
||||||
bool ReloadDataBuckets();
|
bool ReloadDataBuckets();
|
||||||
void ShowZoneShardMenu();
|
void ShowZoneShardMenu();
|
||||||
|
void GrantPetNameChange();
|
||||||
|
|
||||||
Lua_Expedition CreateExpedition(luabind::object expedition_info);
|
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);
|
Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players);
|
||||||
|
|||||||
@@ -4668,16 +4668,6 @@ int lua_get_zone_npc_maximum_aggro_distance(uint32 zone_id, int version)
|
|||||||
return zone_store.GetZoneNPCMaximumAggroDistance(zone_id, 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)
|
int8 lua_get_zone_minimum_expansion(uint32 zone_id)
|
||||||
{
|
{
|
||||||
return zone_store.GetZoneMinimumExpansion(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_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))&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_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))&lua_get_zone_minimum_expansion),
|
||||||
luabind::def("get_zone_minimum_expansion", (int8(*)(uint32,int))&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),
|
luabind::def("get_zone_maximum_expansion", (int8(*)(uint32))&lua_get_zone_maximum_expansion),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "raycast_mesh.h"
|
#include "raycast_mesh.h"
|
||||||
#include "zone.h"
|
#include "zone.h"
|
||||||
#include "../common/file.h"
|
#include "../common/file.h"
|
||||||
|
#include "../common/memory/ksm.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -953,6 +954,7 @@ bool Map::LoadV2(FILE *f) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Map::RotateVertex(glm::vec3 &v, float rx, float ry, float rz) {
|
void Map::RotateVertex(glm::vec3 &v, float rx, float ry, float rz) {
|
||||||
glm::vec3 nv = v;
|
glm::vec3 nv = v;
|
||||||
|
|
||||||
|
|||||||
+23
-31
@@ -125,10 +125,11 @@ Mob::Mob(
|
|||||||
tmHidden(-1),
|
tmHidden(-1),
|
||||||
mitigation_ac(0),
|
mitigation_ac(0),
|
||||||
m_specialattacks(eSpecialAttacks::None),
|
m_specialattacks(eSpecialAttacks::None),
|
||||||
attack_anim_timer(500),
|
attack_anim_timer(100),
|
||||||
position_update_melee_push_timer(500),
|
position_update_melee_push_timer(500),
|
||||||
hate_list_cleanup_timer(6000),
|
hate_list_cleanup_timer(6000),
|
||||||
m_scan_close_mobs_timer(6000),
|
m_scan_close_mobs_timer(6000),
|
||||||
|
m_see_close_mobs_timer(1000),
|
||||||
m_mob_check_moving_timer(1000),
|
m_mob_check_moving_timer(1000),
|
||||||
bot_attack_flag_timer(10000)
|
bot_attack_flag_timer(10000)
|
||||||
{
|
{
|
||||||
@@ -1521,16 +1522,12 @@ void Mob::SendHPUpdate(bool force_update_all)
|
|||||||
last_hp
|
last_hp
|
||||||
);
|
);
|
||||||
|
|
||||||
auto client_packet = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct));
|
static EQApplicationPacket p(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct));
|
||||||
auto *hp_packet_client = (SpawnHPUpdate_Struct *) client_packet->pBuffer;
|
auto b = (SpawnHPUpdate_Struct*) p.pBuffer;
|
||||||
|
b->cur_hp = static_cast<uint32>(CastToClient()->GetHP() - itembonuses.HP);
|
||||||
hp_packet_client->cur_hp = static_cast<uint32>(CastToClient()->GetHP() - itembonuses.HP);
|
b->spawn_id = GetID();
|
||||||
hp_packet_client->spawn_id = GetID();
|
b->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP;
|
||||||
hp_packet_client->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP;
|
CastToClient()->QueuePacket(&p);
|
||||||
|
|
||||||
CastToClient()->QueuePacket(client_packet);
|
|
||||||
|
|
||||||
safe_delete(client_packet);
|
|
||||||
|
|
||||||
ResetHPUpdateTimer();
|
ResetHPUpdateTimer();
|
||||||
|
|
||||||
@@ -3539,24 +3536,21 @@ void Mob::DoAnim(const int animation_id, int animation_speed, bool ackreq, eqFil
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto outapp = new EQApplicationPacket(OP_Animation, sizeof(Animation_Struct));
|
static EQApplicationPacket p(OP_Animation, sizeof(Animation_Struct));
|
||||||
auto *a = (Animation_Struct *) outapp->pBuffer;
|
auto a = (Animation_Struct*) p.pBuffer;
|
||||||
|
|
||||||
a->spawnid = GetID();
|
a->spawnid = GetID();
|
||||||
a->action = animation_id;
|
a->action = animation_id;
|
||||||
a->speed = animation_speed ? animation_speed : 10;
|
a->speed = animation_speed ? animation_speed : 10;
|
||||||
|
|
||||||
entity_list.QueueCloseClients(
|
entity_list.QueueCloseClients(
|
||||||
this, /* Sender */
|
this, /* Sender */
|
||||||
outapp, /* Packet */
|
&p, /* Packet */
|
||||||
false, /* Ignore Sender */
|
false, /* Ignore Sender */
|
||||||
RuleI(Range, Anims),
|
RuleI(Range, Anims),
|
||||||
0, /* Skip this mob */
|
0, /* Skip this mob */
|
||||||
ackreq, /* Packet ACK */
|
ackreq, /* Packet ACK */
|
||||||
filter /* eqFilterType filter */
|
filter /* eqFilterType filter */
|
||||||
);
|
);
|
||||||
|
|
||||||
safe_delete(outapp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mob::ShowBuffs(Client* c) {
|
void Mob::ShowBuffs(Client* c) {
|
||||||
@@ -7716,28 +7710,26 @@ bool Mob::CanRaceEquipItem(uint32 item_id)
|
|||||||
|
|
||||||
void Mob::SendAddPlayerState(PlayerState new_state)
|
void Mob::SendAddPlayerState(PlayerState new_state)
|
||||||
{
|
{
|
||||||
auto app = new EQApplicationPacket(OP_PlayerStateAdd, sizeof(PlayerState_Struct));
|
static EQApplicationPacket p(OP_PlayerStateAdd, sizeof(PlayerState_Struct));
|
||||||
auto ps = (PlayerState_Struct *)app->pBuffer;
|
auto b = (PlayerState_Struct *) p.pBuffer;
|
||||||
|
|
||||||
ps->spawn_id = GetID();
|
b->spawn_id = GetID();
|
||||||
ps->state = static_cast<uint32>(new_state);
|
b->state = static_cast<uint32>(new_state);
|
||||||
|
|
||||||
AddPlayerState(ps->state);
|
AddPlayerState(b->state);
|
||||||
entity_list.QueueClients(nullptr, app);
|
entity_list.QueueClients(nullptr, &p);
|
||||||
safe_delete(app);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mob::SendRemovePlayerState(PlayerState old_state)
|
void Mob::SendRemovePlayerState(PlayerState old_state)
|
||||||
{
|
{
|
||||||
auto app = new EQApplicationPacket(OP_PlayerStateRemove, sizeof(PlayerState_Struct));
|
static EQApplicationPacket p(OP_PlayerStateRemove, sizeof(PlayerState_Struct));
|
||||||
auto ps = (PlayerState_Struct *)app->pBuffer;
|
auto b = (PlayerState_Struct *) p.pBuffer;
|
||||||
|
|
||||||
ps->spawn_id = GetID();
|
b->spawn_id = GetID();
|
||||||
ps->state = static_cast<uint32>(old_state);
|
b->state = static_cast<uint32>(old_state);
|
||||||
|
|
||||||
RemovePlayerState(ps->state);
|
RemovePlayerState(b->state);
|
||||||
entity_list.QueueClients(nullptr, app);
|
entity_list.QueueClients(nullptr, &p);
|
||||||
safe_delete(app);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 Mob::GetMeleeMitigation() {
|
int32 Mob::GetMeleeMitigation() {
|
||||||
|
|||||||
@@ -202,7 +202,9 @@ public:
|
|||||||
void DisplayInfo(Mob *mob);
|
void DisplayInfo(Mob *mob);
|
||||||
|
|
||||||
std::unordered_map<uint16, Mob *> m_close_mobs;
|
std::unordered_map<uint16, Mob *> m_close_mobs;
|
||||||
|
std::unordered_map<int, glm::vec4> m_last_seen_mob_position;
|
||||||
Timer m_scan_close_mobs_timer;
|
Timer m_scan_close_mobs_timer;
|
||||||
|
Timer m_see_close_mobs_timer;
|
||||||
Timer m_mob_check_moving_timer;
|
Timer m_mob_check_moving_timer;
|
||||||
|
|
||||||
// Bot attack flag
|
// Bot attack flag
|
||||||
|
|||||||
@@ -824,8 +824,8 @@ void MobMovementManager::SendCommandToClients(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EQApplicationPacket outapp(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
static EQApplicationPacket p(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||||
auto *spu = (PlayerPositionUpdateServer_Struct *) outapp.pBuffer;
|
auto *spu = (PlayerPositionUpdateServer_Struct *) p.pBuffer;
|
||||||
|
|
||||||
FillCommandStruct(spu, mob, delta_x, delta_y, delta_z, delta_heading, anim);
|
FillCommandStruct(spu, mob, delta_x, delta_y, delta_z, delta_heading, anim);
|
||||||
|
|
||||||
@@ -851,12 +851,24 @@ void MobMovementManager::SendCommandToClients(
|
|||||||
_impl->Stats.TotalSentPosition++;
|
_impl->Stats.TotalSentPosition++;
|
||||||
}
|
}
|
||||||
|
|
||||||
c->QueuePacket(&outapp, false);
|
if (c->m_last_seen_mob_position.contains(mob->GetID())) {
|
||||||
|
if (c->m_last_seen_mob_position[mob->GetID()] == mob->GetPosition() && anim == 0) {
|
||||||
|
LogPositionUpdate(
|
||||||
|
"Mob [{}] has already been sent to client [{}] at this position, skipping",
|
||||||
|
mob->GetCleanName(),
|
||||||
|
c->GetCleanName()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c->QueuePacket(&p, false);
|
||||||
|
c->m_last_seen_mob_position[mob->GetID()] = mob->GetPosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
float short_range = RuleR(Pathing, ShortMovementUpdateRange);
|
float short_range = RuleR(Pathing, ShortMovementUpdateRange);
|
||||||
float long_range = zone->GetNpcPositionUpdateDistance();
|
float long_range = RuleI(Range, MobCloseScanDistance);
|
||||||
|
|
||||||
for (auto &c : _impl->Clients) {
|
for (auto &c : _impl->Clients) {
|
||||||
if (single_client && c != single_client) {
|
if (single_client && c != single_client) {
|
||||||
@@ -901,7 +913,19 @@ void MobMovementManager::SendCommandToClients(
|
|||||||
_impl->Stats.TotalSentPosition++;
|
_impl->Stats.TotalSentPosition++;
|
||||||
}
|
}
|
||||||
|
|
||||||
c->QueuePacket(&outapp, false);
|
if (c->m_last_seen_mob_position.contains(mob->GetID())) {
|
||||||
|
if (c->m_last_seen_mob_position[mob->GetID()] == mob->GetPosition() && anim == 0) {
|
||||||
|
LogPositionUpdate(
|
||||||
|
"Mob [{}] has already been sent to client [{}] at this position, skipping",
|
||||||
|
mob->GetCleanName(),
|
||||||
|
c->GetCleanName()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c->QueuePacket(&p, false);
|
||||||
|
c->m_last_seen_mob_position[mob->GetID()] = mob->GetPosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-6
@@ -997,8 +997,8 @@ NPC * NPC::SpawnNodeNPC(std::string name, std::string last_name, const glm::vec4
|
|||||||
|
|
||||||
npc_type->current_hp = 4000000;
|
npc_type->current_hp = 4000000;
|
||||||
npc_type->max_hp = 4000000;
|
npc_type->max_hp = 4000000;
|
||||||
npc_type->race = 2254;
|
npc_type->race = 127;
|
||||||
npc_type->gender = 2;
|
npc_type->gender = 0;
|
||||||
npc_type->class_ = 9;
|
npc_type->class_ = 9;
|
||||||
npc_type->deity = 1;
|
npc_type->deity = 1;
|
||||||
npc_type->level = 200;
|
npc_type->level = 200;
|
||||||
@@ -3824,13 +3824,12 @@ int NPC::GetRolledItemCount(uint32 item_id)
|
|||||||
|
|
||||||
void NPC::SendPositionToClients()
|
void NPC::SendPositionToClients()
|
||||||
{
|
{
|
||||||
auto p = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
static EQApplicationPacket p(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||||
auto *s = (PlayerPositionUpdateServer_Struct *) p->pBuffer;
|
auto *s = (PlayerPositionUpdateServer_Struct *) p.pBuffer;
|
||||||
for (auto &c: entity_list.GetClientList()) {
|
for (auto &c: entity_list.GetClientList()) {
|
||||||
MakeSpawnUpdate(s);
|
MakeSpawnUpdate(s);
|
||||||
c.second->QueuePacket(p, false);
|
c.second->QueuePacket(&p, false);
|
||||||
}
|
}
|
||||||
safe_delete(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NPC::HandleRoambox()
|
void NPC::HandleRoambox()
|
||||||
|
|||||||
@@ -3229,11 +3229,46 @@ perl::array Perl_Client_GetInventorySlots(Client* self)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Perl_Client_GrantPetNameChange(Client* self)
|
||||||
|
{
|
||||||
|
self->GrantPetNameChange();
|
||||||
|
}
|
||||||
|
|
||||||
void Perl_Client_SetAAEXPPercentage(Client* self, uint8 percentage)
|
void Perl_Client_SetAAEXPPercentage(Client* self, uint8 percentage)
|
||||||
{
|
{
|
||||||
self->SetAAEXPPercentage(percentage);
|
self->SetAAEXPPercentage(percentage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Perl_Client_SetAccountBucket(Client* self, std::string bucket_name, std::string bucket_value)
|
||||||
|
{
|
||||||
|
self->SetAccountBucket(bucket_name, bucket_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Perl_Client_SetAccountBucket(Client* self, std::string bucket_name, std::string bucket_value, std::string expiration = "")
|
||||||
|
{
|
||||||
|
self->SetAccountBucket(bucket_name, bucket_value, expiration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Perl_Client_DeleteAccountBucket(Client* self, std::string bucket_name)
|
||||||
|
{
|
||||||
|
self->DeleteAccountBucket(bucket_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Perl_Client_GetAccountBucket(Client* self, std::string bucket_name)
|
||||||
|
{
|
||||||
|
return self->GetAccountBucket(bucket_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Perl_Client_GetAccountBucketExpires(Client* self, std::string bucket_name)
|
||||||
|
{
|
||||||
|
return self->GetAccountBucketExpires(bucket_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Perl_Client_GetAccountBucketRemaining(Client* self, std::string bucket_name)
|
||||||
|
{
|
||||||
|
return self->GetAccountBucketRemaining(bucket_name);
|
||||||
|
}
|
||||||
|
|
||||||
void perl_register_client()
|
void perl_register_client()
|
||||||
{
|
{
|
||||||
perl::interpreter perl(PERL_GET_THX);
|
perl::interpreter perl(PERL_GET_THX);
|
||||||
@@ -3305,6 +3340,7 @@ void perl_register_client()
|
|||||||
package.add("CanHaveSkill", &Perl_Client_CanHaveSkill);
|
package.add("CanHaveSkill", &Perl_Client_CanHaveSkill);
|
||||||
package.add("CashReward", &Perl_Client_CashReward);
|
package.add("CashReward", &Perl_Client_CashReward);
|
||||||
package.add("ChangeLastName", &Perl_Client_ChangeLastName);
|
package.add("ChangeLastName", &Perl_Client_ChangeLastName);
|
||||||
|
package.add("GrantPetNameChange", &Perl_Client_GrantPetNameChange);
|
||||||
package.add("CharacterID", &Perl_Client_CharacterID);
|
package.add("CharacterID", &Perl_Client_CharacterID);
|
||||||
package.add("CheckIncreaseSkill", (bool(*)(Client*, int))&Perl_Client_CheckIncreaseSkill);
|
package.add("CheckIncreaseSkill", (bool(*)(Client*, int))&Perl_Client_CheckIncreaseSkill);
|
||||||
package.add("CheckIncreaseSkill", (bool(*)(Client*, int, int))&Perl_Client_CheckIncreaseSkill);
|
package.add("CheckIncreaseSkill", (bool(*)(Client*, int, int))&Perl_Client_CheckIncreaseSkill);
|
||||||
@@ -3325,6 +3361,7 @@ void perl_register_client()
|
|||||||
package.add("CreateTaskDynamicZone", &Perl_Client_CreateTaskDynamicZone);
|
package.add("CreateTaskDynamicZone", &Perl_Client_CreateTaskDynamicZone);
|
||||||
package.add("DecreaseByID", &Perl_Client_DecreaseByID);
|
package.add("DecreaseByID", &Perl_Client_DecreaseByID);
|
||||||
package.add("DescribeSpecialAbilities", &Perl_Client_DescribeSpecialAbilities);
|
package.add("DescribeSpecialAbilities", &Perl_Client_DescribeSpecialAbilities);
|
||||||
|
package.add("DeleteAccountBucket", &Perl_Client_DeleteAccountBucket);
|
||||||
package.add("DeleteItemInInventory", (void(*)(Client*, int16))&Perl_Client_DeleteItemInInventory);
|
package.add("DeleteItemInInventory", (void(*)(Client*, int16))&Perl_Client_DeleteItemInInventory);
|
||||||
package.add("DeleteItemInInventory", (void(*)(Client*, int16, int16))&Perl_Client_DeleteItemInInventory);
|
package.add("DeleteItemInInventory", (void(*)(Client*, int16, int16))&Perl_Client_DeleteItemInInventory);
|
||||||
package.add("DeleteItemInInventory", (void(*)(Client*, int16, int16, bool))&Perl_Client_DeleteItemInInventory);
|
package.add("DeleteItemInInventory", (void(*)(Client*, int16, int16, bool))&Perl_Client_DeleteItemInInventory);
|
||||||
@@ -3362,6 +3399,9 @@ void perl_register_client()
|
|||||||
package.add("GetAAPoints", &Perl_Client_GetAAPoints);
|
package.add("GetAAPoints", &Perl_Client_GetAAPoints);
|
||||||
package.add("GetAFK", &Perl_Client_GetAFK);
|
package.add("GetAFK", &Perl_Client_GetAFK);
|
||||||
package.add("GetAccountAge", &Perl_Client_GetAccountAge);
|
package.add("GetAccountAge", &Perl_Client_GetAccountAge);
|
||||||
|
package.add("GetAccountBucket", &Perl_Client_GetAccountBucket);
|
||||||
|
package.add("GetAccountBucketExpires", &Perl_Client_GetAccountBucketExpires);
|
||||||
|
package.add("GetGetAccountBucketRemaining", &Perl_Client_GetAccountBucketRemaining);
|
||||||
package.add("GetAccountFlag", &Perl_Client_GetAccountFlag);
|
package.add("GetAccountFlag", &Perl_Client_GetAccountFlag);
|
||||||
package.add("GetAccountFlags", &Perl_Client_GetAccountFlags);
|
package.add("GetAccountFlags", &Perl_Client_GetAccountFlags);
|
||||||
package.add("GetAggroCount", &Perl_Client_GetAggroCount);
|
package.add("GetAggroCount", &Perl_Client_GetAggroCount);
|
||||||
@@ -3668,6 +3708,8 @@ void perl_register_client()
|
|||||||
package.add("SetAATitle", (void(*)(Client*, std::string, bool))&Perl_Client_SetAATitle);
|
package.add("SetAATitle", (void(*)(Client*, std::string, bool))&Perl_Client_SetAATitle);
|
||||||
package.add("SetAFK", &Perl_Client_SetAFK);
|
package.add("SetAFK", &Perl_Client_SetAFK);
|
||||||
package.add("SetAccountFlag", &Perl_Client_SetAccountFlag);
|
package.add("SetAccountFlag", &Perl_Client_SetAccountFlag);
|
||||||
|
package.add("SetAccountBucket", (void(*)(Client*, std::string, std::string))&Perl_Client_SetAccountBucket);
|
||||||
|
package.add("SetAccountBucket", (void(*)(Client*, std::string, std::string, std::string))&Perl_Client_SetAccountBucket);
|
||||||
package.add("SetAlternateCurrencyValue", &Perl_Client_SetAlternateCurrencyValue);
|
package.add("SetAlternateCurrencyValue", &Perl_Client_SetAlternateCurrencyValue);
|
||||||
package.add("SetAnon", &Perl_Client_SetAnon);
|
package.add("SetAnon", &Perl_Client_SetAnon);
|
||||||
package.add("SetAutoLoginCharacterName", (bool(*)(Client*))&Perl_Client_SetAutoLoginCharacterName);
|
package.add("SetAutoLoginCharacterName", (bool(*)(Client*))&Perl_Client_SetAutoLoginCharacterName);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "../common/repositories/pets_repository.h"
|
#include "../common/repositories/pets_repository.h"
|
||||||
#include "../common/repositories/pets_beastlord_data_repository.h"
|
#include "../common/repositories/pets_beastlord_data_repository.h"
|
||||||
|
#include "../common/repositories/character_pet_name_repository.h"
|
||||||
|
|
||||||
#include "entity.h"
|
#include "entity.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
@@ -164,6 +165,12 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
|
|||||||
// 4 - Keep DB name
|
// 4 - Keep DB name
|
||||||
// 5 - `s ward
|
// 5 - `s ward
|
||||||
|
|
||||||
|
if (IsClient() && !petname) {
|
||||||
|
const auto vanity_name = CharacterPetNameRepository::FindOne(database, CastToClient()->CharacterID());
|
||||||
|
if (!vanity_name.name.empty()) {
|
||||||
|
petname = vanity_name.name.c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (petname != nullptr) {
|
if (petname != nullptr) {
|
||||||
// Name was provided, use it.
|
// Name was provided, use it.
|
||||||
|
|||||||
+3
-1
@@ -3449,7 +3449,9 @@ std::string QuestManager::varlink(
|
|||||||
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
|
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
|
||||||
linker.SetItemInst(item);
|
linker.SetItemInst(item);
|
||||||
|
|
||||||
return linker.GenerateLink();
|
auto link = linker.GenerateLink();
|
||||||
|
safe_delete(item);
|
||||||
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string QuestManager::getitemcomment(uint32 item_id) {
|
std::string QuestManager::getitemcomment(uint32 item_id) {
|
||||||
|
|||||||
+70
-13
@@ -1,4 +1,6 @@
|
|||||||
#include "raycast_mesh.h"
|
#include "raycast_mesh.h"
|
||||||
|
#include "../common/memory/ksm.hpp"
|
||||||
|
#include "../common/eqemu_logsys.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -39,7 +41,7 @@
|
|||||||
namespace RAYCAST_MESH
|
namespace RAYCAST_MESH
|
||||||
{
|
{
|
||||||
|
|
||||||
typedef std::vector< RmUint32 > TriVector;
|
typedef std::vector<RmUint32, PageAlignedAllocator<RmUint32>> TriVector;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
/**
|
/**
|
||||||
@@ -754,28 +756,60 @@ public:
|
|||||||
{
|
{
|
||||||
mMaxNodeCount+=pow2Table[i];
|
mMaxNodeCount+=pow2Table[i];
|
||||||
}
|
}
|
||||||
mNodes = new NodeAABB[mMaxNodeCount];
|
// Allocate page-aligned memory
|
||||||
|
mNodes = static_cast<NodeAABB*>(KSM::AllocatePageAligned(sizeof(NodeAABB) * mMaxNodeCount));
|
||||||
|
if (!mNodes) {
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
mNodeCount = 0;
|
mNodeCount = 0;
|
||||||
|
KSM::CheckPageAlignment(mNodes);
|
||||||
|
|
||||||
|
mVertices = static_cast<RmReal*>(KSM::AllocatePageAligned(sizeof(RmReal) * 3 * vcount));
|
||||||
|
if (!mVertices) {
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
std::memcpy(mVertices, vertices, sizeof(RmReal) * 3 * vcount);
|
||||||
mVcount = vcount;
|
mVcount = vcount;
|
||||||
mVertices = (RmReal *)::malloc(sizeof(RmReal)*3*vcount);
|
|
||||||
memcpy(mVertices,vertices,sizeof(RmReal)*3*vcount);
|
mIndices = static_cast<RmUint32*>(KSM::AllocatePageAligned(sizeof(RmUint32) * 3 * tcount));
|
||||||
|
if (!mIndices) {
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
std::memcpy(mIndices, indices, sizeof(RmUint32) * 3 * tcount);
|
||||||
mTcount = tcount;
|
mTcount = tcount;
|
||||||
mIndices = (RmUint32 *)::malloc(sizeof(RmUint32)*tcount*3);
|
|
||||||
memcpy(mIndices,indices,sizeof(RmUint32)*tcount*3);
|
mRaycastTriangles = static_cast<RmUint32*>(KSM::AllocatePageAligned(sizeof(RmUint32) * tcount));
|
||||||
mRaycastTriangles = (RmUint32 *)::malloc(tcount*sizeof(RmUint32));
|
if (!mRaycastTriangles) {
|
||||||
memset(mRaycastTriangles,0,tcount*sizeof(RmUint32));
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
std::memset(mRaycastTriangles, 0, sizeof(RmUint32) * tcount);
|
||||||
|
|
||||||
|
mFaceNormals = static_cast<RmReal*>(KSM::AllocatePageAligned(sizeof(RmReal) * 3 * tcount));
|
||||||
|
if (!mFaceNormals) {
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
std::memset(mFaceNormals, 0, sizeof(RmReal) * 3 * tcount);
|
||||||
|
|
||||||
|
// Mark memory as mergeable for KSM
|
||||||
|
KSM::MarkMemoryForKSM(mVertices, sizeof(RmReal) * 3 * vcount);
|
||||||
|
KSM::MarkMemoryForKSM(mIndices, sizeof(RmUint32) * 3 * tcount);
|
||||||
|
KSM::MarkMemoryForKSM(mRaycastTriangles, sizeof(RmUint32) * tcount);
|
||||||
|
KSM::MarkMemoryForKSM(mFaceNormals, sizeof(RmReal) * 3 * tcount);
|
||||||
|
|
||||||
mRoot = getNode();
|
mRoot = getNode();
|
||||||
mFaceNormals = NULL;
|
mFaceNormals = NULL;
|
||||||
new ( mRoot ) NodeAABB(mVcount,mVertices,mTcount,mIndices,maxDepth,minLeafSize,minAxisSize,this,mLeafTriangles);
|
new ( mRoot ) NodeAABB(mVcount,mVertices,mTcount,mIndices,maxDepth,minLeafSize,minAxisSize,this,mLeafTriangles);
|
||||||
|
|
||||||
|
KSM::MarkMemoryForKSM(mLeafTriangles.data(), mLeafTriangles.size() * sizeof(RmUint32));
|
||||||
}
|
}
|
||||||
|
|
||||||
~MyRaycastMesh(void)
|
~MyRaycastMesh(void)
|
||||||
{
|
{
|
||||||
delete []mNodes;
|
if (mNodes) { free(mNodes); }
|
||||||
::free(mVertices);
|
if (mVertices) { free(mVertices); }
|
||||||
::free(mIndices);
|
if (mIndices) { free(mIndices); }
|
||||||
::free(mFaceNormals);
|
if (mRaycastTriangles) { free(mRaycastTriangles); }
|
||||||
::free(mRaycastTriangles);
|
if (mFaceNormals) { free(mFaceNormals); }
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool raycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance)
|
virtual bool raycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance)
|
||||||
@@ -938,6 +972,29 @@ RaycastMesh * createRaycastMesh(RmUint32 vcount, // The number of vertices in t
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto m = new MyRaycastMesh(vcount, vertices, tcount, indices, maxDepth, minLeafSize, minAxisSize);
|
auto m = new MyRaycastMesh(vcount, vertices, tcount, indices, maxDepth, minLeafSize, minAxisSize);
|
||||||
|
|
||||||
|
// Calculate memory usage
|
||||||
|
size_t vertex_size = vcount * sizeof(RmReal) * 3; // Each vertex has 3 floats
|
||||||
|
size_t index_size = tcount * 3 * sizeof(RmUint32); // Each triangle has 3 indices
|
||||||
|
size_t bvh_node_size = m->mNodeCount * sizeof(NodeAABB); // BVH Node memory usage
|
||||||
|
size_t bvh_leaf_size = m->mLeafTriangles.size() * sizeof(RmUint32); // BVH leaf triangles
|
||||||
|
|
||||||
|
size_t bvh_size = bvh_node_size + bvh_leaf_size; // Total BVH size
|
||||||
|
size_t total_size = vertex_size + index_size + bvh_size;
|
||||||
|
|
||||||
|
KSM::CheckPageAlignment(m->mNodes);
|
||||||
|
KSM::CheckPageAlignment(m->mVertices);
|
||||||
|
|
||||||
|
LogInfo(
|
||||||
|
"Map Raycast Memory Usage | Vertices [{:.2f}] MB Indices [{:.2f}] MB BVH Nodes [{:.2f}] MB BVH Leaves [{:.2f}] MB BVH Total [{:.2f}] MB",
|
||||||
|
vertex_size / (1024.0 * 1024.0),
|
||||||
|
index_size / (1024.0 * 1024.0),
|
||||||
|
bvh_node_size / (1024.0 * 1024.0),
|
||||||
|
bvh_leaf_size / (1024.0 * 1024.0),
|
||||||
|
bvh_size / (1024.0 * 1024.0)
|
||||||
|
);
|
||||||
|
LogInfo("Total Raycast Memory [{:.2f}] MB", total_size / (1024.0 * 1024.0));
|
||||||
|
|
||||||
return static_cast< RaycastMesh * >(m);
|
return static_cast< RaycastMesh * >(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -533,6 +533,7 @@ bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList<Spawn2*> &spa
|
|||||||
);
|
);
|
||||||
|
|
||||||
spawn2_list.Insert(new_spawn);
|
spawn2_list.Insert(new_spawn);
|
||||||
|
new_spawn->Process();
|
||||||
}
|
}
|
||||||
|
|
||||||
LogInfo("Loaded [{}] spawn2 entries", Strings::Commify(l.size()));
|
LogInfo("Loaded [{}] spawn2 entries", Strings::Commify(l.size()));
|
||||||
|
|||||||
+15
-18
@@ -953,9 +953,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
|||||||
auto action_packet =
|
auto action_packet =
|
||||||
new EQApplicationPacket(OP_Action, sizeof(Action_Struct));
|
new EQApplicationPacket(OP_Action, sizeof(Action_Struct));
|
||||||
Action_Struct* action = (Action_Struct*) action_packet->pBuffer;
|
Action_Struct* action = (Action_Struct*) action_packet->pBuffer;
|
||||||
auto message_packet =
|
|
||||||
new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct));
|
static EQApplicationPacket p(OP_Damage, sizeof(CombatDamage_Struct));
|
||||||
CombatDamage_Struct *cd = (CombatDamage_Struct *)message_packet->pBuffer;
|
auto cd = (CombatDamage_Struct *) p.pBuffer;
|
||||||
|
|
||||||
action->target = GetID();
|
action->target = GetID();
|
||||||
action->source = caster ? caster->GetID() : GetID();
|
action->source = caster ? caster->GetID() : GetID();
|
||||||
@@ -978,16 +978,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
|||||||
caster->CastToClient()->QueuePacket(action_packet);
|
caster->CastToClient()->QueuePacket(action_packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
CastToClient()->QueuePacket(message_packet);
|
CastToClient()->QueuePacket(&p);
|
||||||
|
|
||||||
if (caster && caster->IsClient() && caster != this) {
|
if (caster && caster->IsClient() && caster != this) {
|
||||||
caster->CastToClient()->QueuePacket(message_packet);
|
caster->CastToClient()->QueuePacket(&p);
|
||||||
}
|
}
|
||||||
|
|
||||||
CastToClient()->SetBindPoint(spells[spell_id].base_value[i] - 1);
|
CastToClient()->SetBindPoint(spells[spell_id].base_value[i] - 1);
|
||||||
Save();
|
Save();
|
||||||
safe_delete(action_packet);
|
safe_delete(action_packet);
|
||||||
safe_delete(message_packet);
|
|
||||||
} else {
|
} else {
|
||||||
if (!zone->CanBind()) {
|
if (!zone->CanBind()) {
|
||||||
MessageString(Chat::SpellFailure, CANNOT_BIND);
|
MessageString(Chat::SpellFailure, CANNOT_BIND);
|
||||||
@@ -1002,9 +1001,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
|||||||
auto action_packet = new EQApplicationPacket(
|
auto action_packet = new EQApplicationPacket(
|
||||||
OP_Action, sizeof(Action_Struct));
|
OP_Action, sizeof(Action_Struct));
|
||||||
Action_Struct* action = (Action_Struct*) action_packet->pBuffer;
|
Action_Struct* action = (Action_Struct*) action_packet->pBuffer;
|
||||||
auto message_packet = new EQApplicationPacket(
|
|
||||||
OP_Damage, sizeof(CombatDamage_Struct));
|
static EQApplicationPacket p(OP_Damage, sizeof(CombatDamage_Struct));
|
||||||
CombatDamage_Struct *cd = (CombatDamage_Struct *)message_packet->pBuffer;
|
auto cd = (CombatDamage_Struct *) p.pBuffer;
|
||||||
|
|
||||||
action->target = GetID();
|
action->target = GetID();
|
||||||
action->source = caster ? caster->GetID() : GetID();
|
action->source = caster ? caster->GetID() : GetID();
|
||||||
@@ -1027,24 +1026,23 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
|||||||
caster->CastToClient()->QueuePacket(action_packet);
|
caster->CastToClient()->QueuePacket(action_packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
CastToClient()->QueuePacket(message_packet);
|
CastToClient()->QueuePacket(&p);
|
||||||
|
|
||||||
if (caster->IsClient() && caster != this) {
|
if (caster->IsClient() && caster != this) {
|
||||||
caster->CastToClient()->QueuePacket(message_packet);
|
caster->CastToClient()->QueuePacket(&p);
|
||||||
}
|
}
|
||||||
|
|
||||||
CastToClient()->SetBindPoint(spells[spell_id].base_value[i] - 1);
|
CastToClient()->SetBindPoint(spells[spell_id].base_value[i] - 1);
|
||||||
Save();
|
Save();
|
||||||
safe_delete(action_packet);
|
safe_delete(action_packet);
|
||||||
safe_delete(message_packet);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto action_packet =
|
auto action_packet =
|
||||||
new EQApplicationPacket(OP_Action, sizeof(Action_Struct));
|
new EQApplicationPacket(OP_Action, sizeof(Action_Struct));
|
||||||
Action_Struct* action = (Action_Struct*) action_packet->pBuffer;
|
Action_Struct* action = (Action_Struct*) action_packet->pBuffer;
|
||||||
auto message_packet = new EQApplicationPacket(
|
|
||||||
OP_Damage, sizeof(CombatDamage_Struct));
|
static EQApplicationPacket p(OP_Damage, sizeof(CombatDamage_Struct));
|
||||||
CombatDamage_Struct *cd = (CombatDamage_Struct *)message_packet->pBuffer;
|
auto cd = (CombatDamage_Struct *) p.pBuffer;
|
||||||
|
|
||||||
action->target = GetID();
|
action->target = GetID();
|
||||||
action->source = caster ? caster->GetID() : GetID();
|
action->source = caster ? caster->GetID() : GetID();
|
||||||
@@ -1067,16 +1065,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
|||||||
caster->CastToClient()->QueuePacket(action_packet);
|
caster->CastToClient()->QueuePacket(action_packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
CastToClient()->QueuePacket(message_packet);
|
CastToClient()->QueuePacket(&p);
|
||||||
|
|
||||||
if (caster->IsClient() && caster != this) {
|
if (caster->IsClient() && caster != this) {
|
||||||
caster->CastToClient()->QueuePacket(message_packet);
|
caster->CastToClient()->QueuePacket(&p);
|
||||||
}
|
}
|
||||||
|
|
||||||
CastToClient()->SetBindPoint(spells[spell_id].base_value[i] - 1);
|
CastToClient()->SetBindPoint(spells[spell_id].base_value[i] - 1);
|
||||||
Save();
|
Save();
|
||||||
safe_delete(action_packet);
|
safe_delete(action_packet);
|
||||||
safe_delete(message_packet);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-5
@@ -4564,8 +4564,8 @@ bool Mob::SpellOnTarget(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message_packet = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct));
|
static EQApplicationPacket p(OP_Damage, sizeof(CombatDamage_Struct));
|
||||||
CombatDamage_Struct *cd = (CombatDamage_Struct *)message_packet->pBuffer;
|
auto cd = (CombatDamage_Struct *) p.pBuffer;
|
||||||
cd->target = action->target;
|
cd->target = action->target;
|
||||||
cd->source = action->source;
|
cd->source = action->source;
|
||||||
cd->type = action->type;
|
cd->type = action->type;
|
||||||
@@ -4583,7 +4583,7 @@ bool Mob::SpellOnTarget(
|
|||||||
) {
|
) {
|
||||||
entity_list.QueueCloseClients(
|
entity_list.QueueCloseClients(
|
||||||
spelltar, /* Sender */
|
spelltar, /* Sender */
|
||||||
message_packet, /* Packet */
|
&p, /* Packet */
|
||||||
false, /* Ignore Sender */
|
false, /* Ignore Sender */
|
||||||
RuleI(Range, SpellMessages),
|
RuleI(Range, SpellMessages),
|
||||||
0, /* Skip this mob */
|
0, /* Skip this mob */
|
||||||
@@ -4593,7 +4593,6 @@ bool Mob::SpellOnTarget(
|
|||||||
}
|
}
|
||||||
|
|
||||||
safe_delete(action_packet);
|
safe_delete(action_packet);
|
||||||
safe_delete(message_packet);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Bug: When an HP buff with a heal effect is applied for first time, the heal portion of the effect heals the client and
|
Bug: When an HP buff with a heal effect is applied for first time, the heal portion of the effect heals the client and
|
||||||
@@ -7379,14 +7378,16 @@ void Mob::SetHP(int64 hp)
|
|||||||
void Mob::DrawDebugCoordinateNode(std::string node_name, const glm::vec4 vec)
|
void Mob::DrawDebugCoordinateNode(std::string node_name, const glm::vec4 vec)
|
||||||
{
|
{
|
||||||
NPC *node = nullptr;
|
NPC *node = nullptr;
|
||||||
|
|
||||||
for (const auto &n: entity_list.GetNPCList()) {
|
for (const auto &n: entity_list.GetNPCList()) {
|
||||||
if (n.second->GetCleanName() == node_name) {
|
if (n.second->GetEntityVariable("node_parent_id") == std::to_string(GetID())) {
|
||||||
node = n.second;
|
node = n.second;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!node) {
|
if (!node) {
|
||||||
node = NPC::SpawnNodeNPC(node_name, "", GetPosition());
|
node = NPC::SpawnNodeNPC(node_name, "", GetPosition());
|
||||||
|
node->SetEntityVariable("node_parent_id", std::to_string(GetID()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-313
@@ -1800,7 +1800,13 @@ void Client::SendBarterWelcome()
|
|||||||
|
|
||||||
void Client::DoBazaarSearch(BazaarSearchCriteria_Struct search_criteria)
|
void Client::DoBazaarSearch(BazaarSearchCriteria_Struct search_criteria)
|
||||||
{
|
{
|
||||||
auto results = Bazaar::GetSearchResults(database, search_criteria, GetZoneID(), GetInstanceID());
|
std::vector<BazaarSearchResultsFromDB_Struct> results = Bazaar::GetSearchResults(
|
||||||
|
database,
|
||||||
|
content_db,
|
||||||
|
search_criteria,
|
||||||
|
GetZoneID(),
|
||||||
|
GetInstanceID()
|
||||||
|
);
|
||||||
if (results.empty()) {
|
if (results.empty()) {
|
||||||
SendBazaarDone(GetID());
|
SendBazaarDone(GetID());
|
||||||
return;
|
return;
|
||||||
@@ -1822,317 +1828,6 @@ void Client::DoBazaarSearch(BazaarSearchCriteria_Struct search_criteria)
|
|||||||
SendBazaarDeliveryCosts();
|
SendBazaarDeliveryCosts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::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
|
|
||||||
)
|
|
||||||
{
|
|
||||||
std::string search_values = " COUNT(item_id), trader.*, items.name ";
|
|
||||||
std::string search_criteria = " WHERE trader.item_id = items.id ";
|
|
||||||
|
|
||||||
if (trader_id > 0) {
|
|
||||||
Client *trader = entity_list.GetClientByID(trader_id);
|
|
||||||
|
|
||||||
if (trader) {
|
|
||||||
search_criteria.append(StringFormat(" AND trader.char_id = %i", trader->CharacterID()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (min_price != 0) {
|
|
||||||
search_criteria.append(StringFormat(" AND trader.item_cost >= %i", min_price));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (max_price != 0) {
|
|
||||||
search_criteria.append(StringFormat(" AND trader.item_cost <= %i", max_price));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen(item_name) > 0) {
|
|
||||||
char *safeName = RemoveApostrophes(item_name);
|
|
||||||
search_criteria.append(StringFormat(" AND items.name LIKE '%%%s%%'", safeName));
|
|
||||||
safe_delete_array(safeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_class != 0xFFFFFFFF) {
|
|
||||||
search_criteria.append(StringFormat(" AND MID(REVERSE(BIN(items.classes)), %i, 1) = 1", in_class));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_race != 0xFFFFFFFF) {
|
|
||||||
search_criteria.append(StringFormat(" AND MID(REVERSE(BIN(items.races)), %i, 1) = 1", in_race));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item_slot != 0xFFFFFFFF) {
|
|
||||||
search_criteria.append(StringFormat(" AND MID(REVERSE(BIN(items.slots)), %i, 1) = 1", item_slot + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (item_type) {
|
|
||||||
case 0xFFFFFFFF:
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
// 1H Slashing
|
|
||||||
search_criteria.append(" AND items.itemtype = 0 AND damage > 0");
|
|
||||||
break;
|
|
||||||
case 31:
|
|
||||||
search_criteria.append(" AND items.itemclass = 2");
|
|
||||||
break;
|
|
||||||
case 46:
|
|
||||||
search_criteria.append(" AND items.scrolleffect > 0 AND items.scrolleffect < 65000");
|
|
||||||
break;
|
|
||||||
case 47:
|
|
||||||
search_criteria.append(" AND items.worneffect = 998");
|
|
||||||
break;
|
|
||||||
case 48:
|
|
||||||
search_criteria.append(" AND items.worneffect >= 1298 AND items.worneffect <= 1307");
|
|
||||||
break;
|
|
||||||
case 49:
|
|
||||||
search_criteria.append(" AND items.focuseffect > 0");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
search_criteria.append(StringFormat(" AND items.itemtype = %i", item_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (item_stat) {
|
|
||||||
|
|
||||||
case STAT_AC:
|
|
||||||
search_criteria.append(" AND items.ac > 0");
|
|
||||||
search_values.append(", items.ac");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_AGI:
|
|
||||||
search_criteria.append(" AND items.aagi > 0");
|
|
||||||
search_values.append(", items.aagi");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_CHA:
|
|
||||||
search_criteria.append(" AND items.acha > 0");
|
|
||||||
search_values.append(", items.acha");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_DEX:
|
|
||||||
search_criteria.append(" AND items.adex > 0");
|
|
||||||
search_values.append(", items.adex");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_INT:
|
|
||||||
search_criteria.append(" AND items.aint > 0");
|
|
||||||
search_values.append(", items.aint");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_STA:
|
|
||||||
search_criteria.append(" AND items.asta > 0");
|
|
||||||
search_values.append(", items.asta");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_STR:
|
|
||||||
search_criteria.append(" AND items.astr > 0");
|
|
||||||
search_values.append(", items.astr");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_WIS:
|
|
||||||
search_criteria.append(" AND items.awis > 0");
|
|
||||||
search_values.append(", items.awis");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_COLD:
|
|
||||||
search_criteria.append(" AND items.cr > 0");
|
|
||||||
search_values.append(", items.cr");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_DISEASE:
|
|
||||||
search_criteria.append(" AND items.dr > 0");
|
|
||||||
search_values.append(", items.dr");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_FIRE:
|
|
||||||
search_criteria.append(" AND items.fr > 0");
|
|
||||||
search_values.append(", items.fr");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_MAGIC:
|
|
||||||
search_criteria.append(" AND items.mr > 0");
|
|
||||||
search_values.append(", items.mr");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_POISON:
|
|
||||||
search_criteria.append(" AND items.pr > 0");
|
|
||||||
search_values.append(", items.pr");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_HP:
|
|
||||||
search_criteria.append(" AND items.hp > 0");
|
|
||||||
search_values.append(", items.hp");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_MANA:
|
|
||||||
search_criteria.append(" AND items.mana > 0");
|
|
||||||
search_values.append(", items.mana");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_ENDURANCE:
|
|
||||||
search_criteria.append(" AND items.endur > 0");
|
|
||||||
search_values.append(", items.endur");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_ATTACK:
|
|
||||||
search_criteria.append(" AND items.attack > 0");
|
|
||||||
search_values.append(", items.attack");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_HP_REGEN:
|
|
||||||
search_criteria.append(" AND items.regen > 0");
|
|
||||||
search_values.append(", items.regen");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_MANA_REGEN:
|
|
||||||
search_criteria.append(" AND items.manaregen > 0");
|
|
||||||
search_values.append(", items.manaregen");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_HASTE:
|
|
||||||
search_criteria.append(" AND items.haste > 0");
|
|
||||||
search_values.append(", items.haste");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_DAMAGE_SHIELD:
|
|
||||||
search_criteria.append(" AND items.damageshield > 0");
|
|
||||||
search_values.append(", items.damageshield");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
search_values.append(", 0");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string query = StringFormat(
|
|
||||||
"SELECT %s, SUM(charges), items.stackable "
|
|
||||||
"FROM trader, items %s GROUP BY items.id, charges, char_id LIMIT %i",
|
|
||||||
search_values.c_str(),
|
|
||||||
search_criteria.c_str(),
|
|
||||||
RuleI(Bazaar, MaxSearchResults)
|
|
||||||
);
|
|
||||||
|
|
||||||
auto results = database.QueryDatabase(query);
|
|
||||||
|
|
||||||
if (!results.Success()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogTrading("SRCH: [{}]", query.c_str());
|
|
||||||
|
|
||||||
int Size = 0;
|
|
||||||
uint32 ID = 0;
|
|
||||||
|
|
||||||
if (results.RowCount() == static_cast<unsigned long>(RuleI(Bazaar, MaxSearchResults))) {
|
|
||||||
Message(
|
|
||||||
Chat::Yellow,
|
|
||||||
"Your search reached the limit of %i results. Please narrow your search down by selecting more options.",
|
|
||||||
RuleI(Bazaar, MaxSearchResults));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results.RowCount() == 0) {
|
|
||||||
auto outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct));
|
|
||||||
BazaarReturnDone_Struct *brds = (BazaarReturnDone_Struct *) outapp2->pBuffer;
|
|
||||||
brds->TraderID = ID;
|
|
||||||
brds->Type = BazaarSearchDone;
|
|
||||||
brds->Unknown008 = 0xFFFFFFFF;
|
|
||||||
brds->Unknown012 = 0xFFFFFFFF;
|
|
||||||
brds->Unknown016 = 0xFFFFFFFF;
|
|
||||||
QueuePacket(outapp2);
|
|
||||||
safe_delete(outapp2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Size = results.RowCount() * sizeof(BazaarSearchResults_Struct);
|
|
||||||
auto buffer = new uchar[Size];
|
|
||||||
uchar *bufptr = buffer;
|
|
||||||
memset(buffer, 0, Size);
|
|
||||||
|
|
||||||
int Action = BazaarSearchResults;
|
|
||||||
uint32 Cost = 0;
|
|
||||||
int32 SerialNumber = 0;
|
|
||||||
char temp_buffer[64] = {0};
|
|
||||||
int Count = 0;
|
|
||||||
uint32 StatValue = 0;
|
|
||||||
|
|
||||||
for (auto &row = results.begin(); row != results.end(); ++row) {
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Action);
|
|
||||||
Count = Strings::ToInt(row[0]);
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Count);
|
|
||||||
SerialNumber = Strings::ToInt(row[3]);
|
|
||||||
VARSTRUCT_ENCODE_TYPE(int32, bufptr, SerialNumber);
|
|
||||||
Client *Trader2 = entity_list.GetClientByCharID(Strings::ToInt(row[1]));
|
|
||||||
if (Trader2) {
|
|
||||||
ID = Trader2->GetID();
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, ID);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LogTrading("Unable to find trader: [{}]\n", Strings::ToInt(row[1]));
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0);
|
|
||||||
}
|
|
||||||
Cost = Strings::ToInt(row[5]);
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Cost);
|
|
||||||
StatValue = Strings::ToInt(row[8]);
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, StatValue);
|
|
||||||
bool Stackable = Strings::ToInt(row[10]);
|
|
||||||
if (Stackable) {
|
|
||||||
int Charges = Strings::ToInt(row[9]);
|
|
||||||
sprintf(temp_buffer, "%s(%i)", row[7], Charges);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sprintf(temp_buffer, "%s(%i)", row[7], Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(bufptr, &temp_buffer, strlen(temp_buffer));
|
|
||||||
|
|
||||||
bufptr += 64;
|
|
||||||
|
|
||||||
// Extra fields for SoD+
|
|
||||||
//
|
|
||||||
if (Trader2) {
|
|
||||||
sprintf(temp_buffer, "%s", Trader2->GetName());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sprintf(temp_buffer, "Unknown");
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(bufptr, &temp_buffer, strlen(temp_buffer));
|
|
||||||
|
|
||||||
bufptr += 64;
|
|
||||||
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Strings::ToInt(row[1])); // ItemID
|
|
||||||
}
|
|
||||||
|
|
||||||
auto outapp = new EQApplicationPacket(OP_BazaarSearch, Size);
|
|
||||||
|
|
||||||
memcpy(outapp->pBuffer, buffer, Size);
|
|
||||||
|
|
||||||
QueuePacket(outapp);
|
|
||||||
|
|
||||||
safe_delete(outapp);
|
|
||||||
safe_delete_array(buffer);
|
|
||||||
|
|
||||||
auto outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct));
|
|
||||||
BazaarReturnDone_Struct *brds = (BazaarReturnDone_Struct *) outapp2->pBuffer;
|
|
||||||
|
|
||||||
brds->TraderID = ID;
|
|
||||||
brds->Type = BazaarSearchDone;
|
|
||||||
|
|
||||||
brds->Unknown008 = 0xFFFFFFFF;
|
|
||||||
brds->Unknown012 = 0xFFFFFFFF;
|
|
||||||
brds->Unknown016 = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
QueuePacket(outapp2);
|
|
||||||
|
|
||||||
safe_delete(outapp2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void UpdateTraderCustomerItemsAdded(
|
static void UpdateTraderCustomerItemsAdded(
|
||||||
uint32 customer_id,
|
uint32 customer_id,
|
||||||
std::vector<BaseTraderRepository::Trader> trader_items,
|
std::vector<BaseTraderRepository::Trader> trader_items,
|
||||||
@@ -3602,7 +3297,7 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati
|
|||||||
out_data->id = trader_item.id;
|
out_data->id = trader_item.id;
|
||||||
strn0cpy(out_data->trader_buy_struct.buyer_name, GetCleanName(), sizeof(out_data->trader_buy_struct.buyer_name));
|
strn0cpy(out_data->trader_buy_struct.buyer_name, GetCleanName(), sizeof(out_data->trader_buy_struct.buyer_name));
|
||||||
|
|
||||||
worldserver.SendPacket(out_server.release());
|
worldserver.SendPacket(out_server.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::SetBuyerWelcomeMessage(const char *welcome_message)
|
void Client::SetBuyerWelcomeMessage(const char *welcome_message)
|
||||||
|
|||||||
+1
-62
@@ -1091,7 +1091,6 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name)
|
|||||||
|
|
||||||
mMovementManager = &MobMovementManager::Get();
|
mMovementManager = &MobMovementManager::Get();
|
||||||
|
|
||||||
SetNpcPositionUpdateDistance(0);
|
|
||||||
SetQuestHotReloadQueued(false);
|
SetQuestHotReloadQueued(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1380,7 +1379,7 @@ bool Zone::LoadZoneCFG(const char* filename, uint16 instance_version)
|
|||||||
can_levitate = z->canlevitate != 0;
|
can_levitate = z->canlevitate != 0;
|
||||||
can_castoutdoor = z->castoutdoor != 0;
|
can_castoutdoor = z->castoutdoor != 0;
|
||||||
is_hotzone = z->hotzone != 0;
|
is_hotzone = z->hotzone != 0;
|
||||||
max_movement_update_range = z->max_movement_update_range;
|
m_client_update_range = z->client_update_range;
|
||||||
default_ruleset = z->ruleset;
|
default_ruleset = z->ruleset;
|
||||||
allow_mercs = true;
|
allow_mercs = true;
|
||||||
m_graveyard_id = z->graveyard_id;
|
m_graveyard_id = z->graveyard_id;
|
||||||
@@ -1538,10 +1537,6 @@ bool Zone::Process() {
|
|||||||
if (adv_data && !did_adventure_actions) {
|
if (adv_data && !did_adventure_actions) {
|
||||||
DoAdventureActions();
|
DoAdventureActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetNpcPositionUpdateDistance() == 0) {
|
|
||||||
CalculateNpcUpdateDistanceSpread();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hot_reload_timer.Check() && IsQuestHotReloadQueued()) {
|
if (hot_reload_timer.Check() && IsQuestHotReloadQueued()) {
|
||||||
@@ -2735,62 +2730,6 @@ void Zone::SetUCSServerAvailable(bool ucss_available, uint32 update_timestamp) {
|
|||||||
m_ucss_available = ucss_available;
|
m_ucss_available = ucss_available;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Zone::GetNpcPositionUpdateDistance() const
|
|
||||||
{
|
|
||||||
return npc_position_update_distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Zone::SetNpcPositionUpdateDistance(int in_npc_position_update_distance)
|
|
||||||
{
|
|
||||||
Zone::npc_position_update_distance = in_npc_position_update_distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Zone::CalculateNpcUpdateDistanceSpread()
|
|
||||||
{
|
|
||||||
float max_x = 0;
|
|
||||||
float max_y = 0;
|
|
||||||
float min_x = 0;
|
|
||||||
float min_y = 0;
|
|
||||||
|
|
||||||
auto &mob_list = entity_list.GetMobList();
|
|
||||||
|
|
||||||
for (auto &it : mob_list) {
|
|
||||||
Mob *entity = it.second;
|
|
||||||
if (!entity->IsNPC()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity->GetX() <= min_x) {
|
|
||||||
min_x = entity->GetX();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity->GetY() <= min_y) {
|
|
||||||
min_y = entity->GetY();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity->GetX() >= max_x) {
|
|
||||||
max_x = entity->GetX();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity->GetY() >= max_y) {
|
|
||||||
max_y = entity->GetY();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int x_spread = int(std::abs(max_x - min_x));
|
|
||||||
int y_spread = int(std::abs(max_y - min_y));
|
|
||||||
int combined_spread = int(std::abs((x_spread + y_spread) / 2));
|
|
||||||
int update_distance = EQ::ClampLower(int(combined_spread / 4), int(zone->GetMaxMovementUpdateRange()));
|
|
||||||
|
|
||||||
SetNpcPositionUpdateDistance(update_distance);
|
|
||||||
|
|
||||||
Log(Logs::General, Logs::Debug,
|
|
||||||
"NPC update spread distance set to [%i] combined_spread [%i]",
|
|
||||||
update_distance,
|
|
||||||
combined_spread
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Zone::IsQuestHotReloadQueued() const
|
bool Zone::IsQuestHotReloadQueued() const
|
||||||
{
|
{
|
||||||
return quest_hot_reload_queued;
|
return quest_hot_reload_queued;
|
||||||
|
|||||||
+2
-5
@@ -156,9 +156,6 @@ public:
|
|||||||
bool SaveZoneCFG();
|
bool SaveZoneCFG();
|
||||||
bool DoesAlternateCurrencyExist(uint32 currency_id);
|
bool DoesAlternateCurrencyExist(uint32 currency_id);
|
||||||
|
|
||||||
int GetNpcPositionUpdateDistance() const;
|
|
||||||
void SetNpcPositionUpdateDistance(int in_npc_position_update_distance);
|
|
||||||
|
|
||||||
char *adv_data;
|
char *adv_data;
|
||||||
|
|
||||||
const char *GetSpellBlockedMessage(uint32 spell_id, const glm::vec3 &location);
|
const char *GetSpellBlockedMessage(uint32 spell_id, const glm::vec3 &location);
|
||||||
@@ -417,7 +414,7 @@ public:
|
|||||||
SendDiscordMessage(webhook_id, message_prefix + Discord::FormatDiscordMessage(log_category, message));
|
SendDiscordMessage(webhook_id, message_prefix + Discord::FormatDiscordMessage(log_category, message));
|
||||||
};
|
};
|
||||||
|
|
||||||
double GetMaxMovementUpdateRange() const { return max_movement_update_range; }
|
double GetClientUpdateRange() const { return m_client_update_range; }
|
||||||
|
|
||||||
void SetIsHotzone(bool is_hotzone);
|
void SetIsHotzone(bool is_hotzone);
|
||||||
|
|
||||||
@@ -469,7 +466,7 @@ private:
|
|||||||
bool staticzone;
|
bool staticzone;
|
||||||
bool zone_has_current_time;
|
bool zone_has_current_time;
|
||||||
bool quest_hot_reload_queued;
|
bool quest_hot_reload_queued;
|
||||||
double max_movement_update_range;
|
double m_client_update_range;
|
||||||
char *long_name;
|
char *long_name;
|
||||||
char *map_name;
|
char *map_name;
|
||||||
char *short_name;
|
char *short_name;
|
||||||
|
|||||||
Reference in New Issue
Block a user