From aa94b032e75f6f54382a9101096170d1fe7f111d Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 18 Jan 2025 17:18:57 +0000 Subject: [PATCH 1/5] Core: Refactor ZoneEntities iteration to use FOR_EACH_PAIR_CAST_SECOND As shown below, these three iteration types are identical in the assembly they output. Original: ```cpp for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) { CCharEntity* PCurrentChar = (CCharEntity*)it->second; ```` Macros: ```cpp for (const auto& [_key, _value] : _collection) \ if (auto _var = static_cast<_type>(_value); true) for (const auto& [_key, _value] : _collection) \ if (auto _var = _value; true) ``` Assembly: ```asm originalLoop(std::unordered_map, std::equal_to, std::allocator>> const&): push rbx mov rbx, qword ptr [rdi + 16] test rbx, rbx je .LBB0_3 .LBB0_1: mov rdi, qword ptr [rbx + 16] call CCharEntity::doSomethingCharacterful() const mov rbx, qword ptr [rbx] test rbx, rbx jne .LBB0_1 .LBB0_3: pop rbx ret ``` ```asm macroLoopWithCast(std::unordered_map, std::equal_to, std::allocator>> const&): push rbx mov rbx, qword ptr [rdi + 16] test rbx, rbx je .LBB2_3 .LBB2_1: mov rdi, qword ptr [rbx + 16] call CCharEntity::doSomethingCharacterful() const mov rbx, qword ptr [rbx] test rbx, rbx jne .LBB2_1 .LBB2_3: pop rbx ``` ```asm macroLoopNoCast(std::unordered_map, std::equal_to, std::allocator>> const&): push rbx mov rbx, qword ptr [rdi + 16] test rbx, rbx je .LBB3_3 .LBB3_1: mov rdi, qword ptr [rbx + 16] call CBaseEntity::doSomethingBasic() const mov rbx, qword ptr [rbx] test rbx, rbx jne .LBB3_1 .LBB3_3: pop rbx ret ``` Core: Fix issue iterating wrong container in SpawnPCs --- .github/workflows/build.yml | 2 +- src/common/cbasetypes.h | 56 +- src/common/macros.h | 97 ++ .../ai/controllers/automaton_controller.cpp | 5 +- src/map/ai/helpers/targetfind.cpp | 14 +- src/map/pch.h | 3 + src/map/utils/battleutils.cpp | 33 +- src/map/zone.cpp | 26 +- src/map/zone_entities.cpp | 909 ++++++++++-------- src/map/zone_entities.h | 1 + src/map/zone_instance.cpp | 14 +- 11 files changed, 649 insertions(+), 511 deletions(-) create mode 100644 src/common/macros.h diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d2be54f6ea7..682fdef88b8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -391,7 +391,7 @@ jobs: - name: Build shell: cmd run: | - cmake --build build -j4 + cmake --build build --config Release -j4 - name: Archive Executables uses: actions/upload-artifact@v4 with: diff --git a/src/common/cbasetypes.h b/src/common/cbasetypes.h index 92136a344dd..f92451ff7af 100644 --- a/src/common/cbasetypes.h +++ b/src/common/cbasetypes.h @@ -9,29 +9,7 @@ #include #include "logging.h" - -// The following definitions are set by CMake based on the architecture -// #define ENV64BIT -// #define ENV32BIT - -// Ensure one of the definitions is set -#if !defined(ENV64BIT) && !defined(ENV32BIT) -#error "Neither ENV64BIT nor ENV32BIT is defined" -#endif - -// Debug mode -#if defined(_DEBUG) && !defined(DEBUG) -#define DEBUG -#endif - -// Release mode -#if !defined(_DEBUG) && !defined(RELEASE) -#define RELEASE -#endif - -// define a break macro for debugging -#define XI_DEBUG_BREAK_IF(_CONDITION_) \ - static_assert(false, "Use of XI_DEBUG_BREAK_IF is deprecated. Check your conditions and log appropriately instead.") +#include "macros.h" // typedef/using using int8 = std::int8_t; @@ -71,26 +49,6 @@ inline void destroy_arr(T*& ptr) ptr = nullptr; } -// string case comparison for *nix portability -#if !defined(_MSC_VER) -#define strcmpi strcasecmp -#define stricmp strcasecmp - -// https://stackoverflow.com/questions/12044519/what-is-the-windows-equivalent-of-the-unix-function-gmtime-r -// gmtime_r() is the thread-safe version of gmtime(). The MSVC implementation of gmtime() is already thread safe, -// the returned struct tm* is allocated in thread-local storage. -// That doesn't make it immune from trouble if the function is called multiple times on the same -// thread and the returned pointer is stored. -// You can use gmtime_s() instead. Closest to gmtime_r() but with the arguments reversed - -// Provide func_s implementations for Unix -#define _gmtime_s(a, b) gmtime_r(b, a) -#define _localtime_s(a, b) localtime_r(b, a) -#else // MSVC -#define _gmtime_s(a, b) gmtime_s(a, b) -#define _localtime_s(a, b) localtime_s(a, b) -#endif - #include using namespace std::literals::chrono_literals; @@ -119,18 +77,6 @@ struct PtrGreater template using MinHeapPtr = std::priority_queue, PtrGreater>; -#define DISALLOW_COPY(TypeName) \ - TypeName(const TypeName&) = delete; \ - TypeName& operator=(const TypeName&) = delete; - -#define DISALLOW_MOVE(TypeName) \ - TypeName(TypeName&&) = delete; \ - TypeName& operator=(TypeName&&) = delete; - -#define DISALLOW_COPY_AND_MOVE(TypeName) \ - DISALLOW_COPY(TypeName) \ - DISALLOW_MOVE(TypeName) - #include "tracy.h" #endif /* _CBASETYPES_H_ */ diff --git a/src/common/macros.h b/src/common/macros.h new file mode 100644 index 00000000000..a4343c0ccf3 --- /dev/null +++ b/src/common/macros.h @@ -0,0 +1,97 @@ +/* +=========================================================================== + + Copyright (c) 2025 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +// The following definitions are set by CMake based on the architecture +// #define ENV64BIT +// #define ENV32BIT + +// Ensure one of the definitions is set +#if !defined(ENV64BIT) && !defined(ENV32BIT) +#error "Neither ENV64BIT nor ENV32BIT is defined" +#endif + +// Debug mode +#if defined(_DEBUG) && !defined(DEBUG) +#define DEBUG +#endif + +// Release mode +#if !defined(_DEBUG) && !defined(RELEASE) +#define RELEASE +#endif + +// define a break macro for debugging +#define XI_DEBUG_BREAK_IF(_CONDITION_) \ + static_assert(false, "Use of XI_DEBUG_BREAK_IF is deprecated. Check your conditions and log appropriately instead.") + +#define DISALLOW_COPY(TypeName) \ + TypeName(const TypeName&) = delete; \ + TypeName& operator=(const TypeName&) = delete; + +#define DISALLOW_MOVE(TypeName) \ + TypeName(TypeName&&) = delete; \ + TypeName& operator=(TypeName&&) = delete; + +#define DISALLOW_COPY_AND_MOVE(TypeName) \ + DISALLOW_COPY(TypeName) \ + DISALLOW_MOVE(TypeName) + +// +// This `FOR_EACH_PAIR_CAST_SECOND` macro replaces a common pattern we had in the hot path: +// +// ```cpp +// for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) +// { +// CCharEntity* PCurrentChar = (CCharEntity*)it->second; +// ``` +// +// It provides a cleaner and more concise way to iterate over collections +// while maintaining the same performance characteristics as the original code. +// +// Both the original and macro version produce identical assembly output. +// +// Assembly Analysis: https://github.com/LandSandBoat/server/pull/6751 +// +#define FOR_EACH_PAIR_CAST_SECOND(_collection, _type, _var) \ + for (const auto& [_key, _value] : _collection) \ + if (auto _var = static_cast<_type>(_value); true) + +// string case comparison for *nix portability +#if !defined(_MSC_VER) +#define strcmpi strcasecmp +#define stricmp strcasecmp + +// https://stackoverflow.com/questions/12044519/what-is-the-windows-equivalent-of-the-unix-function-gmtime-r +// gmtime_r() is the thread-safe version of gmtime(). The MSVC implementation of gmtime() is already thread safe, +// the returned struct tm* is allocated in thread-local storage. +// That doesn't make it immune from trouble if the function is called multiple times on the same +// thread and the returned pointer is stored. +// You can use gmtime_s() instead. Closest to gmtime_r() but with the arguments reversed + +// Provide func_s implementations for Unix +#define _gmtime_s(a, b) gmtime_r(b, a) +#define _localtime_s(a, b) localtime_r(b, a) +#else // MSVC +#define _gmtime_s(a, b) gmtime_s(a, b) +#define _localtime_s(a, b) localtime_s(a, b) +#endif diff --git a/src/map/ai/controllers/automaton_controller.cpp b/src/map/ai/controllers/automaton_controller.cpp index 0040edd4456..1169e00207e 100644 --- a/src/map/ai/controllers/automaton_controller.cpp +++ b/src/map/ai/controllers/automaton_controller.cpp @@ -223,9 +223,8 @@ void CAutomatonController::DoCombatTick(time_point tick) void CAutomatonController::Move() { - float currentDistance = distanceSquared(PAutomaton->loc.p, PTarget->loc.p); - - if ((shouldStandBack() && (currentDistance > 225)) || (PAutomaton->health.mp < 8 && PAutomaton->health.maxmp > 8)) + if ((shouldStandBack() && !isWithinDistance(PAutomaton->loc.p, PTarget->loc.p, 15.0f)) || + (PAutomaton->health.mp < 8 && PAutomaton->health.maxmp > 8)) { PAutomaton->m_Behavior &= ~BEHAVIOR_STANDBACK; } diff --git a/src/map/ai/helpers/targetfind.cpp b/src/map/ai/helpers/targetfind.cpp index fa699fb5589..10ad26dbeec 100644 --- a/src/map/ai/helpers/targetfind.cpp +++ b/src/map/ai/helpers/targetfind.cpp @@ -235,9 +235,8 @@ void CTargetFind::addAllInMobList(CBattleEntity* PTarget, bool withPet) CCharEntity* PChar = dynamic_cast(findMaster(m_PBattleEntity)); if (PChar) { - for (SpawnIDList_t::const_iterator it = PChar->SpawnMOBList.begin(); it != PChar->SpawnMOBList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(PChar->SpawnMOBList, CMobEntity*, PBattleTarget) { - CBattleEntity* PBattleTarget = dynamic_cast(it->second); if (PBattleTarget && isMobOwner(PBattleTarget)) { addEntity(PBattleTarget, withPet); @@ -311,12 +310,10 @@ void CTargetFind::addAllInEnmityList() { if (m_PBattleEntity->objtype == TYPE_MOB) { - CMobEntity* mob = (CMobEntity*)m_PBattleEntity; - EnmityList_t* enmityList = mob->PEnmityContainer->GetEnmityList(); + CMobEntity* PMob = static_cast(m_PBattleEntity); - for (auto& it : *enmityList) + for (const auto& [_, PEnmityObject] : *PMob->PEnmityContainer->GetEnmityList()) { - EnmityObject_t& PEnmityObject = it.second; if (PEnmityObject.PEnmityOwner) { addEntity(PEnmityObject.PEnmityOwner, false); @@ -335,11 +332,10 @@ void CTargetFind::addAllInRange(CBattleEntity* PTarget, float radius, ALLEGIANCE if (PTarget->objtype == TYPE_PC) { CCharEntity* PChar = static_cast(PTarget); - for (const auto& list : { PChar->SpawnPCList, PChar->SpawnPETList }) + for (auto& list : { PChar->SpawnPCList, PChar->SpawnPETList }) { - for (const auto& pair : list) + FOR_EACH_PAIR_CAST_SECOND(list, CBattleEntity*, PBattleEntity) { - CBattleEntity* PBattleEntity = static_cast(pair.second); if (PBattleEntity && isWithinArea(&(PBattleEntity->loc.p)) && !PBattleEntity->isDead() && PBattleEntity->allegiance == ALLEGIANCE_TYPE::PLAYER) { diff --git a/src/map/pch.h b/src/map/pch.h index 89ea3d6b78e..c5686f56fc2 100644 --- a/src/map/pch.h +++ b/src/map/pch.h @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +84,7 @@ #include "common/database.h" #include "common/kernel.h" #include "common/logging.h" +#include "common/macros.h" #include "common/md52.h" #include "common/mmo.h" #include "common/socket.h" @@ -92,6 +94,7 @@ #include "common/tracy.h" #include "common/utils.h" #include "common/version.h" +#include "common/xi.h" #include "common/xirand.h" #include diff --git a/src/map/utils/battleutils.cpp b/src/map/utils/battleutils.cpp index ef19d3038d7..e41935cb90a 100644 --- a/src/map/utils/battleutils.cpp +++ b/src/map/utils/battleutils.cpp @@ -1228,11 +1228,11 @@ namespace battleutils { if (PAttacker->objtype == TYPE_PC && PAttacker->PParty != nullptr) { - for (auto& member : PAttacker->PParty->members) + for (auto* PMember : PAttacker->PParty->members) { - PDefender->StatusEffectContainer->DelStatusEffect(EFFECT_DRAIN_DAZE, member->id); - PDefender->StatusEffectContainer->DelStatusEffect(EFFECT_HASTE_DAZE, member->id); - PDefender->StatusEffectContainer->DelStatusEffect(EFFECT_ASPIR_DAZE, member->id); + PDefender->StatusEffectContainer->DelStatusEffect(EFFECT_DRAIN_DAZE, PMember->id); + PDefender->StatusEffectContainer->DelStatusEffect(EFFECT_HASTE_DAZE, PMember->id); + PDefender->StatusEffectContainer->DelStatusEffect(EFFECT_ASPIR_DAZE, PMember->id); } } else if (PAttacker->objtype == TYPE_TRUST && PAttacker->PMaster) @@ -4344,16 +4344,16 @@ namespace battleutils // Collect all potential TA targets who are closer to the mob than the TA user for (auto&& party : taPartyList) { - for (auto&& member : party->members) + for (auto&& PMember : party->members) { - float distTAtarget = distance(member->loc.p, PMob->loc.p); + float distTAtarget = distance(PMember->loc.p, PMob->loc.p); // require closer target not be closer than .5 yalms (.5*.5=.25 distsquared) to mob if (distTAtarget >= worldAngleMinDistance && distTAtarget < distTAmob) { - taTargetList.emplace_back(distTAtarget, member); + taTargetList.emplace_back(distTAtarget, PMember); } - if (auto* PChar = dynamic_cast(member)) + if (auto* PChar = dynamic_cast(PMember)) { for (auto* PTrust : PChar->PTrusts) { @@ -4426,9 +4426,9 @@ namespace battleutils return; } - for (auto* entity : *PTarget->PNotorietyContainer) + for (auto* PEntity : *PTarget->PNotorietyContainer) { - if (CMobEntity* PCurrentMob = dynamic_cast(entity)) + if (CMobEntity* PCurrentMob = dynamic_cast(PEntity)) { if (PCurrentMob->m_HiPCLvl > 0 && PCurrentMob->PEnmityContainer->HasID(PTarget->id)) { @@ -4463,10 +4463,8 @@ namespace battleutils if (PIterSource) { - for (SpawnIDList_t::const_iterator it = PIterSource->SpawnMOBList.begin(); it != PIterSource->SpawnMOBList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(PIterSource->SpawnMOBList, CMobEntity*, PCurrentMob) { - CMobEntity* PCurrentMob = (CMobEntity*)it->second; - if (PCurrentMob->m_HiPCLvl > 0 && PCurrentMob->PEnmityContainer->HasID(PSource->id)) { PCurrentMob->PEnmityContainer->UpdateEnmity(PSource, CE, VE, false, false, false); @@ -6813,12 +6811,13 @@ namespace battleutils // If the cover ability target is in a party, try to find a cover ability user if (PCoverAbilityTarget->PParty != nullptr) { - for (auto member : PCoverAbilityTarget->PParty->members) + for (auto* PMember : PCoverAbilityTarget->PParty->members) { - if (coverAbilityTargetID == member->GetLocalVar("COVER_ABILITY_TARGET") && member->StatusEffectContainer->HasStatusEffect(EFFECT_COVER) && - member->isAlive()) + if (coverAbilityTargetID == PMember->GetLocalVar("COVER_ABILITY_TARGET") && + PMember->StatusEffectContainer->HasStatusEffect(EFFECT_COVER) && + PMember->isAlive()) { - PCoverAbilityUser = member; + PCoverAbilityUser = PMember; break; } } diff --git a/src/map/zone.cpp b/src/map/zone.cpp index b242b938e4f..1453d49e22d 100644 --- a/src/map/zone.cpp +++ b/src/map/zone.cpp @@ -339,11 +339,11 @@ bool CZone::IsWeatherStatic() const zoneLine_t* CZone::GetZoneLine(uint32 zoneLineID) { - for (zoneLineList_t::const_iterator i = m_zoneLineList.begin(); i != m_zoneLineList.end(); ++i) + for (const auto& zoneLine : m_zoneLineList) { - if ((*i)->m_zoneLineID == zoneLineID) + if (zoneLine->m_zoneLineID == zoneLineID) { - return (*i); + return zoneLine; } } return nullptr; @@ -1141,11 +1141,11 @@ void CZone::CharZoneIn(CCharEntity* PChar) void CZone::CharZoneOut(CCharEntity* PChar) { TracyZoneScoped; - for (triggerAreaList_t::const_iterator triggerAreaItr = m_triggerAreaList.begin(); triggerAreaItr != m_triggerAreaList.end(); ++triggerAreaItr) + for (const auto& triggerArea : m_triggerAreaList) { - if ((*triggerAreaItr)->GetTriggerAreaID() == PChar->m_InsideTriggerAreaID) + if (triggerArea->GetTriggerAreaID() == PChar->m_InsideTriggerAreaID) { - luautils::OnTriggerAreaLeave(PChar, *triggerAreaItr); + luautils::OnTriggerAreaLeave(PChar, triggerArea); break; } } @@ -1231,15 +1231,15 @@ void CZone::CheckTriggerAreas() // : use them here to make the search domain smaller. uint32 triggerAreaID = 0; - for (triggerAreaList_t::const_iterator triggerAreaItr = m_triggerAreaList.begin(); triggerAreaItr != m_triggerAreaList.end(); ++triggerAreaItr) + for (const auto& triggerArea : m_triggerAreaList) { - if ((*triggerAreaItr)->isPointInside(PChar->loc.p)) + if (triggerArea->isPointInside(PChar->loc.p)) { - triggerAreaID = (*triggerAreaItr)->GetTriggerAreaID(); + triggerAreaID = triggerArea->GetTriggerAreaID(); - if ((*triggerAreaItr)->GetTriggerAreaID() != PChar->m_InsideTriggerAreaID) + if (triggerArea->GetTriggerAreaID() != PChar->m_InsideTriggerAreaID) { - luautils::OnTriggerAreaEnter(PChar, *triggerAreaItr); + luautils::OnTriggerAreaEnter(PChar, triggerArea); } if (PChar->m_InsideTriggerAreaID == 0) @@ -1247,9 +1247,9 @@ void CZone::CheckTriggerAreas() break; } } - else if ((*triggerAreaItr)->GetTriggerAreaID() == PChar->m_InsideTriggerAreaID) + else if (triggerArea->GetTriggerAreaID() == PChar->m_InsideTriggerAreaID) { - luautils::OnTriggerAreaLeave(PChar, *triggerAreaItr); + luautils::OnTriggerAreaLeave(PChar, triggerArea); } } PChar->m_InsideTriggerAreaID = triggerAreaID; diff --git a/src/map/zone_entities.cpp b/src/map/zone_entities.cpp index f9d66d7f430..c499a77e45d 100644 --- a/src/map/zone_entities.cpp +++ b/src/map/zone_entities.cpp @@ -60,6 +60,8 @@ namespace { + const float ENTITY_RENDER_DISTANCE = 50.0f; + const float ENTITY_VERTICAL_RENDER_DISTANCE = 20.0f; const float CHARACTER_SYNC_DISTANCE = 45.0f; const float CHARACTER_DESPAWN_DISTANCE = 50.0f; const int CHARACTER_SWAP_MAX = 5; @@ -68,6 +70,11 @@ namespace const int CHARACTER_SYNC_PARTY_SIGNIFICANCE = 100000; const int CHARACTER_SYNC_ALLI_SIGNIFICANCE = 10000; const int PERSIST_CHECK_CHARACTERS = 20; + + inline bool isWithinVerticalDistance(CBaseEntity* source, CBaseEntity* target) + { + return abs(target->loc.p.y - source->loc.p.y - 0.5f) <= ENTITY_VERTICAL_RENDER_DISTANCE; + } } // namespace typedef std::pair CharScorePair; @@ -121,63 +128,145 @@ CZoneEntities::~CZoneEntities() void CZoneEntities::HealAllMobs() { TracyZoneScoped; - for (EntityList_t::const_iterator it = m_mobList.begin(); it != m_mobList.end(); ++it) - { - CMobEntity* PCurrentMob = (CMobEntity*)it->second; + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) + { // keep resting until i'm full PCurrentMob->Rest(1); } } +void CZoneEntities::TryAddToNearbySpawnLists(CBaseEntity* PEntity) +{ + TracyZoneScoped; + + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + { + const auto isInHeightRange = isWithinVerticalDistance(PEntity, PCurrentChar); + const auto isInRange = distance(PEntity->loc.p, PCurrentChar->loc.p) <= ENTITY_RENDER_DISTANCE; + + if (isInHeightRange && isInRange) + { + switch (PEntity->objtype) + { + case TYPE_PC: + { + PCurrentChar->SpawnPCList[PEntity->id] = PEntity; + PCurrentChar->updateEntityPacket(PEntity, ENTITY_SPAWN, UPDATE_ALL_CHAR); + break; + } + case TYPE_NPC: + { + PCurrentChar->SpawnNPCList[PEntity->id] = PEntity; + PCurrentChar->updateEntityPacket(PEntity, ENTITY_SPAWN, UPDATE_ALL_MOB); + break; + } + case TYPE_MOB: + { + PCurrentChar->SpawnMOBList[PEntity->id] = PEntity; + PCurrentChar->updateEntityPacket(PEntity, ENTITY_SPAWN, UPDATE_ALL_MOB); + break; + } + + case TYPE_PET: + { + PCurrentChar->SpawnPETList[PEntity->id] = PEntity; + PCurrentChar->updateEntityPacket(PEntity, ENTITY_SPAWN, UPDATE_ALL_MOB); + break; + } + case TYPE_TRUST: + { + PCurrentChar->SpawnTRUSTList[PEntity->id] = PEntity; + PCurrentChar->updateEntityPacket(PEntity, ENTITY_SPAWN, UPDATE_ALL_MOB); + break; + } + // case TYPE_FELLOW: + // { + // PCurrentChar->SpawnFellowList[PEntity->id] = PEntity; + // PCurrentChar->updateEntityPacket(PEntity, ENTITY_SPAWN, UPDATE_ALL_MOB); + // break; + // } + default: + return; + break; + } + } + } +} + void CZoneEntities::InsertPC(CCharEntity* PChar) { TracyZoneScoped; + PChar->loc.zone = m_zone; charTargIds.insert(PChar->targid); m_charList[PChar->targid] = PChar; + + // TODO: Do we need to force-add the entity to spawn lists? It'll happen on a char's next update anyway? + TryAddToNearbySpawnLists(PChar); + ShowDebug("CZone:: %s IncreaseZoneCounter <%u> %s", m_zone->getName(), m_charList.size(), PChar->getName()); } void CZoneEntities::InsertAlly(CBaseEntity* PMob) { TracyZoneScoped; + if ((PMob != nullptr) && (PMob->objtype == TYPE_MOB)) { PMob->loc.zone = m_zone; m_allyList[PMob->targid] = PMob; + + // TODO: Do we need to force-add the entity to spawn lists? It'll happen on a char's next update anyway? + TryAddToNearbySpawnLists(PMob); } } void CZoneEntities::InsertMOB(CBaseEntity* PMob) { TracyZoneScoped; + if ((PMob != nullptr) && (PMob->objtype == TYPE_MOB)) { PMob->loc.zone = m_zone; FindPartyForMob(PMob); m_mobList[PMob->targid] = PMob; + + // TODO: Do we need to force-add the entity to spawn lists? It'll happen on a char's next update anyway? + TryAddToNearbySpawnLists(PMob); } } void CZoneEntities::InsertNPC(CBaseEntity* PNpc) { TracyZoneScoped; + if ((PNpc != nullptr) && (PNpc->objtype == TYPE_NPC)) { PNpc->loc.zone = m_zone; if (PNpc->look.size == MODEL_SHIP) { + if (m_TransportList.contains(PNpc->targid)) + { + ShowError("Error: Inserting Transport NPC with duplicate ID!"); + } + m_TransportList[PNpc->targid] = PNpc; - return; } - if (m_npcList.contains(PNpc->targid)) + else { - ShowError("Error: Inserting NPC with duplicate ID!"); + if (m_npcList.contains(PNpc->targid)) + { + ShowError("Error: Inserting NPC with duplicate ID!"); + } + + m_npcList[PNpc->targid] = PNpc; } - m_npcList[PNpc->targid] = PNpc; + + // TODO: Do we need to force-add the entity to spawn lists? It'll happen on a char's next update anyway? + TryAddToNearbySpawnLists(PNpc); } } @@ -192,22 +281,15 @@ void CZoneEntities::DeletePET(CBaseEntity* PPet) void CZoneEntities::InsertPET(CBaseEntity* PPet) { TracyZoneScoped; + if (PPet != nullptr) { m_zone->GetZoneEntities()->AssignDynamicTargIDandLongID(PPet); m_petList[PPet->targid] = PPet; - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) - { - CCharEntity* PCurrentChar = (CCharEntity*)it->second; - - if (distance(PPet->loc.p, PCurrentChar->loc.p) <= 50) - { - PCurrentChar->SpawnPETList[PPet->id] = PPet; - PCurrentChar->updateEntityPacket(PPet, ENTITY_SPAWN, UPDATE_ALL_MOB); - } - } + // TODO: Do we need to force-add the entity to spawn lists? It'll happen on a char's next update anyway? + TryAddToNearbySpawnLists(PPet); PPet->spawnAnimation = SPAWN_ANIMATION::NORMAL; // Turn off special spawn animation @@ -219,24 +301,14 @@ void CZoneEntities::InsertPET(CBaseEntity* PPet) void CZoneEntities::InsertTRUST(CBaseEntity* PTrust) { TracyZoneScoped; + if (PTrust != nullptr) { m_zone->GetZoneEntities()->AssignDynamicTargIDandLongID(PTrust); m_trustList[PTrust->targid] = PTrust; - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) - { - CCharEntity* PCurrentChar = (CCharEntity*)it->second; - - if (distance(PTrust->loc.p, PCurrentChar->loc.p) <= 50) - { - if (PCurrentChar->targid == ((CBattleEntity*)PTrust)->PMaster->targid) - { - PCurrentChar->SpawnTRUSTList[PTrust->id] = PTrust; - } - PCurrentChar->updateEntityPacket(PTrust, ENTITY_SPAWN, UPDATE_ALL_MOB); - } - } + // TODO: Do we need to force-add the entity to spawn lists? It'll happen on a char's next update anyway? + TryAddToNearbySpawnLists(PTrust); PTrust->spawnAnimation = SPAWN_ANIMATION::NORMAL; // Turn off special spawn animation @@ -255,6 +327,7 @@ void CZoneEntities::DeleteTRUST(CBaseEntity* PTrust) void CZoneEntities::FindPartyForMob(CBaseEntity* PEntity) { TracyZoneScoped; + if (PEntity == nullptr) { ShowWarning("PEntity was null."); @@ -267,7 +340,7 @@ void CZoneEntities::FindPartyForMob(CBaseEntity* PEntity) return; } - CMobEntity* PMob = (CMobEntity*)PEntity; + CMobEntity* PMob = static_cast(PEntity); // force all mobs in a burning circle to link ZONE_TYPE zonetype = m_zone->GetTypeMask(); @@ -275,10 +348,8 @@ void CZoneEntities::FindPartyForMob(CBaseEntity* PEntity) if ((forceLink || PMob->m_Link || PMob->m_Type & MOBTYPE_BATTLEFIELD) && PMob->PParty == nullptr) { - for (EntityList_t::const_iterator it = m_mobList.begin(); it != m_mobList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) { - CMobEntity* PCurrentMob = (CMobEntity*)it->second; - if (!forceLink && !PCurrentMob->m_Link) { continue; @@ -303,10 +374,9 @@ void CZoneEntities::FindPartyForMob(CBaseEntity* PEntity) void CZoneEntities::TransportDepart(uint16 boundary, uint16 zone) { TracyZoneScoped; - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) - { - CCharEntity* PCurrentChar = (CCharEntity*)it->second; + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + { if (PCurrentChar->loc.boundary == boundary) { if (PCurrentChar->eventPreparation->targetEntity != nullptr) @@ -333,12 +403,13 @@ void CZoneEntities::TransportDepart(uint16 boundary, uint16 zone) void CZoneEntities::WeatherChange(WEATHER weather) { TracyZoneScoped; - auto element = zoneutils::GetWeatherElement(weather); - for (EntityList_t::const_iterator it = m_mobList.begin(); it != m_mobList.end(); ++it) - { - CMobEntity* PCurrentMob = (CMobEntity*)it->second; + const auto element = zoneutils::GetWeatherElement(weather); + + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) + { PCurrentMob->PAI->EventHandler.triggerListener("WEATHER_CHANGE", CLuaBaseEntity(PCurrentMob), static_cast(weather), element); + // can't detect by scent in this weather if (PCurrentMob->getMobMod(MOBMOD_DETECTION) & DETECT_SCENT) { @@ -375,30 +446,27 @@ void CZoneEntities::WeatherChange(WEATHER weather) } } - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { - CCharEntity* PChar = (CCharEntity*)it->second; - - PChar->PLatentEffectContainer->CheckLatentsWeather(weather); - PChar->PAI->EventHandler.triggerListener("WEATHER_CHANGE", CLuaBaseEntity(PChar), static_cast(weather), element); + PCurrentChar->PLatentEffectContainer->CheckLatentsWeather(weather); + PCurrentChar->PAI->EventHandler.triggerListener("WEATHER_CHANGE", CLuaBaseEntity(PCurrentChar), static_cast(weather), element); } } void CZoneEntities::MusicChange(uint16 BlockID, uint16 MusicTrackID) { - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) - { - CCharEntity* PCurrentChar = (CCharEntity*)it->second; + TracyZoneScoped; - if (PCurrentChar != nullptr) - { - PCurrentChar->pushPacket(BlockID, MusicTrackID); - } + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) + { + PChar->pushPacket(BlockID, MusicTrackID); } } void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) { + TracyZoneScoped; + if (PChar == nullptr) { ShowWarning("PChar is null."); @@ -411,14 +479,15 @@ void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) return; } - TracyZoneScoped; - battleutils::RelinquishClaim(PChar); // remove pets if (PChar->PPet != nullptr) { - charutils::BuildingCharPetAbilityTable(PChar, (CPetEntity*)PChar->PPet, 0); // blank the pet commands + auto* PPet = static_cast(PChar->PPet); + + charutils::BuildingCharPetAbilityTable(PChar, PPet, 0); // blank the pet commands + if (PChar->PPet->isCharmed) { petutils::DespawnPet(PChar); @@ -426,7 +495,7 @@ void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) else { PChar->PPet->status = STATUS_TYPE::DISAPPEAR; - if (((CPetEntity*)(PChar->PPet))->getPetType() == PET_TYPE::AVATAR) + if (static_cast(PChar->PPet)->getPetType() == PET_TYPE::AVATAR) { PChar->setModifier(Mod::AVATAR_PERPETUATION, 0); } @@ -436,15 +505,14 @@ void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) { PChar->PPet->PAI->Disengage(); - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { // inform other players of the pets removal - CCharEntity* PCurrentChar = (CCharEntity*)it->second; - SpawnIDList_t::iterator PET = PCurrentChar->SpawnPETList.find(PChar->PPet->id); + SpawnIDList_t::iterator itr = PCurrentChar->SpawnPETList.find(PChar->PPet->id); - if (PET != PCurrentChar->SpawnPETList.end()) + if (itr != PCurrentChar->SpawnPETList.end()) { - PCurrentChar->SpawnPETList.erase(PET); + PCurrentChar->SpawnPETList.erase(itr); PCurrentChar->updateEntityPacket(PChar->PPet, ENTITY_DESPAWN, UPDATE_NONE); } } @@ -452,14 +520,13 @@ void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) } } - // remove trusts - for (auto* trust : PChar->PTrusts) + // Remove trusts + for (auto* PTrust : PChar->PTrusts) { - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { - // inform other players of the pets removal - CCharEntity* PCurrentChar = (CCharEntity*)it->second; - PCurrentChar->updateEntityPacket(trust, ENTITY_DESPAWN, UPDATE_NONE); + // inform other players of the trusts removal + PCurrentChar->updateEntityPacket(PTrust, ENTITY_DESPAWN, UPDATE_NONE); } } PChar->ClearTrusts(); @@ -470,9 +537,8 @@ void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) m_zone->m_BattlefieldHandler->RemoveFromBattlefield(PChar, PChar->PBattlefield, BATTLEFIELD_LEAVE_CODE_WARPDC); } - for (auto PMobIt : m_mobList) + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) { - CMobEntity* PCurrentMob = (CMobEntity*)PMobIt.second; PCurrentMob->PEnmityContainer->LogoutReset(PChar->id); if (PCurrentMob->m_OwnerID.id == PChar->id) { @@ -587,14 +653,16 @@ bool CZoneEntities::CharListEmpty() const void CZoneEntities::DespawnPC(CCharEntity* PChar) { - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) + TracyZoneScoped; + + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { - CCharEntity* PCurrentChar = (CCharEntity*)it->second; - SpawnIDList_t::iterator PC = PCurrentChar->SpawnPCList.find(PChar->id); + const auto itr = PCurrentChar->SpawnPCList.find(PChar->id); + const auto isInSpawnList = itr != PCurrentChar->SpawnPCList.end(); - if (PC != PCurrentChar->SpawnPCList.end()) + if (isInSpawnList) { - PCurrentChar->SpawnPCList.erase(PC); + PCurrentChar->SpawnPCList.erase(itr); PCurrentChar->updateCharPacket(PChar, ENTITY_DESPAWN, UPDATE_NONE); } } @@ -603,27 +671,42 @@ void CZoneEntities::DespawnPC(CCharEntity* PChar) void CZoneEntities::SpawnMOBs(CCharEntity* PChar) { TracyZoneScoped; - for (EntityList_t::const_iterator it = m_mobList.begin(); it != m_mobList.end(); ++it) + + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) { - CMobEntity* PCurrentMob = (CMobEntity*)it->second; - SpawnIDList_t::iterator MOB = PChar->SpawnMOBList.find(PCurrentMob->id); + auto& spawnList = PChar->SpawnMOBList; - float CurrentDistance = distance(PChar->loc.p, PCurrentMob->loc.p); + const auto id = PCurrentMob->id; + const auto itr = spawnList.find(id); + const auto isInSpawnList = itr != spawnList.end(); + const auto isInHeightRange = isWithinVerticalDistance(PChar, PCurrentMob); + const auto isInRange = distance(PChar->loc.p, PCurrentMob->loc.p) <= ENTITY_RENDER_DISTANCE; + const auto isVisibleStatus = PCurrentMob->status != STATUS_TYPE::DISAPPEAR; - // Is this mob "visible" to the player? - if (PCurrentMob->status != STATUS_TYPE::DISAPPEAR && CurrentDistance <= 50) + const auto tryAddToSpawnList = [&]() { - // mob not in update list for player, add it in - if (MOB == PChar->SpawnMOBList.end()) + if (!isInSpawnList) { - PChar->SpawnMOBList.insert(MOB, SpawnIDList_t::value_type(PCurrentMob->id, PCurrentMob)); + spawnList.insert(itr, SpawnIDList_t::value_type(id, PCurrentMob)); PChar->updateEntityPacket(PCurrentMob, ENTITY_SPAWN, UPDATE_ALL_MOB); } + }; + + const auto tryRemoveFromSpawnList = [&]() + { + if (isInSpawnList) + { + spawnList.erase(itr); + PChar->updateEntityPacket(PCurrentMob, ENTITY_DESPAWN, UPDATE_NONE); + } + }; + const auto tapAggro = [&]() + { // Check to skip aggro routine if (PChar->isDead() || PChar->visibleGmLevel >= 3 || PCurrentMob->PMaster) { - continue; + return; } // checking monsters night/daytime sleep is already taken into account in the CurrentAction check, because monsters don't move in their sleep @@ -638,7 +721,7 @@ void CZoneEntities::SpawnMOBs(CCharEntity* PChar) { PController->SetFollowTarget(PChar, FollowType::Roam); } - continue; + return; } bool validAggro = mobCheck > EMobDifficulty::TooWeak || PChar->isSitting() || PCurrentMob->getMobMod(MOBMOD_ALWAYS_AGGRO); @@ -646,11 +729,19 @@ void CZoneEntities::SpawnMOBs(CCharEntity* PChar) { PCurrentMob->PAI->Engage(PChar->targid); } + }; + + // Is this mob "visible" to the player? + if (isVisibleStatus && isInHeightRange && isInRange) + { + tryAddToSpawnList(); + + // TODO: Can/should this aggro routine be moved out of here and into the entity's first tick/spawn? + tapAggro(); } - else if (MOB != PChar->SpawnMOBList.end()) + else { - PChar->SpawnMOBList.erase(MOB); - PChar->updateEntityPacket(PCurrentMob, ENTITY_DESPAWN, UPDATE_NONE); + tryRemoveFromSpawnList(); } } } @@ -659,26 +750,46 @@ void CZoneEntities::SpawnPETs(CCharEntity* PChar) { TracyZoneScoped; - for (EntityList_t::const_iterator it = m_petList.begin(); it != m_petList.end(); ++it) + // TODO: Rather than iterating every entity in the zone, we should be doing + // : spatial partitioning to only check entities within a certain range of the player. + // : This would change this loop to look like: + // : Compare previous and current spatial partitioning results to determine which entities to add/remove from the spawn list. + for (const auto& [_, PCurrentEntity] : m_petList) { - CPetEntity* PCurrentPet = (CPetEntity*)it->second; - SpawnIDList_t::iterator PET = PChar->SpawnPETList.find(PCurrentPet->id); + auto& spawnList = PChar->SpawnPETList; - // Is this pet "visible" to the player? - if ((PCurrentPet->status == STATUS_TYPE::NORMAL || PCurrentPet->status == STATUS_TYPE::UPDATE) && distance(PChar->loc.p, PCurrentPet->loc.p) <= 50) + const auto id = PCurrentEntity->id; + const auto itr = spawnList.find(id); + const auto isInSpawnList = itr != spawnList.end(); + const auto isInHeightRange = isWithinVerticalDistance(PChar, PCurrentEntity); + const auto isInRange = distance(PChar->loc.p, PCurrentEntity->loc.p) <= ENTITY_RENDER_DISTANCE; + const auto isVisibleStatus = PCurrentEntity->status == STATUS_TYPE::NORMAL || PCurrentEntity->status == STATUS_TYPE::UPDATE; + + const auto tryAddToSpawnList = [&]() + { + if (!isInSpawnList) + { + spawnList.insert(itr, SpawnIDList_t::value_type(id, PCurrentEntity)); + PChar->updateEntityPacket(PCurrentEntity, ENTITY_SPAWN, UPDATE_ALL_MOB); + } + }; + + const auto tryRemoveFromSpawnList = [&]() { - // pet not in update list for player, add it in - if (PET == PChar->SpawnPETList.end()) + if (isInSpawnList) { - PChar->SpawnPETList.insert(PET, SpawnIDList_t::value_type(PCurrentPet->id, PCurrentPet)); - PChar->updateEntityPacket(PCurrentPet, ENTITY_SPAWN, UPDATE_ALL_MOB); + spawnList.erase(itr); + PChar->updateEntityPacket(PCurrentEntity, ENTITY_DESPAWN, UPDATE_NONE); } + }; + + if (isVisibleStatus && isInHeightRange && isInRange) + { + tryAddToSpawnList(); } - // Pet not visible, remove it from spawn list if it's in there - else if (PET != PChar->SpawnPETList.end()) + else { - PChar->SpawnPETList.erase(PET); - PChar->updateEntityPacket(PCurrentPet, ENTITY_DESPAWN, UPDATE_NONE); + tryRemoveFromSpawnList(); } } } @@ -686,38 +797,52 @@ void CZoneEntities::SpawnPETs(CCharEntity* PChar) void CZoneEntities::SpawnNPCs(CCharEntity* PChar) { TracyZoneScoped; - if (!PChar->m_moghouseID) + + if (PChar->m_moghouseID) { - for (EntityList_t::const_iterator it = m_npcList.begin(); it != m_npcList.end(); ++it) - { - CNpcEntity* PCurrentNpc = (CNpcEntity*)it->second; - SpawnIDList_t::iterator NPC = PChar->SpawnNPCList.find(PCurrentNpc->id); + return; + } + + // TODO: Rather than iterating every entity in the zone, we should be doing + // : spatial partitioning to only check entities within a certain range of the player. + // : This would change this loop to look like: + // : Compare previous and current spatial partitioning results to determine which entities to add/remove from the spawn list. + for (const auto& [_, PCurrentEntity] : m_npcList) + { + auto& spawnList = PChar->SpawnNPCList; + + const auto id = PCurrentEntity->id; + const auto itr = spawnList.find(id); + const auto isInSpawnList = itr != spawnList.end(); + const auto isInHeightRange = isWithinVerticalDistance(PChar, PCurrentEntity); + const auto isInRange = distance(PChar->loc.p, PCurrentEntity->loc.p) <= ENTITY_RENDER_DISTANCE; + const auto isVisibleStatus = PCurrentEntity->status == STATUS_TYPE::NORMAL || PCurrentEntity->status == STATUS_TYPE::UPDATE; - if (PCurrentNpc->status == STATUS_TYPE::NORMAL || PCurrentNpc->status == STATUS_TYPE::UPDATE) + const auto tryAddToSpawnList = [&]() + { + if (!isInSpawnList) { - // Is this npc "visible" to the player? - if (distance(PChar->loc.p, PCurrentNpc->loc.p) <= 50) - { - // npc not in update list for player, add it in - if (NPC == PChar->SpawnNPCList.end()) - { - PChar->SpawnNPCList.insert(NPC, SpawnIDList_t::value_type(PCurrentNpc->id, PCurrentNpc)); - PChar->updateEntityPacket(PCurrentNpc, ENTITY_SPAWN, UPDATE_ALL_MOB); - } - } - // npc not visible, remove it from spawn list if it's in there - else if (NPC != PChar->SpawnNPCList.end()) - { - PChar->SpawnNPCList.erase(NPC); - PChar->updateEntityPacket(PCurrentNpc, ENTITY_DESPAWN, UPDATE_NONE); - } + spawnList.insert(itr, SpawnIDList_t::value_type(id, PCurrentEntity)); + PChar->updateEntityPacket(PCurrentEntity, ENTITY_SPAWN, UPDATE_ALL_MOB); } - // NPC not visible, remove it from spawn list if it's in there - else if (NPC != PChar->SpawnNPCList.end()) + }; + + const auto tryRemoveFromSpawnList = [&]() + { + if (isInSpawnList) { - PChar->SpawnNPCList.erase(NPC); - PChar->updateEntityPacket(PCurrentNpc, ENTITY_DESPAWN, UPDATE_NONE); + spawnList.erase(itr); + PChar->updateEntityPacket(PCurrentEntity, ENTITY_DESPAWN, UPDATE_NONE); } + }; + + if (isVisibleStatus && isInHeightRange && isInRange) + { + tryAddToSpawnList(); + } + else + { + tryRemoveFromSpawnList(); } } } @@ -725,32 +850,47 @@ void CZoneEntities::SpawnNPCs(CCharEntity* PChar) void CZoneEntities::SpawnTRUSTs(CCharEntity* PChar) { TracyZoneScoped; - for (EntityList_t::const_iterator TrustItr = m_trustList.begin(); TrustItr != m_trustList.end(); ++TrustItr) + + // TODO: Rather than iterating every entity in the zone, we should be doing + // : spatial partitioning to only check entities within a certain range of the player. + // : This would change this loop to look like: + // : Compare previous and current spatial partitioning results to determine which entities to add/remove from the spawn list. + for (const auto& [_, PCurrentEntity] : m_trustList) { - if (CTrustEntity* PCurrentTrust = dynamic_cast(TrustItr->second)) - { - SpawnIDList_t::iterator SpawnTrustItr = PChar->SpawnTRUSTList.find(PCurrentTrust->id); - CCharEntity* PMaster = dynamic_cast(PCurrentTrust->PMaster); + auto& spawnList = PChar->SpawnTRUSTList; + + const auto id = PCurrentEntity->id; + const auto itr = spawnList.find(id); + const auto isInSpawnList = itr != spawnList.end(); + const auto isInHeightRange = isWithinVerticalDistance(PChar, PCurrentEntity); + const auto isInRange = distance(PChar->loc.p, PCurrentEntity->loc.p) <= ENTITY_RENDER_DISTANCE; + const auto isVisibleStatus = PCurrentEntity->status == STATUS_TYPE::NORMAL || PCurrentEntity->status == STATUS_TYPE::UPDATE; - // Is this trust "visible" to the player? - if (PCurrentTrust->status == STATUS_TYPE::NORMAL && distance(PChar->loc.p, PCurrentTrust->loc.p) <= 50) + const auto tryAddToSpawnList = [&]() + { + if (!isInSpawnList) { - // trust not in update list for player, add it in - if (SpawnTrustItr == PChar->SpawnTRUSTList.end()) - { - PChar->SpawnTRUSTList.insert(SpawnTrustItr, SpawnIDList_t::value_type(PCurrentTrust->id, PCurrentTrust)); - if (PMaster) - { - PChar->pushPacket(PCurrentTrust); - PChar->updateEntityPacket(PCurrentTrust, ENTITY_SPAWN, UPDATE_ALL_MOB); - } - } + spawnList.insert(itr, SpawnIDList_t::value_type(id, PCurrentEntity)); + PChar->updateEntityPacket(PCurrentEntity, ENTITY_SPAWN, UPDATE_ALL_MOB); } - // trust not visible, remove it from spawn list if it's in there - else if (SpawnTrustItr != PChar->SpawnTRUSTList.end()) + }; + + const auto tryRemoveFromSpawnList = [&]() + { + if (isInSpawnList) { - PChar->SpawnTRUSTList.erase(SpawnTrustItr); + spawnList.erase(itr); + PChar->updateEntityPacket(PCurrentEntity, ENTITY_DESPAWN, UPDATE_NONE); } + }; + + if (isVisibleStatus && isInHeightRange && isInRange) + { + tryAddToSpawnList(); + } + else + { + tryRemoveFromSpawnList(); } } } @@ -783,23 +923,17 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) { TracyZoneScoped; + // TODO: This is a temporary fix so that Feretory and Mog Garden _seem_ like a solo zones. + if (PChar->loc.zone->GetID() == ZONE_FERETORY || PChar->loc.zone->GetID() == ZONE_MOG_GARDEN) + { + return; + } + // Provide bonus score to characters targeted by spawned mobs or other conflict players, if in conflict std::unordered_map scoreBonus = std::unordered_map(); - for (auto mobEntry : PChar->SpawnMOBList) + FOR_EACH_PAIR_CAST_SECOND(PChar->SpawnMOBList, CMobEntity*, PMob) { - auto* PMob = mobEntry.second; - if (PMob == nullptr) - { - // clang-format off - auto targId = mobEntry.first - 0x1000000 - (m_zone->GetID() << 12); - auto isDynamicStr = targId >= 0x700 ? "Dynamic" : "Static"; - ShowError(fmt::format("Empty {} Mob entry (targId: {}) found in {}'s SpawnMOBList in {}! This indicates an improperly cleaned-up Mob object! This is dangerous!", - isDynamicStr, targId, PChar->name, m_zone->getName()).c_str()); - // clang-format on - continue; - } - CState* state = PMob->PAI->GetCurrentState(); if (!state) { @@ -817,42 +951,29 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) MinHeap spawnedCharacters; std::vector toRemove; - for (auto iter : PChar->SpawnPCList) + FOR_EACH_PAIR_CAST_SECOND(PChar->SpawnPCList, CCharEntity*, PCurrentChar) { - CCharEntity* pc = (CCharEntity*)iter.second; - // Despawn character if it's a hidden GM, is in a different mog house, or if player is in a conflict while other is not, or too far up/down - if (pc->m_isGMHidden || PChar->m_moghouseID != pc->m_moghouseID) + if (PCurrentChar->m_isGMHidden || + PChar->m_moghouseID != PCurrentChar->m_moghouseID || + !isWithinVerticalDistance(PChar, PCurrentChar)) { - toRemove.emplace_back(pc); - continue; - } - - // TODO: This is a temporary fix so that Feretory _seems_ like a solo zone. - // We need a better solution for this that properly supports: - // - Shared moghouse (both floors) - // - Mog Garden - // - Feretory - // and the NPCs that exist per-player in those zones (Garden, Music Items in MH, etc.) - // Despawn character if it's a hidden GM, is in a different mog house, or if player is in a conflict while other is not, or too far up/down - if (PChar->loc.zone->GetID() == ZONE_FERETORY) - { - toRemove.emplace_back(pc); + toRemove.emplace_back(PCurrentChar); continue; } // Despawn character if it's currently spawned and is far away - float charDistance = distance(PChar->loc.p, pc->loc.p); + float charDistance = distance(PChar->loc.p, PCurrentChar->loc.p); if (charDistance >= CHARACTER_DESPAWN_DISTANCE) { - toRemove.emplace_back(pc); + toRemove.emplace_back(PCurrentChar); continue; } // Total score is determined by the significance between the characters, adding any bonuses, // and then subtracting the distance to make characters further away less important. - float significanceScore = getSignificanceScore(PChar, pc); - auto bonusIter = scoreBonus.find(iter.second->id); + float significanceScore = getSignificanceScore(PChar, PCurrentChar); + auto bonusIter = scoreBonus.find(PCurrentChar->id); auto bonus = bonusIter == scoreBonus.end() ? 0 : bonusIter->second; float totalScore = significanceScore + bonus - charDistance + CHARACTER_SYNC_DISTANCE_SWAP_THRESHOLD; @@ -861,17 +982,17 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) // Is spawned and should be considered for removal if necessary if (spawnedCharacters.size() < CHARACTER_SYNC_LIMIT_MAX) { - spawnedCharacters.emplace(std::make_pair(totalScore, pc)); + spawnedCharacters.emplace(std::make_pair(totalScore, PCurrentChar)); } else if (!spawnedCharacters.empty() && spawnedCharacters.top().first < totalScore) { - spawnedCharacters.emplace(std::make_pair(totalScore, pc)); + spawnedCharacters.emplace(std::make_pair(totalScore, PCurrentChar)); spawnedCharacters.pop(); } } } - for (auto removeChar : toRemove) + for (const auto& removeChar : toRemove) { PChar->updateCharPacket(removeChar, ENTITY_DESPAWN, UPDATE_NONE); PChar->SpawnPCList.erase(removeChar->id); @@ -880,10 +1001,8 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) // Find candidates to spawn MinHeap candidateCharacters; - for (auto it : m_charList) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { - CCharEntity* PCurrentChar = (CCharEntity*)it.second; - if (PCurrentChar != nullptr && PChar != PCurrentChar && PChar->SpawnPCList.find(PCurrentChar->id) == PChar->SpawnPCList.end()) { if (PCurrentChar->m_isGMHidden || PChar->m_moghouseID != PCurrentChar->m_moghouseID) @@ -933,7 +1052,7 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) uint8 swapCount = 0; // Loop through candidates to be spawned from best to worst - for (auto candidatePair : candidates) + for (const auto& [candidateScore, candidateChar] : candidates) { if (swapCount >= CHARACTER_SWAP_MAX) { @@ -951,7 +1070,7 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) // Check that the candidate score is better than the worst spawned score by a certain threshold, // to avoid causing a lot of spawn/despawns all the time as people move around. - if (candidatePair.first > spawnedCharacters.top().first) + if (candidateScore > spawnedCharacters.top().first) { CCharEntity* spawnedChar = spawnedCharacters.top().second; PChar->SpawnPCList.erase(spawnedChar->id); @@ -968,7 +1087,6 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) } // Spawn best candidate character - CCharEntity* candidateChar = candidatePair.second; PChar->SpawnPCList[candidateChar->id] = candidateChar; PChar->updateCharPacket(candidateChar, ENTITY_SPAWN, UPDATE_ALL_CHAR); PChar->pushPacket(candidateChar); @@ -1001,36 +1119,34 @@ void CZoneEntities::SpawnConditionalNPCs(CCharEntity* PChar) // NOTE: We're not changing the NPC's status to NORMAL here, because we don't want them to be visible to all players. // : We're sending updates AS IF they were visible, but only to this current player based on their conditions. - const auto toggleVisibilityForPlayer = [PChar](CNpcEntity* PNpc, bool visible) + const auto toggleVisibilityForPlayer = [PChar](CBaseEntity* PEntity, bool visible) { if (visible) { - PNpc->status = STATUS_TYPE::NORMAL; + PEntity->status = STATUS_TYPE::NORMAL; } else { - PNpc->status = STATUS_TYPE::DISAPPEAR; + PEntity->status = STATUS_TYPE::DISAPPEAR; } - PChar->updateEntityPacket(PNpc, ENTITY_SPAWN, UPDATE_ALL_MOB); - PNpc->status = STATUS_TYPE::DISAPPEAR; + PChar->updateEntityPacket(PEntity, ENTITY_SPAWN, UPDATE_ALL_MOB); + PEntity->status = STATUS_TYPE::DISAPPEAR; }; - for (EntityList_t::const_iterator it = m_npcList.begin(); it != m_npcList.end(); ++it) + for (const auto& [_, PCurrentEntity] : m_npcList) { - CNpcEntity* PCurrentNpc = (CNpcEntity*)it->second; - // TODO: Come up with a sane way to mark "You only" NPCs - if (PCurrentNpc->name == "Moogle" && PCurrentNpc->loc.p.z == 1.5 && PCurrentNpc->look.face == 0x52) + if (PCurrentEntity->name == "Moogle" && PCurrentEntity->loc.p.z == 1.5 && PCurrentEntity->look.face == 0x52) { - toggleVisibilityForPlayer(PCurrentNpc, inMogHouse && !onMH2F); + toggleVisibilityForPlayer(PCurrentEntity, inMogHouse && !onMH2F); continue; } - if (PCurrentNpc->name == "Symphonic_Curator") + if (PCurrentEntity->name == "Symphonic_Curator") { - toggleVisibilityForPlayer(PCurrentNpc, inMHinHomeNation && orchestrionPlaced); + toggleVisibilityForPlayer(PCurrentEntity, inMHinHomeNation && orchestrionPlaced); continue; } } @@ -1039,39 +1155,48 @@ void CZoneEntities::SpawnConditionalNPCs(CCharEntity* PChar) void CZoneEntities::SpawnTransport(CCharEntity* PChar) { TracyZoneScoped; - for (auto transport : m_TransportList) + + FOR_EACH_PAIR_CAST_SECOND(m_TransportList, CNpcEntity*, PEntity) { - PChar->updateEntityPacket(transport.second, ENTITY_SPAWN, UPDATE_ALL_MOB); + PChar->updateEntityPacket(PEntity, ENTITY_SPAWN, UPDATE_ALL_MOB); } } CBaseEntity* CZoneEntities::GetEntity(uint16 targid, uint8 filter) { TracyZoneScoped; + + const auto findEntity = [&](const EntityList_t& entityList) -> CBaseEntity* + { + const auto it = entityList.find(targid); + if (it != entityList.end()) + { + return it->second; + } + return nullptr; + }; + if (targid < 0x400) { if (filter & TYPE_MOB) { - EntityList_t::const_iterator it = m_mobList.find(targid); - if (it != m_mobList.end()) + if (auto* PEntity = findEntity(m_mobList)) { - return it->second; + return PEntity; } } if (filter & TYPE_NPC) { - EntityList_t::const_iterator it = m_npcList.find(targid); - if (it != m_npcList.end()) + if (auto* PEntity = findEntity(m_npcList)) { - return it->second; + return PEntity; } } if (filter & TYPE_SHIP) { - EntityList_t::const_iterator it = m_TransportList.find(targid); - if (it != m_TransportList.end()) + if (auto* PEntity = findEntity(m_TransportList)) { - return it->second; + return PEntity; } } } @@ -1079,10 +1204,9 @@ CBaseEntity* CZoneEntities::GetEntity(uint16 targid, uint8 filter) { if (filter & TYPE_PC) { - EntityList_t::const_iterator it = m_charList.find(targid); - if (it != m_charList.end()) + if (auto* PEntity = findEntity(m_charList)) { - return it->second; + return PEntity; } } } @@ -1090,34 +1214,30 @@ CBaseEntity* CZoneEntities::GetEntity(uint16 targid, uint8 filter) { if (filter & TYPE_PET) { - EntityList_t::const_iterator it = m_petList.find(targid); - if (it != m_petList.end()) + if (auto* PEntity = findEntity(m_petList)) { - return it->second; + return PEntity; } } if (filter & TYPE_TRUST) { - EntityList_t::const_iterator it = m_trustList.find(targid); - if (it != m_trustList.end()) + if (auto* PEntity = findEntity(m_trustList)) { - return it->second; + return PEntity; } } if (filter & TYPE_NPC) { - EntityList_t::const_iterator it = m_npcList.find(targid); - if (it != m_npcList.end()) + if (auto* PEntity = findEntity(m_npcList)) { - return it->second; + return PEntity; } } if (filter & TYPE_MOB) { - EntityList_t::const_iterator it = m_mobList.find(targid); - if (it != m_mobList.end()) + if (auto* PEntity = findEntity(m_mobList)) { - return it->second; + return PEntity; } } } @@ -1132,6 +1252,7 @@ CBaseEntity* CZoneEntities::GetEntity(uint16 targid, uint8 filter) void CZoneEntities::TOTDChange(TIMETYPE TOTD) { TracyZoneScoped; + SCRIPTTYPE ScriptType = SCRIPT_NONE; switch (TOTD) @@ -1142,10 +1263,8 @@ void CZoneEntities::TOTDChange(TIMETYPE TOTD) break; case TIME_NEWDAY: { - for (EntityList_t::const_iterator it = m_mobList.begin(); it != m_mobList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) { - CMobEntity* PMob = (CMobEntity*)it->second; - if (PMob->m_SpawnType & SPAWNTYPE_ATNIGHT) { PMob->SetDespawnTime(1ms); @@ -1158,10 +1277,8 @@ void CZoneEntities::TOTDChange(TIMETYPE TOTD) { ScriptType = SCRIPT_TIME_DAWN; - for (EntityList_t::const_iterator it = m_mobList.begin(); it != m_mobList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) { - CMobEntity* PMob = (CMobEntity*)it->second; - if (PMob->m_SpawnType & SPAWNTYPE_ATEVENING) { PMob->SetDespawnTime(1ms); @@ -1184,10 +1301,8 @@ void CZoneEntities::TOTDChange(TIMETYPE TOTD) { ScriptType = SCRIPT_TIME_EVENING; - for (EntityList_t::const_iterator it = m_mobList.begin(); it != m_mobList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) { - CMobEntity* PMob = (CMobEntity*)it->second; - if (PMob->m_SpawnType & SPAWNTYPE_ATEVENING) { PMob->SetDespawnTime(0s); @@ -1199,10 +1314,8 @@ void CZoneEntities::TOTDChange(TIMETYPE TOTD) break; case TIME_NIGHT: { - for (EntityList_t::const_iterator it = m_mobList.begin(); it != m_mobList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) { - CMobEntity* PMob = (CMobEntity*)it->second; - if (PMob->m_SpawnType & SPAWNTYPE_ATNIGHT) { PMob->SetDespawnTime(0s); @@ -1217,9 +1330,9 @@ void CZoneEntities::TOTDChange(TIMETYPE TOTD) } if (ScriptType != SCRIPT_NONE) { - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) { - charutils::CheckEquipLogic((CCharEntity*)it->second, ScriptType, TOTD); + charutils::CheckEquipLogic(PChar, ScriptType, TOTD); } } } @@ -1227,29 +1340,22 @@ void CZoneEntities::TOTDChange(TIMETYPE TOTD) void CZoneEntities::SavePlayTime() { TracyZoneScoped; - if (!m_charList.empty()) + + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) { - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) - { - CCharEntity* PChar = (CCharEntity*)it->second; - charutils::SavePlayTime(PChar); - } + charutils::SavePlayTime(PChar); } } CCharEntity* CZoneEntities::GetCharByName(const std::string& name) { TracyZoneScoped; - if (!m_charList.empty()) + + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) + if (strcmpi(PCurrentChar->getName().c_str(), name.c_str()) == 0) { - CCharEntity* PCurrentChar = (CCharEntity*)it->second; - - if (strcmpi(PCurrentChar->getName().c_str(), name.c_str()) == 0) - { - return PCurrentChar; - } + return PCurrentChar; } } return nullptr; @@ -1258,11 +1364,12 @@ CCharEntity* CZoneEntities::GetCharByName(const std::string& name) CCharEntity* CZoneEntities::GetCharByID(uint32 id) { TracyZoneScoped; - for (auto PChar : m_charList) + + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { - if (PChar.second->id == id) + if (PCurrentChar->id == id) { - return (CCharEntity*)PChar.second; + return PCurrentChar; } } return nullptr; @@ -1278,12 +1385,12 @@ void CZoneEntities::UpdateCharPacket(CCharEntity* PChar, ENTITYUPDATE type, uint return; } - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { - CCharEntity* PCurrentChar = (CCharEntity*)it->second; - if (PCurrentChar == PChar) + { continue; + } if (type == ENTITY_SPAWN || type == ENTITY_DESPAWN || PCurrentChar->SpawnPCList.find(PChar->id) != PCurrentChar->SpawnPCList.end()) { @@ -1296,10 +1403,8 @@ void CZoneEntities::UpdateEntityPacket(CBaseEntity* PEntity, ENTITYUPDATE type, { TracyZoneScoped; - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { - CCharEntity* PCurrentChar = (CCharEntity*)it->second; - if (alwaysInclude || type == ENTITY_SPAWN || type == ENTITY_DESPAWN || charutils::hasEntitySpawned(PCurrentChar, PEntity)) { PCurrentChar->updateEntityPacket(PEntity, type, updatemask); @@ -1318,10 +1423,12 @@ void CZoneEntities::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message } // Do not send packets that are updates of a hidden GM.. - if (packet->getType() == 0x00D && PEntity != nullptr && PEntity->objtype == TYPE_PC && ((CCharEntity*)PEntity)->m_isGMHidden) + if (packet->getType() == 0x00D && PEntity != nullptr && PEntity->objtype == TYPE_PC) { + auto* PChar = static_cast(PEntity); + // Ensure this packet is not despawning us.. - if (packet->ref(0x0A) != 0x20) + if (PChar->m_isGMHidden && packet->ref(0x0A) != 0x20) { return; } @@ -1344,17 +1451,16 @@ void CZoneEntities::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message case CHAR_INRANGE: { TracyZoneCString("CHAR_INRANGE"); - // todo: rewrite packet handlers and use enums instead of rawdog packet ids + // TODO: rewrite packet handlers and use enums instead of rawdog packet ids // 30 yalms if action packet, 50 otherwise const int checkDistanceSq = packet->getType() == 0x0028 ? 900 : 2500; - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { - CCharEntity* PCurrentChar = (CCharEntity*)it->second; if (PEntity != PCurrentChar) { if (distanceSquared(PEntity->loc.p, PCurrentChar->loc.p) < checkDistanceSq && - ((PEntity->objtype != TYPE_PC) || (((CCharEntity*)PEntity)->m_moghouseID == PCurrentChar->m_moghouseID))) + (PEntity->objtype != TYPE_PC || static_cast(PEntity)->m_moghouseID == PCurrentChar->m_moghouseID)) { uint16 packetType = packet->getType(); if @@ -1395,8 +1501,8 @@ void CZoneEntities::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message auto pushPacketIfInSpawnList = [&](CCharEntity* PChar, SpawnIDList_t const& spawnlist) { - SpawnIDList_t::const_iterator iter = spawnlist.lower_bound(id); - if (!(iter == spawnlist.end() || spawnlist.key_comp()(id, iter->first))) + auto iter = spawnlist.lower_bound(id); + if (iter != spawnlist.end() && !spawnlist.key_comp()(id, iter->first)) { PCurrentChar->pushPacket(packet->copy()); } @@ -1435,13 +1541,12 @@ void CZoneEntities::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message case CHAR_INSHOUT: { TracyZoneCString("CHAR_INSHOUT"); - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { - CCharEntity* PCurrentChar = (CCharEntity*)it->second; if (PEntity != PCurrentChar) { - if (distance(PEntity->loc.p, PCurrentChar->loc.p) < 180 && - ((PEntity->objtype != TYPE_PC) || (((CCharEntity*)PEntity)->m_moghouseID == PCurrentChar->m_moghouseID))) + if (distance(PEntity->loc.p, PCurrentChar->loc.p) < 180.0f && + (PEntity->objtype != TYPE_PC || static_cast(PEntity)->m_moghouseID == PCurrentChar->m_moghouseID)) { PCurrentChar->pushPacket(packet->copy()); } @@ -1452,10 +1557,8 @@ void CZoneEntities::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message case CHAR_INZONE: { TracyZoneCString("CHAR_INZONE"); - for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { - CCharEntity* PCurrentChar = (CCharEntity*)it->second; - if (PCurrentChar->m_moghouseID == 0) { if (PEntity != PCurrentChar) @@ -1474,21 +1577,16 @@ void CZoneEntities::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message void CZoneEntities::WideScan(CCharEntity* PChar, uint16 radius) { TracyZoneScoped; + PChar->pushPacket(WIDESCAN_BEGIN); - for (EntityList_t::const_iterator it = m_npcList.begin(); it != m_npcList.end(); ++it) - { - CNpcEntity* PNpc = (CNpcEntity*)it->second; - if (PNpc->isWideScannable() && distance(PChar->loc.p, PNpc->loc.p) < radius) - { - PChar->pushPacket(PChar, PNpc); - } - } - for (EntityList_t::const_iterator it = m_mobList.begin(); it != m_mobList.end(); ++it) + for (const auto& entityList : { m_npcList, m_mobList }) { - CMobEntity* PMob = (CMobEntity*)it->second; - if (PMob->isWideScannable() && distance(PChar->loc.p, PMob->loc.p) < radius) + for (const auto& [_, PEntity] : entityList) { - PChar->pushPacket(PChar, PMob); + if (PEntity->isWideScannable() && distance(PChar->loc.p, PEntity->loc.p) < radius) + { + PChar->pushPacket(PChar, PEntity); + } } } PChar->pushPacket(WIDESCAN_END); @@ -1501,14 +1599,17 @@ void CZoneEntities::ZoneServer(time_point tick) luautils::OnZoneTick(this->m_zone); + std::vector mobsToDelete; + std::vector npcsToDelete; + std::vector petsToDelete; + std::vector trustsToDelete; + std::vector aggroableMobs; - EntityList_t::iterator it = m_mobList.begin(); - while (it != m_mobList.end()) + + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) { - CMobEntity* PMob = dynamic_cast(it->second); if (!PMob) { - ++it; continue; } @@ -1516,7 +1617,6 @@ void CZoneEntities::ZoneServer(time_point tick) if (PMob->PBattlefield && PMob->PBattlefield->CanCleanup()) { - ++it; continue; } @@ -1530,6 +1630,7 @@ void CZoneEntities::ZoneServer(time_point tick) PMob->PAI->Tick(tick); + // This is only valid for dynamic entities if (PMob->status == STATUS_TYPE::DISAPPEAR && PMob->m_bReleaseTargIDOnDisappear) { if (PMob->PPet != nullptr) @@ -1542,10 +1643,9 @@ void CZoneEntities::ZoneServer(time_point tick) PMob->PMaster->PPet = nullptr; } - for (auto PMobIt : m_mobList) + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, POtherMob) { - CMobEntity* PCurrentMob = static_cast(PMobIt.second); - PCurrentMob->PEnmityContainer->Clear(PMob->id); + POtherMob->PEnmityContainer->Clear(PMob->id); } if (PMob->PParty) @@ -1553,11 +1653,8 @@ void CZoneEntities::ZoneServer(time_point tick) PMob->PParty->RemoveMember(PMob); } - for (EntityList_t::const_iterator charIterator = m_charList.begin(); charIterator != m_charList.end(); ++charIterator) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) { - // This is safe because m_charList only receives inserts of CCharEntity - CCharEntity* PChar = static_cast(charIterator->second); - if (PChar->PClaimedMob == PMob) { PChar->PClaimedMob = nullptr; @@ -1574,10 +1671,7 @@ void CZoneEntities::ZoneServer(time_point tick) } } - it->second = nullptr; - m_mobList.erase(it++); - dynamicTargIdsToDelete.emplace_back(PMob->targid, server_clock::now()); - destroy(PMob); + mobsToDelete.emplace_back(PMob); continue; } @@ -1585,17 +1679,17 @@ void CZoneEntities::ZoneServer(time_point tick) { aggroableMobs.emplace_back(PMob); } - - ++it; } // Check to see if any aggroable mobs should be aggroed by other mobs for (CMobEntity* PMob : aggroableMobs) { - for (auto PMobCurrentIter : m_mobList) + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) { - CMobEntity* PCurrentMob = dynamic_cast(PMobCurrentIter.second); - if (PCurrentMob != nullptr && PCurrentMob->isAlive() && PMob->allegiance != PCurrentMob->allegiance && distance(PMob->loc.p, PCurrentMob->loc.p) <= 50) + const auto isInHeightRange = isWithinVerticalDistance(PMob, PCurrentMob); + const auto isInRange = distance(PMob->loc.p, PCurrentMob->loc.p) <= ENTITY_RENDER_DISTANCE; + + if (PCurrentMob != nullptr && PCurrentMob->isAlive() && PMob->allegiance != PCurrentMob->allegiance && isInHeightRange && isInRange) { CMobController* PController = static_cast(PCurrentMob->PAI->GetController()); if (PController != nullptr && PController->CanAggroTarget(PMob)) @@ -1606,11 +1700,8 @@ void CZoneEntities::ZoneServer(time_point tick) } } - it = m_npcList.begin(); - while (it != m_npcList.end()) + FOR_EACH_PAIR_CAST_SECOND(m_npcList, CNpcEntity*, PNpc) { - CNpcEntity* PNpc = (CNpcEntity*)it->second; - ShowTrace(fmt::format("CZoneEntities::ZoneServer: NPC: {} ({})", PNpc->getName(), PNpc->id).c_str()); PNpc->PAI->Tick(tick); @@ -1618,116 +1709,84 @@ void CZoneEntities::ZoneServer(time_point tick) // This is only valid for dynamic entities if (PNpc->status == STATUS_TYPE::DISAPPEAR && PNpc->m_bReleaseTargIDOnDisappear) { - for (EntityList_t::const_iterator charIterator = m_charList.begin(); charIterator != m_charList.end(); ++charIterator) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) { - // This is safe because m_charList only receives inserts of CCharEntity - CCharEntity* PChar = static_cast(charIterator->second); if (PChar->SpawnNPCList.find(PNpc->id) != PChar->SpawnNPCList.end()) { PChar->SpawnNPCList.erase(PNpc->id); } } - destroy(it->second); - dynamicTargIdsToDelete.emplace_back(it->first, server_clock::now()); - - m_npcList.erase(it++); + npcsToDelete.emplace_back(PNpc); continue; } - ++it; } - it = m_petList.begin(); - while (it != m_petList.end()) + FOR_EACH_PAIR_CAST_SECOND(m_petList, CPetEntity*, PPet) { - // TODO: This static cast includes Battlefield Allies. Allies shouldn't be handled here in + // TODO: The static_cast in this loop includes Battlefield Allies. Allies shouldn't be handled here in // : this way, but we need to do this to keep allies working (for now). - if (auto* PPet = static_cast(it->second)) - { - ShowTrace(fmt::format("CZoneEntities::ZoneServer: Pet: {} ({})", PPet->getName(), PPet->id).c_str()); + ShowTrace(fmt::format("CZoneEntities::ZoneServer: Pet: {} ({})", PPet->getName(), PPet->id).c_str()); - /* - * Pets specifically need to be removed prior to evaluating their AI Tick - * to prevent a number of issues which can result as a Pet having a - * deleted/nullptr'd PMaster - */ - if (PPet->status == STATUS_TYPE::DISAPPEAR) - { - for (auto PMobIt : m_mobList) - { - CMobEntity* PCurrentMob = (CMobEntity*)PMobIt.second; - PCurrentMob->PEnmityContainer->Clear(PPet->id); - } + // TODO: Is this still necessary? - if (PPet->getPetType() != PET_TYPE::AUTOMATON || !PPet->PMaster) - { - destroy(it->second); - } - - dynamicTargIdsToDelete.emplace_back(it->first, server_clock::now()); - - m_petList.erase(it++); - continue; - } - - PPet->PRecastContainer->Check(); - PPet->StatusEffectContainer->CheckEffectsExpiry(tick); - if (tick > m_EffectCheckTime) + // Pets specifically need to have their AI tick skipped if they're marked for deletion + // to prevent a number of issues which can result from a pet having a deleted/nullptr'd PMaster + if (PPet->status == STATUS_TYPE::DISAPPEAR) + { + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) { - PPet->StatusEffectContainer->TickRegen(tick); - PPet->StatusEffectContainer->TickEffects(tick); + PCurrentMob->PEnmityContainer->Clear(PPet->id); } - PPet->PAI->Tick(tick); + + petsToDelete.emplace_back(PPet); + continue; } - ++it; + + PPet->PRecastContainer->Check(); + PPet->StatusEffectContainer->CheckEffectsExpiry(tick); + if (tick > m_EffectCheckTime) + { + PPet->StatusEffectContainer->TickRegen(tick); + PPet->StatusEffectContainer->TickEffects(tick); + } + + PPet->PAI->Tick(tick); } - it = m_trustList.begin(); - while (it != m_trustList.end()) + FOR_EACH_PAIR_CAST_SECOND(m_trustList, CTrustEntity*, PTrust) { - if (CTrustEntity* PTrust = dynamic_cast(it->second)) + ShowTrace(fmt::format("CZoneEntities::ZoneServer: Trust: {} ({})", PTrust->getName(), PTrust->id).c_str()); + + PTrust->PRecastContainer->Check(); + PTrust->StatusEffectContainer->CheckEffectsExpiry(tick); + if (tick > m_EffectCheckTime) { - ShowTrace(fmt::format("CZoneEntities::ZoneServer: Trust: {} ({})", PTrust->getName(), PTrust->id).c_str()); + PTrust->StatusEffectContainer->TickRegen(tick); + PTrust->StatusEffectContainer->TickEffects(tick); + } - PTrust->PRecastContainer->Check(); - PTrust->StatusEffectContainer->CheckEffectsExpiry(tick); - if (tick > m_EffectCheckTime) + PTrust->PAI->Tick(tick); + + if (PTrust->status == STATUS_TYPE::DISAPPEAR) + { + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) { - PTrust->StatusEffectContainer->TickRegen(tick); - PTrust->StatusEffectContainer->TickEffects(tick); + PCurrentMob->PEnmityContainer->Clear(PTrust->id); } - PTrust->PAI->Tick(tick); - if (PTrust->status == STATUS_TYPE::DISAPPEAR) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) { - for (auto& list : { m_mobList, m_trustList }) // Remove from Mobs and Trusts + if (distance(PChar->loc.p, PTrust->loc.p) < ENTITY_RENDER_DISTANCE) { - for (auto& itr : list) - { - CMobEntity* PCurrentMob = static_cast(itr.second); // Force cast to CMobEntity* - PCurrentMob->PEnmityContainer->Clear(PTrust->id); - } + PChar->SpawnTRUSTList.erase(PTrust->id); + PChar->ReloadPartyInc(); } - - for (EntityList_t::const_iterator charIterator = m_charList.begin(); charIterator != m_charList.end(); ++charIterator) - { - // This is safe because m_charList only receives inserts of CCharEntity - CCharEntity* PChar = static_cast(charIterator->second); - if (distance(PChar->loc.p, PTrust->loc.p) < 50) - { - PChar->SpawnTRUSTList.erase(PTrust->id); - PChar->ReloadPartyInc(); - } - } - - destroy(it->second); - dynamicTargIdsToDelete.emplace_back(it->first, server_clock::now()); - - m_trustList.erase(it++); - continue; } + + trustsToDelete.emplace_back(PTrust); + continue; } - ++it; } // Store some lists for chars that may need post-processing for effects that could delete them from m_charList and cause crashes @@ -1735,11 +1794,8 @@ void CZoneEntities::ZoneServer(time_point tick) std::vector charsToWarp = {}; std::vector charsToChangeZone = {}; - for (EntityList_t::const_iterator charIterator = m_charList.begin(); charIterator != m_charList.end(); ++charIterator) + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) { - // This is safe because m_charList only receives inserts of CCharEntity - CCharEntity* PChar = static_cast(charIterator->second); - ShowTrace(fmt::format("CZoneEntities::ZoneServer: Char: {} ({})", PChar->getName(), PChar->id).c_str()); if (PChar->status != STATUS_TYPE::SHUTDOWN) @@ -1751,6 +1807,7 @@ void CZoneEntities::ZoneServer(time_point tick) PChar->StatusEffectContainer->TickRegen(tick); PChar->StatusEffectContainer->TickEffects(tick); } + PChar->PAI->Tick(tick); if (PChar->PTreasurePool) @@ -1775,22 +1832,62 @@ void CZoneEntities::ZoneServer(time_point tick) } } + for (auto* PMob : mobsToDelete) + { + if (auto itr = m_mobList.find(PMob->id); itr != m_mobList.end()) + { + m_mobList.erase(itr); + dynamicTargIdsToDelete.emplace_back(PMob->targid, server_clock::now()); + destroy(PMob); + } + } + + for (auto* PNpc : npcsToDelete) + { + if (auto itr = m_npcList.find(PNpc->id); itr != m_npcList.end()) + { + m_npcList.erase(itr); + dynamicTargIdsToDelete.emplace_back(PNpc->targid, server_clock::now()); + destroy(PNpc); + } + } + + for (auto* PPet : petsToDelete) + { + if (auto itr = m_petList.find(PPet->id); itr != m_petList.end()) + { + m_petList.erase(itr); + dynamicTargIdsToDelete.emplace_back(PPet->targid, server_clock::now()); + destroy(PPet); + } + } + + for (auto* PTrust : trustsToDelete) + { + if (auto itr = m_trustList.find(PTrust->id); itr != m_trustList.end()) + { + m_trustList.erase(itr); + dynamicTargIdsToDelete.emplace_back(PTrust->targid, server_clock::now()); + destroy(PTrust); + } + } + // forceLogout eventually removes the char from m_charList -- so we must remove them here - for (auto PChar : charsToLogout) + for (auto* PChar : charsToLogout) { PChar->clearPacketList(); charutils::ForceLogout(PChar); } // Warp players (do not recover HP/MP) - for (auto PChar : charsToWarp) + for (auto* PChar : charsToWarp) { PChar->clearPacketList(); charutils::HomePoint(PChar, false); } // Change player's zone (teleports, etc) - for (auto PChar : charsToChangeZone) + for (auto* PChar : charsToChangeZone) { PChar->clearPacketList(); @@ -1825,7 +1922,7 @@ void CZoneEntities::ZoneServer(time_point tick) for (size_t i = 0; i < maxChecks; i++) { - CCharEntity* pc = (CCharEntity*)m_charList[*charTargIdIter]; + CCharEntity* pc = static_cast(m_charList[*charTargIdIter]); charTargIdIter++; if (charTargIdIter == charTargIds.end()) { diff --git a/src/map/zone_entities.h b/src/map/zone_entities.h index fb5ad295b3f..7da2aedf09d 100644 --- a/src/map/zone_entities.h +++ b/src/map/zone_entities.h @@ -31,6 +31,7 @@ class CZoneEntities { public: void HealAllMobs(); + void TryAddToNearbySpawnLists(CBaseEntity* PEntity); CCharEntity* GetCharByName(const std::string& name); // finds the player if exists in zone CCharEntity* GetCharByID(uint32 id); diff --git a/src/map/zone_instance.cpp b/src/map/zone_instance.cpp index 1c9b3302c72..5eca9694012 100644 --- a/src/map/zone_instance.cpp +++ b/src/map/zone_instance.cpp @@ -461,15 +461,15 @@ void CZoneInstance::CheckTriggerAreas() // : use them here to make the search domain smaller. uint32 triggerAreaID = 0; - for (triggerAreaList_t::const_iterator triggerAreaItr = m_triggerAreaList.begin(); triggerAreaItr != m_triggerAreaList.end(); ++triggerAreaItr) + for (const auto& triggerArea : m_triggerAreaList) { - if ((*triggerAreaItr)->isPointInside(PChar->loc.p)) + if (triggerArea->isPointInside(PChar->loc.p)) { - triggerAreaID = (*triggerAreaItr)->GetTriggerAreaID(); + triggerAreaID = triggerArea->GetTriggerAreaID(); - if ((*triggerAreaItr)->GetTriggerAreaID() != PChar->m_InsideTriggerAreaID) + if (triggerArea->GetTriggerAreaID() != PChar->m_InsideTriggerAreaID) { - luautils::OnTriggerAreaEnter(PChar, *triggerAreaItr); + luautils::OnTriggerAreaEnter(PChar, triggerArea); } if (PChar->m_InsideTriggerAreaID == 0) @@ -477,9 +477,9 @@ void CZoneInstance::CheckTriggerAreas() break; } } - else if ((*triggerAreaItr)->GetTriggerAreaID() == PChar->m_InsideTriggerAreaID) + else if (triggerArea->GetTriggerAreaID() == PChar->m_InsideTriggerAreaID) { - luautils::OnTriggerAreaLeave(PChar, *triggerAreaItr); + luautils::OnTriggerAreaLeave(PChar, triggerArea); } } PChar->m_InsideTriggerAreaID = triggerAreaID; From 92286c81d65549fe12de1f30078473493435c75a Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 20 Jan 2025 00:23:33 +0000 Subject: [PATCH 2/5] Core: Lift intermediate containers out of ZoneServer --- src/map/map.cpp | 7 +- src/map/zone.cpp | 35 +++---- src/map/zone_entities.cpp | 194 ++++++++++++++++++++------------------ src/map/zone_entities.h | 36 +++++-- 4 files changed, 147 insertions(+), 125 deletions(-) diff --git a/src/map/map.cpp b/src/map/map.cpp index 706bda98301..e24a5c56be0 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -1376,15 +1376,14 @@ int32 map_cleanup(time_point tick, CTaskMgr::CTask* PTask) // clang-format off zoneutils::ForEachZone([](CZone* PZone) { - auto& staledynamicTargIds = PZone->GetZoneEntities()->dynamicTargIdsToDelete; + auto& staledynamicTargIds = PZone->GetZoneEntities()->m_dynamicTargIdsToDelete; - auto it = staledynamicTargIds.begin(); - while(it != staledynamicTargIds.end()) + for (auto it = staledynamicTargIds.begin(); it != staledynamicTargIds.end();) { // Erase dynamic targid if it's stale enough if ((server_clock::now() - it->second) > 60s) { - PZone->GetZoneEntities()->dynamicTargIds.erase(it->first); + PZone->GetZoneEntities()->m_dynamicTargIds.erase(it->first); it = staledynamicTargIds.erase(it); } else diff --git a/src/map/zone.cpp b/src/map/zone.cpp index 1453d49e22d..33eebf07c5f 100644 --- a/src/map/zone.cpp +++ b/src/map/zone.cpp @@ -959,63 +959,54 @@ void CZone::ZoneServer(time_point tick) void CZone::ForEachChar(std::function const& func) { TracyZoneScoped; - for (auto PChar : m_zoneEntities->GetCharList()) + FOR_EACH_PAIR_CAST_SECOND(m_zoneEntities->m_charList, CCharEntity*, PChar) { - func((CCharEntity*)PChar.second); + func(PChar); } } void CZone::ForEachCharInstance(CBaseEntity* PEntity, std::function const& func) { TracyZoneScoped; - for (auto PChar : m_zoneEntities->GetCharList()) - { - func((CCharEntity*)PChar.second); - } + ForEachChar(func); } void CZone::ForEachMob(std::function const& func) { TracyZoneScoped; - for (auto PMob : m_zoneEntities->m_mobList) + FOR_EACH_PAIR_CAST_SECOND(m_zoneEntities->m_mobList, CMobEntity*, PMob) { - func((CMobEntity*)PMob.second); + func(PMob); } } void CZone::ForEachMobInstance(CBaseEntity* PEntity, std::function const& func) { TracyZoneScoped; - for (auto PMob : m_zoneEntities->m_mobList) - { - func((CMobEntity*)PMob.second); - } + ForEachMob(func); } void CZone::ForEachTrust(std::function const& func) { TracyZoneScoped; - for (auto PTrust : m_zoneEntities->m_trustList) + FOR_EACH_PAIR_CAST_SECOND(m_zoneEntities->m_trustList, CTrustEntity*, PTrust) { - func((CTrustEntity*)PTrust.second); + func(PTrust); } } void CZone::ForEachTrustInstance(CBaseEntity* PEntity, std::function const& func) { TracyZoneScoped; - for (auto PTrust : m_zoneEntities->m_trustList) - { - func((CTrustEntity*)PTrust.second); - } + ForEachTrust(func); } void CZone::ForEachNpc(std::function const& func) { TracyZoneScoped; - for (auto PNpc : m_zoneEntities->m_npcList) + FOR_EACH_PAIR_CAST_SECOND(m_zoneEntities->m_npcList, CNpcEntity*, PNpc) { - func((CNpcEntity*)PNpc.second); + func(PNpc); } } @@ -1223,10 +1214,8 @@ void CZone::CheckTriggerAreas() { TracyZoneScoped; - for (auto const& [targid, PEntity] : m_zoneEntities->m_charList) + FOR_EACH_PAIR_CAST_SECOND(m_zoneEntities->m_charList, CCharEntity*, PChar) { - auto* PChar = static_cast(PEntity); - // TODO: When we start to use octrees or spatial hashing to split up zones, // : use them here to make the search domain smaller. diff --git a/src/map/zone_entities.cpp b/src/map/zone_entities.cpp index c499a77e45d..752f3f77ac7 100644 --- a/src/map/zone_entities.cpp +++ b/src/map/zone_entities.cpp @@ -80,11 +80,20 @@ namespace typedef std::pair CharScorePair; CZoneEntities::CZoneEntities(CZone* zone) -: nextDynamicTargID(0x700) // Start of dynamic entity range // TODO: Make this into a constexpr somewhere. +: m_nextDynamicTargID(0x700) // Start of dynamic entity range // TODO: Make this into a constexpr somewhere. , m_zone(zone) -, lastCharComputeTargId(0) -, lastCharPersistTargId(0) +, m_lastCharComputeTargId(0) +, m_lastCharPersistTargId(0) { + // Ensure internal collections have enough capacity so they won't resize at runtime. + m_mobsToDelete.reserve(16); + m_npcsToDelete.reserve(16); + m_petsToDelete.reserve(16); + m_trustsToDelete.reserve(16); + m_aggroableMobs.reserve(16); + m_charsToLogout.reserve(16); + m_charsToWarp.reserve(16); + m_charsToChangeZone.reserve(16); } CZoneEntities::~CZoneEntities() @@ -199,7 +208,7 @@ void CZoneEntities::InsertPC(CCharEntity* PChar) TracyZoneScoped; PChar->loc.zone = m_zone; - charTargIds.insert(PChar->targid); + m_charTargIds.insert(PChar->targid); m_charList[PChar->targid] = PChar; // TODO: Do we need to force-add the entity to spawn lists? It'll happen on a char's next update anyway? @@ -212,7 +221,7 @@ void CZoneEntities::InsertAlly(CBaseEntity* PMob) { TracyZoneScoped; - if ((PMob != nullptr) && (PMob->objtype == TYPE_MOB)) + if (PMob != nullptr && PMob->objtype == TYPE_MOB) { PMob->loc.zone = m_zone; m_allyList[PMob->targid] = PMob; @@ -226,7 +235,7 @@ void CZoneEntities::InsertMOB(CBaseEntity* PMob) { TracyZoneScoped; - if ((PMob != nullptr) && (PMob->objtype == TYPE_MOB)) + if (PMob != nullptr && PMob->objtype == TYPE_MOB) { PMob->loc.zone = m_zone; @@ -242,7 +251,7 @@ void CZoneEntities::InsertNPC(CBaseEntity* PNpc) { TracyZoneScoped; - if ((PNpc != nullptr) && (PNpc->objtype == TYPE_NPC)) + if (PNpc != nullptr && PNpc->objtype == TYPE_NPC) { PNpc->loc.zone = m_zone; @@ -395,6 +404,7 @@ void CZoneEntities::TransportDepart(uint16 boundary, uint16 zone) PCurrentChar->eventPreparation->scriptFile.replace(deleteStart, deleteEnd - deleteStart, "Zone"); } } + luautils::OnTransportEvent(PCurrentChar, zone); } } @@ -481,7 +491,7 @@ void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) battleutils::RelinquishClaim(PChar); - // remove pets + // Remove pets if (PChar->PPet != nullptr) { auto* PPet = static_cast(PChar->PPet); @@ -500,6 +510,7 @@ void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) PChar->setModifier(Mod::AVATAR_PERPETUATION, 0); } } + // It may have been nullptred by DespawnPet if (PChar->PPet != nullptr) { @@ -516,12 +527,13 @@ void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) PCurrentChar->updateEntityPacket(PChar->PPet, ENTITY_DESPAWN, UPDATE_NONE); } } + PChar->PPet = nullptr; } } // Remove trusts - for (auto* PTrust : PChar->PTrusts) + for (const auto& PTrust : PChar->PTrusts) { FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { @@ -570,7 +582,7 @@ void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) } m_charList.erase(PChar->targid); - charTargIds.erase(PChar->targid); + m_charTargIds.erase(PChar->targid); ShowDebug("CZone:: %s DecreaseZoneCounter <%u> %s", m_zone->getName(), m_charList.size(), PChar->getName()); } @@ -579,13 +591,17 @@ uint16 CZoneEntities::GetNewCharTargID() { // NOTE: 0x0D (char_update) entity updates are valid for 1024 to 1791 uint16 targid = 0x400; - for (auto it : charTargIds) + for (auto it : m_charTargIds) { if (targid != it) { break; } - targid++; + ++targid; + } + if (targid >= 0x700) + { + ShowError("targid is high (03hX), update packets will be ignored!", targid); } return targid; } @@ -599,7 +615,7 @@ void CZoneEntities::AssignDynamicTargIDandLongID(CBaseEntity* PEntity) { // NOTE: 0x0E (entity_update) entity updates are valid for 0 to 1023 and 1792 to 2303 // Step targid up linearly from 0x700 one by one to 0x8FF unless that ID is already occupied. - uint16 targid = nextDynamicTargID; + uint16 targid = m_nextDynamicTargID; // Wrap around 0x8FF to 0x700 if (targid > 0x8FF) @@ -610,9 +626,9 @@ void CZoneEntities::AssignDynamicTargIDandLongID(CBaseEntity* PEntity) uint16 counter = 0; // Find next available targid, starting with the computed one above. - while (std::find(dynamicTargIds.begin(), dynamicTargIds.end(), targid) != dynamicTargIds.end()) + while (std::find(m_dynamicTargIds.begin(), m_dynamicTargIds.end(), targid) != m_dynamicTargIds.end()) { - targid++; + ++targid; // Wrap around 0x8FF to 0x700 if (targid > 0x8FF) { @@ -625,15 +641,15 @@ void CZoneEntities::AssignDynamicTargIDandLongID(CBaseEntity* PEntity) targid = 0x900; break; } - counter++; + ++counter; } // We found our targid, the next dynamic entity will want to start searching at +1 of this. - nextDynamicTargID = targid + 1; + m_nextDynamicTargID = targid + 1; auto id = 0x01000000 | (m_zone->GetID() << 0x0C) | (targid + 0x0100); - dynamicTargIds.insert(targid); + m_dynamicTargIds.insert(targid); PEntity->targid = targid; PEntity->id = id; @@ -934,16 +950,16 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) FOR_EACH_PAIR_CAST_SECOND(PChar->SpawnMOBList, CMobEntity*, PMob) { - CState* state = PMob->PAI->GetCurrentState(); - if (!state) + CState* PState = PMob->PAI->GetCurrentState(); + if (!PState) { continue; } - CBaseEntity* target = state->GetTarget(); - if (target && target->objtype == TYPE_PC && target->id != PChar->id) + CBaseEntity* PTarget = PState->GetTarget(); + if (PTarget && PTarget->objtype == TYPE_PC && PTarget->id != PChar->id) { - scoreBonus[target->id] += CHARACTER_SYNC_DISTANCE_SWAP_THRESHOLD; + scoreBonus[PTarget->id] += CHARACTER_SYNC_DISTANCE_SWAP_THRESHOLD; } } @@ -1076,7 +1092,7 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) PChar->SpawnPCList.erase(spawnedChar->id); PChar->updateCharPacket(spawnedChar, ENTITY_DESPAWN, UPDATE_NONE); spawnedCharacters.pop(); - swapCount++; + ++swapCount; } else { @@ -1180,21 +1196,21 @@ CBaseEntity* CZoneEntities::GetEntity(uint16 targid, uint8 filter) { if (filter & TYPE_MOB) { - if (auto* PEntity = findEntity(m_mobList)) + if (const auto& PEntity = findEntity(m_mobList)) { return PEntity; } } if (filter & TYPE_NPC) { - if (auto* PEntity = findEntity(m_npcList)) + if (const auto& PEntity = findEntity(m_npcList)) { return PEntity; } } if (filter & TYPE_SHIP) { - if (auto* PEntity = findEntity(m_TransportList)) + if (const auto& PEntity = findEntity(m_TransportList)) { return PEntity; } @@ -1204,7 +1220,7 @@ CBaseEntity* CZoneEntities::GetEntity(uint16 targid, uint8 filter) { if (filter & TYPE_PC) { - if (auto* PEntity = findEntity(m_charList)) + if (const auto& PEntity = findEntity(m_charList)) { return PEntity; } @@ -1214,28 +1230,28 @@ CBaseEntity* CZoneEntities::GetEntity(uint16 targid, uint8 filter) { if (filter & TYPE_PET) { - if (auto* PEntity = findEntity(m_petList)) + if (const auto& PEntity = findEntity(m_petList)) { return PEntity; } } if (filter & TYPE_TRUST) { - if (auto* PEntity = findEntity(m_trustList)) + if (const auto& PEntity = findEntity(m_trustList)) { return PEntity; } } if (filter & TYPE_NPC) { - if (auto* PEntity = findEntity(m_npcList)) + if (const auto& PEntity = findEntity(m_npcList)) { return PEntity; } } if (filter & TYPE_MOB) { - if (auto* PEntity = findEntity(m_mobList)) + if (const auto& PEntity = findEntity(m_mobList)) { return PEntity; } @@ -1599,13 +1615,6 @@ void CZoneEntities::ZoneServer(time_point tick) luautils::OnZoneTick(this->m_zone); - std::vector mobsToDelete; - std::vector npcsToDelete; - std::vector petsToDelete; - std::vector trustsToDelete; - - std::vector aggroableMobs; - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) { if (!PMob) @@ -1671,18 +1680,18 @@ void CZoneEntities::ZoneServer(time_point tick) } } - mobsToDelete.emplace_back(PMob); + m_mobsToDelete.emplace_back(PMob); continue; } if (PMob->allegiance == ALLEGIANCE_TYPE::PLAYER && PMob->m_isAggroable) { - aggroableMobs.emplace_back(PMob); + m_aggroableMobs.emplace_back(PMob); } } // Check to see if any aggroable mobs should be aggroed by other mobs - for (CMobEntity* PMob : aggroableMobs) + for (const auto& PMob : m_aggroableMobs) { FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) { @@ -1717,7 +1726,7 @@ void CZoneEntities::ZoneServer(time_point tick) } } - npcsToDelete.emplace_back(PNpc); + m_npcsToDelete.emplace_back(PNpc); continue; } } @@ -1739,7 +1748,7 @@ void CZoneEntities::ZoneServer(time_point tick) PCurrentMob->PEnmityContainer->Clear(PPet->id); } - petsToDelete.emplace_back(PPet); + m_petsToDelete.emplace_back(PPet); continue; } @@ -1784,16 +1793,11 @@ void CZoneEntities::ZoneServer(time_point tick) } } - trustsToDelete.emplace_back(PTrust); + m_trustsToDelete.emplace_back(PTrust); continue; } } - // Store some lists for chars that may need post-processing for effects that could delete them from m_charList and cause crashes - std::vector charsToLogout = {}; - std::vector charsToWarp = {}; - std::vector charsToChangeZone = {}; - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) { ShowTrace(fmt::format("CZoneEntities::ZoneServer: Char: {} ({})", PChar->getName(), PChar->id).c_str()); @@ -1820,74 +1824,74 @@ void CZoneEntities::ZoneServer(time_point tick) // This is done to prevent multiple-deletion of PChar if (PChar->status == STATUS_TYPE::SHUTDOWN) // EFFECT_LEAVEGAME effect wore off or char got SHUTDOWN from some other location { - charsToLogout.emplace_back(PChar); + m_charsToLogout.emplace_back(PChar); } else if (PChar->requestedWarp) // EFFECT_TELEPORT can request players to warp { - charsToWarp.emplace_back(PChar); + m_charsToWarp.emplace_back(PChar); } else if (PChar->requestedZoneChange) // EFFECT_TELEPORT can request players to change zones { - charsToChangeZone.emplace_back(PChar); + m_charsToChangeZone.emplace_back(PChar); } } - for (auto* PMob : mobsToDelete) + for (const auto* PMob : m_mobsToDelete) { if (auto itr = m_mobList.find(PMob->id); itr != m_mobList.end()) { m_mobList.erase(itr); - dynamicTargIdsToDelete.emplace_back(PMob->targid, server_clock::now()); + m_dynamicTargIdsToDelete.emplace_back(PMob->targid, server_clock::now()); destroy(PMob); } } - for (auto* PNpc : npcsToDelete) + for (const auto* PNpc : m_npcsToDelete) { if (auto itr = m_npcList.find(PNpc->id); itr != m_npcList.end()) { m_npcList.erase(itr); - dynamicTargIdsToDelete.emplace_back(PNpc->targid, server_clock::now()); + m_dynamicTargIdsToDelete.emplace_back(PNpc->targid, server_clock::now()); destroy(PNpc); } } - for (auto* PPet : petsToDelete) + for (const auto* PPet : m_petsToDelete) { if (auto itr = m_petList.find(PPet->id); itr != m_petList.end()) { m_petList.erase(itr); - dynamicTargIdsToDelete.emplace_back(PPet->targid, server_clock::now()); + m_dynamicTargIdsToDelete.emplace_back(PPet->targid, server_clock::now()); destroy(PPet); } } - for (auto* PTrust : trustsToDelete) + for (const auto* PTrust : m_trustsToDelete) { if (auto itr = m_trustList.find(PTrust->id); itr != m_trustList.end()) { m_trustList.erase(itr); - dynamicTargIdsToDelete.emplace_back(PTrust->targid, server_clock::now()); + m_dynamicTargIdsToDelete.emplace_back(PTrust->targid, server_clock::now()); destroy(PTrust); } } // forceLogout eventually removes the char from m_charList -- so we must remove them here - for (auto* PChar : charsToLogout) + for (auto* PChar : m_charsToLogout) { PChar->clearPacketList(); charutils::ForceLogout(PChar); } // Warp players (do not recover HP/MP) - for (auto* PChar : charsToWarp) + for (auto* PChar : m_charsToWarp) { PChar->clearPacketList(); charutils::HomePoint(PChar, false); } // Change player's zone (teleports, etc) - for (auto* PChar : charsToChangeZone) + for (auto* PChar : m_charsToChangeZone) { PChar->clearPacketList(); @@ -1908,70 +1912,80 @@ void CZoneEntities::ZoneServer(time_point tick) m_EffectCheckTime = m_EffectCheckTime + 3s > tick ? m_EffectCheckTime + 3s : tick + 3s; } - if (tick > charPersistTime && !charTargIds.empty()) + if (tick > m_charPersistTime && !m_charTargIds.empty()) { - charPersistTime = tick + 1s; + m_charPersistTime = tick + 1s; - auto charTargIdIter = charTargIds.lower_bound(lastCharPersistTargId); - if (charTargIdIter == charTargIds.end()) + std::set::iterator charTargIdIter = m_charTargIds.lower_bound(m_lastCharPersistTargId); + if (charTargIdIter == m_charTargIds.end()) { - charTargIdIter = charTargIds.begin(); + charTargIdIter = m_charTargIds.begin(); } - size_t maxChecks = std::min(charTargIds.size(), PERSIST_CHECK_CHARACTERS); + size_t maxChecks = std::min(m_charTargIds.size(), PERSIST_CHECK_CHARACTERS); for (size_t i = 0; i < maxChecks; i++) { - CCharEntity* pc = static_cast(m_charList[*charTargIdIter]); - charTargIdIter++; - if (charTargIdIter == charTargIds.end()) + CCharEntity* PChar = static_cast(m_charList[*charTargIdIter]); + ++charTargIdIter; + if (charTargIdIter == m_charTargIds.end()) { - charTargIdIter = charTargIds.begin(); + charTargIdIter = m_charTargIds.begin(); } - if (pc && pc->PersistData(tick)) + if (PChar && PChar->PersistData(tick)) { // We only want to persist at most 1 character per zone tick break; } } - lastCharPersistTargId = *charTargIdIter; + m_lastCharPersistTargId = *charTargIdIter; } - if (tick > computeTime && !charTargIds.empty()) + if (tick > m_computeTime && !m_charTargIds.empty()) { // Tick time is irregular to avoid consistently happening at the same time as char persistence - computeTime = tick + 567ms; + m_computeTime = tick + 567ms; - auto charTargIdIter = charTargIds.lower_bound(lastCharComputeTargId); - if (charTargIdIter == charTargIds.end()) + std::set::iterator charTargIdIter = m_charTargIds.lower_bound(m_lastCharComputeTargId); + if (charTargIdIter == m_charTargIds.end()) { - charTargIdIter = charTargIds.begin(); + charTargIdIter = m_charTargIds.begin(); } - std::size_t maxIterations = std::min(charTargIds.size(), std::min(10000U / charTargIds.size(), 20U)); + std::size_t maxIterations = std::min(m_charTargIds.size(), std::min(10000U / m_charTargIds.size(), 20U)); for (std::size_t i = 0; i < maxIterations; i++) { - CCharEntity* pc = static_cast(m_charList[*charTargIdIter]); - charTargIdIter++; + CCharEntity* PChar = static_cast(m_charList[*charTargIdIter]); + ++charTargIdIter; - if (charTargIdIter == charTargIds.end()) + if (charTargIdIter == m_charTargIds.end()) { - charTargIdIter = charTargIds.begin(); + charTargIdIter = m_charTargIds.begin(); } - if (pc && pc->requestedInfoSync) + if (PChar && PChar->requestedInfoSync) { - pc->requestedInfoSync = false; - SpawnPCs(pc); + PChar->requestedInfoSync = false; + SpawnPCs(PChar); } } - lastCharComputeTargId = *charTargIdIter; + m_lastCharComputeTargId = *charTargIdIter; } moduleutils::OnZoneTick(m_zone); + + // Clear intermediate containers + m_mobsToDelete.clear(); + m_npcsToDelete.clear(); + m_petsToDelete.clear(); + m_trustsToDelete.clear(); + m_aggroableMobs.clear(); + m_charsToLogout.clear(); + m_charsToWarp.clear(); + m_charsToChangeZone.clear(); } CZone* CZoneEntities::GetZone() diff --git a/src/map/zone_entities.h b/src/map/zone_entities.h index 7da2aedf09d..975db45891d 100644 --- a/src/map/zone_entities.h +++ b/src/map/zone_entities.h @@ -24,6 +24,13 @@ #include "zone.h" +#include "entities/baseentity.h" +#include "entities/charentity.h" +#include "entities/mobentity.h" +#include "entities/npcentity.h" +#include "entities/petentity.h" +#include "entities/trustentity.h" + #include #include @@ -91,11 +98,11 @@ class CZoneEntities EntityList_t m_TransportList; EntityList_t m_charList; - uint16 nextDynamicTargID; // The next dynamic targ ID to chosen -- SE rotates them forwards and skips entries that already exist. - std::set charTargIds; // sorted set of targids for characters - std::set dynamicTargIds; // sorted set of targids for dynamic entities + uint16 m_nextDynamicTargID; // The next dynamic targ ID to chosen -- SE rotates them forwards and skips entries that already exist. + std::set m_charTargIds; // sorted set of targids for characters + std::set m_dynamicTargIds; // sorted set of targids for dynamic entities - std::vector> dynamicTargIdsToDelete; // list of targids pending deletion at a later date + std::vector> m_dynamicTargIdsToDelete; // list of targids pending deletion at a later date CZoneEntities(CZone*); ~CZoneEntities(); @@ -104,11 +111,24 @@ class CZoneEntities CZone* m_zone; time_point m_EffectCheckTime{ server_clock::now() }; - time_point computeTime{ server_clock::now() }; - uint16 lastCharComputeTargId; + time_point m_computeTime{ server_clock::now() }; + uint16 m_lastCharComputeTargId; + + time_point m_charPersistTime{ server_clock::now() }; + uint16 m_lastCharPersistTargId; + + // + // Intermediate collections for use inside ZoneServer + // - time_point charPersistTime{ server_clock::now() }; - uint16 lastCharPersistTargId; + std::vector m_mobsToDelete; + std::vector m_npcsToDelete; + std::vector m_petsToDelete; + std::vector m_trustsToDelete; + std::vector m_aggroableMobs; + std::vector m_charsToLogout; + std::vector m_charsToWarp; + std::vector m_charsToChangeZone; }; #endif From 7ae97242b170caa2449a736ce87130b561c84491 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 20 Jan 2025 01:59:11 +0000 Subject: [PATCH 3/5] Core: Make better use of isWithinDistance() While not as bad as the 90's, `sqrt` (and the instruction `sqrtss`) still have a higher latency than other instructions. These two routines typically have the same (or a similar) number of instructions, but `distance()` has `sqrt` in it. mulss, latency: 7/6: https://c9x.me/x86/html/file_module_x86_id_214.html squrtss: latency 32/23/30 https://c9x.me/x86/html/file_module_x86_id_301.html For the extremely hot path, we should be using any trick we can to claw back performance. `distance() < threshold`: ```asm ; Key operations: subss xmm0, dword ptr [rsp - 16] ; Subtract x coordinates subss xmm1, dword ptr [rsp - 20] ; Subtract y coordinates subss xmm2, dword ptr [rsp - 24] ; Subtract z coordinates mulss xmm1, xmm1 ; Square y difference mulss xmm0, xmm0 ; Square x difference addss xmm0, xmm1 ; Add squared differences mulss xmm2, xmm2 ; Square z difference addss xmm2, xmm0 ; Add all squared differences sqrtss xmm0, xmm2 ; Square root operation ucomiss xmm1, xmm0 ; Compare with threshold ``` `isWithinDistance()`: ```asm ; Key operations: subss xmm1, dword ptr [rsp - 16] ; Subtract x coordinates subss xmm2, dword ptr [rsp - 20] ; Subtract y coordinates subss xmm3, dword ptr [rsp - 24] ; Subtract z coordinates mulss xmm2, xmm2 ; Square y difference mulss xmm1, xmm1 ; Square x difference addss xmm1, xmm2 ; Add squared differences mulss xmm3, xmm3 ; Square z difference addss xmm3, xmm1 ; Add all squared differences mulss xmm0, xmm0 ; Square threshold ucomiss xmm0, xmm3 ; Compare squared values ``` --- src/common/utils.cpp | 20 +------------------- src/common/utils.h | 23 +++++++++++++++++++---- src/map/ai/controllers/mob_controller.cpp | 2 +- src/map/ai/helpers/pathfind.cpp | 18 ++++++++++-------- src/map/ai/states/magic_state.cpp | 2 +- src/map/enmity_container.cpp | 4 ++-- src/map/lua/lua_baseentity.cpp | 13 ++++++------- src/map/utils/charutils.cpp | 4 ++-- src/map/zone_entities.cpp | 22 ++++++++++++---------- 9 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 33f4bcb5788..007b4e60f13 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -91,24 +91,6 @@ bool bin2hex(char* output, unsigned char* input, size_t count) return true; } -float distance(const position_t& A, const position_t& B, bool ignoreVertical) -{ - return sqrt(distanceSquared(A, B, ignoreVertical)); -} - -float distanceSquared(const position_t& A, const position_t& B, bool ignoreVertical) -{ - float diff_x = A.x - B.x; - float diff_y = ignoreVertical ? 0 : A.y - B.y; - float diff_z = A.z - B.z; - return diff_x * diff_x + diff_y * diff_y + diff_z * diff_z; -} - -bool distanceWithin(const position_t& A, const position_t& B, float within, bool ignoreVertical) -{ - return distanceSquared(A, B, ignoreVertical) <= square(within); -} - int32 intpow32(int32 base, int32 exponent) { int32 power = 1; @@ -165,7 +147,7 @@ uint8 worldAngle(const position_t& A, const position_t& B) { uint8 angle = (uint8)(atanf((B.z - A.z) / (B.x - A.x)) * -(128.0f / M_PI)); - return distanceWithin(A, B, 0.1f, true) ? A.rotation : (A.x > B.x ? angle + 128 : angle); + return isWithinDistance(A, B, 0.1f, true) ? A.rotation : (A.x > B.x ? angle + 128 : angle); } uint8 relativeAngle(uint8 world, int16 diff) diff --git a/src/common/utils.h b/src/common/utils.h index afadbb37806..b98a5b525f1 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -43,14 +43,29 @@ int32 checksum(uint8* buf, uint32 buflen, char checkhash[16]); int config_switch(const char* str); bool bin2hex(char* output, unsigned char* input, size_t count); -float distance(const position_t& A, const position_t& B, bool ignoreVertical = false); // distance between positions. Use only horizontal plane (x and z) if ignoreVertical is set. -float distanceSquared(const position_t& A, const position_t& B, bool ignoreVertical = false); // squared distance between positions (use squared unless otherwise needed) -bool distanceWithin(const position_t& A, const position_t& B, float within, bool ignoreVertical = false); // returns true if the distance between the points is <= within. -constexpr float square(float distance) // constexpr square (used with distanceSquared) +constexpr float square(auto distance) // constexpr square (used with distanceSquared) { return distance * distance; } +inline float distanceSquared(const position_t& A, const position_t& B, bool ignoreVertical = false) +{ + float dX = A.x - B.x; + float dY = ignoreVertical ? 0 : A.y - B.y; + float dZ = A.z - B.z; + return dX * dX + dY * dY + dZ * dZ; +} + +inline float distance(const position_t& A, const position_t& B, bool ignoreVertical = false) +{ + return std::sqrt(distanceSquared(A, B, ignoreVertical)); +} + +inline bool isWithinDistance(const position_t& A, const position_t& B, float within, bool ignoreVertical = false) +{ + return distanceSquared(A, B, ignoreVertical) <= square(within); +} + int32 intpow32(int32 base, int32 exponent); // Exponential power of integers void getMSB(uint32* result, uint32 value); // fast Most Significant Byte search under GCC or MSVC. Fallback included. float rotationToRadian(uint8 rotation); diff --git a/src/map/ai/controllers/mob_controller.cpp b/src/map/ai/controllers/mob_controller.cpp index d14efe76c63..929b726c09f 100644 --- a/src/map/ai/controllers/mob_controller.cpp +++ b/src/map/ai/controllers/mob_controller.cpp @@ -745,7 +745,7 @@ void CMobController::Move() PMob->PAI->PathFind->PathInRange(PTarget->loc.p, closeDistance, PATHFLAG_WALLHACK | PATHFLAG_RUN); } } - else if (distanceSquared(PMob->PAI->PathFind->GetDestination(), PTarget->loc.p) > 10) + else if (!isWithinDistance(PMob->PAI->PathFind->GetDestination(), PTarget->loc.p, 2.5f)) { // try to find path towards target PMob->PAI->PathFind->PathInRange(PTarget->loc.p, closeDistance, PATHFLAG_WALLHACK | PATHFLAG_RUN); diff --git a/src/map/ai/helpers/pathfind.cpp b/src/map/ai/helpers/pathfind.cpp index 1840630b92d..28ada647c80 100644 --- a/src/map/ai/helpers/pathfind.cpp +++ b/src/map/ai/helpers/pathfind.cpp @@ -221,11 +221,11 @@ void CPathFind::ResumePatrol() float closestPoint = FLT_MAX; for (size_t i = 0; i < m_points.size(); ++i) { - float distance = distanceSquared(m_POwner->loc.p, m_points[i].position); - if (distance < closestPoint) + const float distanceSq = distanceSquared(m_POwner->loc.p, m_points[i].position); + if (distanceSq < closestPoint) { m_currentPoint = (int16)i; - closestPoint = distance; + closestPoint = distanceSq; } } } @@ -495,9 +495,8 @@ bool CPathFind::FindRandomPath(const position_t& start, float maxRadius, uint8 m return false; } - float distSq = distanceSquared(startPosition, status.second, true); // only add the roam point if it's _actually_ within range of the spawn point... - if (distSq < maxRadius * maxRadius) + if (isWithinDistance(startPosition, status.second, maxRadius, true)) { m_turnPoints.emplace_back(status.second); } @@ -505,8 +504,11 @@ bool CPathFind::FindRandomPath(const position_t& start, float maxRadius, uint8 m // { // ShowDebug("CPathFind::FindRandomPath (%s - %d) random point too far: sq distance (%f)", m_POwner->GetName(), m_POwner->id, distSq); // } + if (m_turnPoints.size() >= m_turnLength) + { break; + } } if (m_turnPoints.size() > 0) { @@ -549,7 +551,7 @@ bool CPathFind::FindClosestPath(const position_t& start, const position_t& end) void CPathFind::LookAt(const position_t& point) { // Avoid unpredictable results if we're too close. - if (!distanceWithin(m_POwner->loc.p, point, 0.1f, true)) + if (!isWithinDistance(m_POwner->loc.p, point, 0.1f, true)) { m_POwner->loc.p.rotation = worldAngle(m_POwner->loc.p, point); m_POwner->updatemask |= UPDATE_POS; @@ -580,11 +582,11 @@ bool CPathFind::AtPoint(const position_t& pos) { if (m_distanceFromPoint == 0) { - return distanceWithin(m_POwner->loc.p, pos, 0.1f); + return isWithinDistance(m_POwner->loc.p, pos, 0.1f); } else { - return distanceWithin(m_POwner->loc.p, pos, m_distanceFromPoint + 0.2f); + return isWithinDistance(m_POwner->loc.p, pos, m_distanceFromPoint + 0.2f); } } diff --git a/src/map/ai/states/magic_state.cpp b/src/map/ai/states/magic_state.cpp index 619da425578..9f5c3ae389d 100644 --- a/src/map/ai/states/magic_state.cpp +++ b/src/map/ai/states/magic_state.cpp @@ -356,7 +356,7 @@ bool CMagicState::CanCastSpell(CBattleEntity* PTarget, bool isEndOfCast) if (dynamic_cast(m_PEntity)) { - if (distanceSquared(m_PEntity->loc.p, PTarget->loc.p) > square(28.5f)) + if (!isWithinDistance(m_PEntity->loc.p, PTarget->loc.p, 28.5f)) { return false; } diff --git a/src/map/enmity_container.cpp b/src/map/enmity_container.cpp index 33388041654..06a4645a317 100644 --- a/src/map/enmity_container.cpp +++ b/src/map/enmity_container.cpp @@ -500,8 +500,8 @@ bool CEnmityContainer::IsWithinEnmityRange(CBattleEntity* PEntity) const { return false; } - float maxRange = square(m_EnmityHolder->m_Type == MOBTYPE_NOTORIOUS ? 28.f : 25.f); - return distanceSquared(m_EnmityHolder->loc.p, PEntity->loc.p) <= maxRange; + float maxRange = m_EnmityHolder->m_Type == MOBTYPE_NOTORIOUS ? 28.0f : 25.0f; + return isWithinDistance(m_EnmityHolder->loc.p, PEntity->loc.p, maxRange); } EnmityList_t* CEnmityContainer::GetEnmityList() diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 8b4deaea1c7..5cd9876746f 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -1728,7 +1728,7 @@ void CLuaBaseEntity::lookAt(sol::object const& arg0, sol::object const& arg1, so } // Avoid unpredictable results if we're too close. - if (!distanceWithin(m_PBaseEntity->loc.p, point, 0.1f, true)) + if (!isWithinDistance(m_PBaseEntity->loc.p, point, 0.1f, true)) { m_PBaseEntity->loc.p.rotation = worldAngle(m_PBaseEntity->loc.p, point); m_PBaseEntity->updatemask |= UPDATE_POS; @@ -1813,7 +1813,7 @@ bool CLuaBaseEntity::atPoint(sol::variadic_args va) pos.z = vec[2]; } - return distanceWithin(m_PBaseEntity->loc.p, pos, 0.01f); + return isWithinDistance(m_PBaseEntity->loc.p, pos, 0.01f); } /************************************************************************ @@ -10916,7 +10916,7 @@ void CLuaBaseEntity::forMembersInRange(float range, sol::function function) // clang-format off target->ForParty([&target, &range, &function](CBattleEntity* member) { - if (target->loc.zone == member->loc.zone && distanceSquared(target->loc.p, member->loc.p) < (range * range)) + if (target->loc.zone == member->loc.zone && isWithinDistance(target->loc.p, member->loc.p, range)) { function(CLuaBaseEntity(member)); } @@ -12805,12 +12805,11 @@ void CLuaBaseEntity::transferEnmity(CLuaBaseEntity* entity, uint8 percent, float if (PIterEntity) { - for (auto&& mob_pair : PIterEntity->SpawnMOBList) + FOR_EACH_PAIR_CAST_SECOND(PIterEntity->SpawnMOBList, CMobEntity*, PMob) { - if (distanceSquared(mob_pair.second->loc.p, PEntity->loc.p) < (range * range)) + if (isWithinDistance(PMob->loc.p, PEntity->loc.p, range)) { - battleutils::TransferEnmity(static_cast(PEntity), static_cast(m_PBaseEntity), - static_cast(mob_pair.second), percent); + battleutils::TransferEnmity(static_cast(PEntity), static_cast(m_PBaseEntity), PMob, percent); } } } diff --git a/src/map/utils/charutils.cpp b/src/map/utils/charutils.cpp index 550418c4b38..11134e3ffbf 100644 --- a/src/map/utils/charutils.cpp +++ b/src/map/utils/charutils.cpp @@ -4088,7 +4088,7 @@ namespace charutils // clang-format off PChar->ForAlliance([PMob, &members](CBattleEntity* PPartyMember) { - if (PPartyMember->getZone() == PMob->getZone() && distanceSquared(PPartyMember->loc.p, PMob->loc.p) < square(100.f)) + if (PPartyMember->getZone() == PMob->getZone() && isWithinDistance(PPartyMember->loc.p, PMob->loc.p, 100.f)) { members.emplace_back((CCharEntity*)PPartyMember); } @@ -4120,7 +4120,7 @@ namespace charutils } } } - else if (distanceSquared(PChar->loc.p, PMob->loc.p) < square(100.f)) + else if (isWithinDistance(PChar->loc.p, PMob->loc.p, 100.f)) { // Check for gilfinder gil += gil * PChar->getMod(Mod::GILFINDER) / 100; diff --git a/src/map/zone_entities.cpp b/src/map/zone_entities.cpp index 752f3f77ac7..75c17e8f3dd 100644 --- a/src/map/zone_entities.cpp +++ b/src/map/zone_entities.cpp @@ -73,7 +73,9 @@ namespace inline bool isWithinVerticalDistance(CBaseEntity* source, CBaseEntity* target) { - return abs(target->loc.p.y - source->loc.p.y - 0.5f) <= ENTITY_VERTICAL_RENDER_DISTANCE; + constexpr float offset = 0.5f; + const float verticalDistance = target->loc.p.y - source->loc.p.y - offset; + return std::abs(verticalDistance) <= ENTITY_VERTICAL_RENDER_DISTANCE; } } // namespace @@ -152,7 +154,7 @@ void CZoneEntities::TryAddToNearbySpawnLists(CBaseEntity* PEntity) FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { const auto isInHeightRange = isWithinVerticalDistance(PEntity, PCurrentChar); - const auto isInRange = distance(PEntity->loc.p, PCurrentChar->loc.p) <= ENTITY_RENDER_DISTANCE; + const auto isInRange = isWithinDistance(PEntity->loc.p, PCurrentChar->loc.p, ENTITY_RENDER_DISTANCE); if (isInHeightRange && isInRange) { @@ -696,7 +698,7 @@ void CZoneEntities::SpawnMOBs(CCharEntity* PChar) const auto itr = spawnList.find(id); const auto isInSpawnList = itr != spawnList.end(); const auto isInHeightRange = isWithinVerticalDistance(PChar, PCurrentMob); - const auto isInRange = distance(PChar->loc.p, PCurrentMob->loc.p) <= ENTITY_RENDER_DISTANCE; + const auto isInRange = isWithinDistance(PChar->loc.p, PCurrentMob->loc.p, ENTITY_RENDER_DISTANCE); const auto isVisibleStatus = PCurrentMob->status != STATUS_TYPE::DISAPPEAR; const auto tryAddToSpawnList = [&]() @@ -778,7 +780,7 @@ void CZoneEntities::SpawnPETs(CCharEntity* PChar) const auto itr = spawnList.find(id); const auto isInSpawnList = itr != spawnList.end(); const auto isInHeightRange = isWithinVerticalDistance(PChar, PCurrentEntity); - const auto isInRange = distance(PChar->loc.p, PCurrentEntity->loc.p) <= ENTITY_RENDER_DISTANCE; + const auto isInRange = isWithinDistance(PChar->loc.p, PCurrentEntity->loc.p, ENTITY_RENDER_DISTANCE); const auto isVisibleStatus = PCurrentEntity->status == STATUS_TYPE::NORMAL || PCurrentEntity->status == STATUS_TYPE::UPDATE; const auto tryAddToSpawnList = [&]() @@ -831,7 +833,7 @@ void CZoneEntities::SpawnNPCs(CCharEntity* PChar) const auto itr = spawnList.find(id); const auto isInSpawnList = itr != spawnList.end(); const auto isInHeightRange = isWithinVerticalDistance(PChar, PCurrentEntity); - const auto isInRange = distance(PChar->loc.p, PCurrentEntity->loc.p) <= ENTITY_RENDER_DISTANCE; + const auto isInRange = isWithinDistance(PChar->loc.p, PCurrentEntity->loc.p, ENTITY_RENDER_DISTANCE); const auto isVisibleStatus = PCurrentEntity->status == STATUS_TYPE::NORMAL || PCurrentEntity->status == STATUS_TYPE::UPDATE; const auto tryAddToSpawnList = [&]() @@ -879,7 +881,7 @@ void CZoneEntities::SpawnTRUSTs(CCharEntity* PChar) const auto itr = spawnList.find(id); const auto isInSpawnList = itr != spawnList.end(); const auto isInHeightRange = isWithinVerticalDistance(PChar, PCurrentEntity); - const auto isInRange = distance(PChar->loc.p, PCurrentEntity->loc.p) <= ENTITY_RENDER_DISTANCE; + const auto isInRange = isWithinDistance(PChar->loc.p, PCurrentEntity->loc.p, ENTITY_RENDER_DISTANCE); const auto isVisibleStatus = PCurrentEntity->status == STATUS_TYPE::NORMAL || PCurrentEntity->status == STATUS_TYPE::UPDATE; const auto tryAddToSpawnList = [&]() @@ -1469,13 +1471,13 @@ void CZoneEntities::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message TracyZoneCString("CHAR_INRANGE"); // TODO: rewrite packet handlers and use enums instead of rawdog packet ids // 30 yalms if action packet, 50 otherwise - const int checkDistanceSq = packet->getType() == 0x0028 ? 900 : 2500; + const int checkDistance = packet->getType() == 0x0028 ? 30 : 50; FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) { if (PEntity != PCurrentChar) { - if (distanceSquared(PEntity->loc.p, PCurrentChar->loc.p) < checkDistanceSq && + if (isWithinDistance(PEntity->loc.p, PCurrentChar->loc.p, checkDistance) && (PEntity->objtype != TYPE_PC || static_cast(PEntity)->m_moghouseID == PCurrentChar->m_moghouseID)) { uint16 packetType = packet->getType(); @@ -1599,7 +1601,7 @@ void CZoneEntities::WideScan(CCharEntity* PChar, uint16 radius) { for (const auto& [_, PEntity] : entityList) { - if (PEntity->isWideScannable() && distance(PChar->loc.p, PEntity->loc.p) < radius) + if (PEntity->isWideScannable() && isWithinDistance(PChar->loc.p, PEntity->loc.p, radius)) { PChar->pushPacket(PChar, PEntity); } @@ -1696,7 +1698,7 @@ void CZoneEntities::ZoneServer(time_point tick) FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) { const auto isInHeightRange = isWithinVerticalDistance(PMob, PCurrentMob); - const auto isInRange = distance(PMob->loc.p, PCurrentMob->loc.p) <= ENTITY_RENDER_DISTANCE; + const auto isInRange = isWithinDistance(PMob->loc.p, PCurrentMob->loc.p, ENTITY_RENDER_DISTANCE); if (PCurrentMob != nullptr && PCurrentMob->isAlive() && PMob->allegiance != PCurrentMob->allegiance && isInHeightRange && isInRange) { From dfff23d9cb418aaa44716149e382caba481aa11e Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 20 Jan 2025 23:37:49 +0000 Subject: [PATCH 4/5] Core: Cleanup and encapsulation of CZoneEntities --- src/map/instance.cpp | 32 ++++++-- src/map/instance_loader.cpp | 64 ++++++++------- src/map/instance_loader.h | 6 +- src/map/lua/lua_instance.cpp | 45 ++++++----- src/map/map.cpp | 17 +--- src/map/utils/mobutils.cpp | 12 +-- src/map/zone.cpp | 133 ++++++++++++++++-------------- src/map/zone.h | 12 ++- src/map/zone_entities.cpp | 105 ++++++++++++++++++++---- src/map/zone_entities.h | 25 +++--- src/map/zone_instance.cpp | 151 +++++++++++++++++++++++++---------- src/map/zone_instance.h | 17 ++-- 12 files changed, 402 insertions(+), 217 deletions(-) diff --git a/src/map/instance.cpp b/src/map/instance.cpp index 819bcdb1381..2794bdf3f66 100644 --- a/src/map/instance.cpp +++ b/src/map/instance.cpp @@ -245,17 +245,35 @@ bool CInstance::CharRegistered(CCharEntity* PChar) void CInstance::ClearEntities() { - auto clearStates = [](auto& entity) + auto clearStates = [](CBattleEntity* entity) { - if (static_cast(entity.second)->isAlive()) + if (static_cast(entity)->isAlive()) { - entity.second->PAI->ClearStateStack(); + entity->PAI->ClearStateStack(); } }; - std::for_each(m_charList.cbegin(), m_charList.cend(), clearStates); - std::for_each(m_mobList.cbegin(), m_mobList.cend(), clearStates); - std::for_each(m_petList.cbegin(), m_petList.cend(), clearStates); - std::for_each(m_trustList.cbegin(), m_trustList.cend(), clearStates); + + // clang-format off + ForEachChar([&](CCharEntity* PChar) + { + clearStates(PChar); + }); + + ForEachMob([&](CMobEntity* PMob) + { + clearStates(PMob); + }); + + ForEachPet([&](CPetEntity* PPet) + { + clearStates(PPet); + }); + + ForEachTrust([&](CTrustEntity* PTrust) + { + clearStates(PTrust); + }); + // clang-format on } void CInstance::Fail() diff --git a/src/map/instance_loader.cpp b/src/map/instance_loader.cpp index 867409b7159..5df69643062 100644 --- a/src/map/instance_loader.cpp +++ b/src/map/instance_loader.cpp @@ -45,6 +45,7 @@ CInstanceLoader::CInstanceLoader(uint16 instanceid, CCharEntity* PRequester) { TracyZoneScoped; + auto instanceData = instanceutils::GetInstanceData(instanceid); CZone* PZone = zoneutils::GetZone(instanceData.instance_zone); @@ -54,9 +55,9 @@ CInstanceLoader::CInstanceLoader(uint16 instanceid, CCharEntity* PRequester) return; } - requester = PRequester; - zone = PZone; - instance = ((CZoneInstance*)PZone)->CreateInstance(instanceid); + m_PRequester = PRequester; + m_PZone = PZone; + m_PInstance = ((CZoneInstance*)PZone)->CreateInstance(instanceid); } CInstanceLoader::~CInstanceLoader() @@ -86,13 +87,13 @@ CInstance* CInstanceLoader::LoadInstance() INNER JOIN mob_family_system ON mob_pools.familyid = mob_family_system.familyID \ WHERE instanceid = %u AND NOT (pos_x = 0 AND pos_y = 0 AND pos_z = 0)"; - int32 ret = _sql->Query(Query, instance->GetID()); + int32 ret = _sql->Query(Query, m_PInstance->GetID()); - if (!instance->Failed() && ret != SQL_ERROR /*&& sql->NumRows() != 0*/) + if (!m_PInstance->Failed() && ret != SQL_ERROR /*&& sql->NumRows() != 0*/) { while (_sql->NextRow() == SQL_SUCCESS) { - CMobEntity* PMob = new CMobEntity; + CMobEntity* PMob = new CMobEntity(); PMob->name.insert(0, (const char*)_sql->GetData(0)); PMob->id = _sql->GetUIntData(1); @@ -224,9 +225,9 @@ CInstance* CInstanceLoader::LoadInstance() // must be here first to define mobmods mobutils::InitializeMob(PMob); - PMob->PInstance = instance; + PMob->PInstance = m_PInstance; - instance->InsertMOB(PMob); + m_PInstance->InsertMOB(PMob); } Query = "SELECT npcid, name, pos_rot, pos_x, pos_y, pos_z,\ @@ -236,10 +237,10 @@ CInstance* CInstanceLoader::LoadInstance() (instance_entities.id = npc_list.npcid) \ WHERE instanceid = %u AND npcid >= %u AND npcid < %u"; - uint32 zoneMin = (zone->GetID() << 12) + 0x1000000; + uint32 zoneMin = (m_PZone->GetID() << 12) + 0x1000000; uint32 zoneMax = zoneMin + 1024; - ret = _sql->Query(Query, instance->GetID(), zoneMin, zoneMax); + ret = _sql->Query(Query, m_PInstance->GetID(), zoneMin, zoneMax); if (ret != SQL_ERROR && _sql->NumRows() != 0) { @@ -276,45 +277,50 @@ CInstance* CInstanceLoader::LoadInstance() PNpc->name_prefix = (uint8)_sql->GetIntData(15); PNpc->widescan = (uint8)_sql->GetIntData(16); - PNpc->PInstance = instance; + PNpc->PInstance = m_PInstance; - instance->InsertNPC(PNpc); + m_PInstance->InsertNPC(PNpc); } } + // clang-format off // Finish setting up Mobs - for (auto PMob : instance->m_mobList) + m_PInstance->ForEachMob([&](CMobEntity* PMob) { - luautils::OnMobInitialize(PMob.second); - luautils::ApplyMixins(PMob.second); - ((CMobEntity*)PMob.second)->saveModifiers(); - ((CMobEntity*)PMob.second)->saveMobModifiers(); + luautils::OnMobInitialize(PMob); + luautils::ApplyMixins(PMob); + ((CMobEntity*)PMob)->saveModifiers(); + ((CMobEntity*)PMob)->saveMobModifiers(); // Add to cache luautils::CacheLuaObjectFromFile( fmt::format("./scripts/zones/{}/mobs/{}.lua", - PMob.second->loc.zone->getName(), - PMob.second->getName())); - } + PMob->loc.zone->getName(), + PMob->getName())); + }); + // clang-format on + // clang-format off // Finish setting up NPCs - for (auto PNpc : instance->m_npcList) + m_PInstance->ForEachNpc([&](CNpcEntity* PNpc) { - luautils::OnNpcSpawn(PNpc.second); + luautils::OnNpcSpawn(PNpc); // Add to cache luautils::CacheLuaObjectFromFile( fmt::format("./scripts/zones/{}/npcs/{}.lua", - PNpc.second->loc.zone->getName(), - PNpc.second->getName())); - } + PNpc->loc.zone->getName(), + PNpc->getName())); + }); + // clang-format on // Cache Instance script (TODO: This will be done multiple times, don't do that) - luautils::CacheLuaObjectFromFile(instanceutils::GetInstanceData(instance->GetID()).filename); + luautils::CacheLuaObjectFromFile(instanceutils::GetInstanceData(m_PInstance->GetID()).filename); // Finish setup - luautils::OnInstanceCreatedCallback(requester, instance); - luautils::OnInstanceCreated(instance); + luautils::OnInstanceCreatedCallback(m_PRequester, m_PInstance); + luautils::OnInstanceCreated(m_PInstance); } - return instance; + + return m_PInstance; } diff --git a/src/map/instance_loader.h b/src/map/instance_loader.h index 63cbff9b467..f1e9eda59f5 100644 --- a/src/map/instance_loader.h +++ b/src/map/instance_loader.h @@ -38,9 +38,9 @@ class CInstanceLoader CInstance* LoadInstance(); private: - CInstance* instance; - CZone* zone; - CCharEntity* requester; + CInstance* m_PInstance; + CZone* m_PZone; + CCharEntity* m_PRequester; }; #endif diff --git a/src/map/lua/lua_instance.cpp b/src/map/lua/lua_instance.cpp index 660a8b02f22..a8e50d34c33 100644 --- a/src/map/lua/lua_instance.cpp +++ b/src/map/lua/lua_instance.cpp @@ -60,57 +60,62 @@ uint32 CLuaInstance::getEntranceZoneID() sol::table CLuaInstance::getAllies() { + // clang-format off auto table = lua.create_table(); - for (auto& member : m_PLuaInstance->m_allyList) + m_PLuaInstance->ForEachAlly([&](CMobEntity* PAlly) { - table.add(CLuaBaseEntity(member.second)); - } - + table.add(CLuaBaseEntity(PAlly)); + }); return table; + // clang-format on } sol::table CLuaInstance::getChars() { + // clang-format off auto table = lua.create_table(); - for (auto& member : m_PLuaInstance->m_charList) + m_PLuaInstance->ForEachChar([&](CCharEntity* PChar) { - table.add(CLuaBaseEntity(member.second)); - } - + table.add(CLuaBaseEntity(PChar)); + }); return table; + // clang-format on } sol::table CLuaInstance::getMobs() { + // clang-format off auto table = lua.create_table(); - for (auto& member : m_PLuaInstance->m_mobList) + m_PLuaInstance->ForEachMob([&](CMobEntity* PMob) { - table.add(CLuaBaseEntity(member.second)); - } - + table.add(CLuaBaseEntity(PMob)); + }); return table; + // clang-format on } sol::table CLuaInstance::getNpcs() { + // clang-format off auto table = lua.create_table(); - for (auto& member : m_PLuaInstance->m_npcList) + m_PLuaInstance->ForEachNpc([&](CNpcEntity* PNpc) { - table.add(CLuaBaseEntity(member.second)); - } - + table.add(CLuaBaseEntity(PNpc)); + }); return table; + // clang-format on } sol::table CLuaInstance::getPets() { + // clang-format off auto table = lua.create_table(); - for (auto& member : m_PLuaInstance->m_petList) + m_PLuaInstance->ForEachPet([&](CPetEntity* PPet) { - table.add(CLuaBaseEntity(member.second)); - } - + table.add(CLuaBaseEntity(PPet)); + }); return table; + // clang-format on } uint32 CLuaInstance::getTimeLimit() diff --git a/src/map/map.cpp b/src/map/map.cpp index e24a5c56be0..b6bf04d6e74 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -1376,23 +1376,10 @@ int32 map_cleanup(time_point tick, CTaskMgr::CTask* PTask) // clang-format off zoneutils::ForEachZone([](CZone* PZone) { - auto& staledynamicTargIds = PZone->GetZoneEntities()->m_dynamicTargIdsToDelete; - - for (auto it = staledynamicTargIds.begin(); it != staledynamicTargIds.end();) - { - // Erase dynamic targid if it's stale enough - if ((server_clock::now() - it->second) > 60s) - { - PZone->GetZoneEntities()->m_dynamicTargIds.erase(it->first); - it = staledynamicTargIds.erase(it); - } - else - { - ++it; - } - } + PZone->GetZoneEntities()->EraseStaleDynamicTargIDs(); }); // clang-format on + return 0; } diff --git a/src/map/utils/mobutils.cpp b/src/map/utils/mobutils.cpp index 7fb8c7cbda9..d9da85d49b2 100644 --- a/src/map/utils/mobutils.cpp +++ b/src/map/utils/mobutils.cpp @@ -1539,7 +1539,7 @@ namespace mobutils { if (_sql->NextRow() == SQL_SUCCESS) { - PMob = new CMobEntity; + PMob = new CMobEntity(); PMob->PInstance = instance; PMob->name.insert(0, (const char*)_sql->GetData(1)); @@ -1645,14 +1645,10 @@ namespace mobutils PMob->m_TrueDetection = _sql->GetUIntData(69); PMob->setMobMod(MOBMOD_DETECTION, _sql->GetUIntData(70)); - CZone* newZone = zoneutils::GetZone(zoneID); - if (newZone) + if (CZone* PZone = zoneutils::GetZone(zoneID)) { - // Get dynamic targid - newZone->GetZoneEntities()->AssignDynamicTargIDandLongID(PMob); - - // Insert ally into zone's mob list. TODO: Do we need to assign party for allies? - newZone->GetZoneEntities()->m_mobList[PMob->targid] = PMob; + PZone->GetZoneEntities()->AssignDynamicTargIDandLongID(PMob); + PZone->GetZoneEntities()->InsertMOB(PMob); } else { diff --git a/src/map/zone.cpp b/src/map/zone.cpp index 33eebf07c5f..1dd3bcfb129 100644 --- a/src/map/zone.cpp +++ b/src/map/zone.cpp @@ -76,32 +76,6 @@ #include "utils/petutils.h" #include "utils/zoneutils.h" -int32 zone_server(time_point tick, CTaskMgr::CTask* PTask) -{ - CZone* PZone = std::any_cast(PTask->m_data); - PZone->ZoneServer(tick); - return 0; -} - -int32 zone_trigger_area(time_point tick, CTaskMgr::CTask* PTask) -{ - CZone* PZone = std::any_cast(PTask->m_data); - PZone->CheckTriggerAreas(); - return 0; -} - -int32 zone_update_weather(time_point tick, CTaskMgr::CTask* PTask) -{ - CZone* PZone = std::any_cast(PTask->m_data); - - if (!PZone->IsWeatherStatic()) - { - PZone->UpdateWeather(); - } - - return 0; -} - CZone::CZone(ZONEID ZoneID, REGION_TYPE RegionID, CONTINENT_TYPE ContinentID, uint8 levelRestriction) : m_zoneID(ZoneID) , m_zoneType(ZONE_TYPE::UNKNOWN) @@ -528,11 +502,6 @@ void CZone::InsertNPC(CBaseEntity* PNpc) m_zoneEntities->InsertNPC(PNpc); } -void CZone::DeletePET(CBaseEntity* PPet) -{ - m_zoneEntities->DeletePET(PPet); -} - /************************************************************************ * * * Add a PET to the zone (free targid 0x700-0x7FF) * @@ -555,11 +524,6 @@ void CZone::InsertTRUST(CBaseEntity* PTrust) m_zoneEntities->InsertTRUST(PTrust); } -void CZone::DeleteTRUST(CBaseEntity* PTrust) -{ - m_zoneEntities->DeleteTRUST(PTrust); -} - /************************************************************************ * * * Add a trigger area to the zone * @@ -724,7 +688,12 @@ void CZone::UpdateWeather() CTaskMgr::getInstance()->AddTask("zone_update_weather", server_clock::now() + std::chrono::seconds(WeatherNextUpdate), this, CTaskMgr::TASK_ONCE, 1s, [](time_point tick, CTaskMgr::CTask* PTask) { - return zone_update_weather(tick, PTask); + CZone* PZone = std::any_cast(PTask->m_data); + if (!PZone->IsWeatherStatic()) + { + PZone->UpdateWeather(); + } + return 0; }); // clang-format on } @@ -937,6 +906,7 @@ void CZone::WideScan(CCharEntity* PChar, uint16 radius) void CZone::ZoneServer(time_point tick) { TracyZoneScoped; + m_zoneEntities->ZoneServer(tick); if (m_BattlefieldHandler != nullptr) @@ -959,55 +929,85 @@ void CZone::ZoneServer(time_point tick) void CZone::ForEachChar(std::function const& func) { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_zoneEntities->m_charList, CCharEntity*, PChar) - { - func(PChar); - } + + m_zoneEntities->ForEachChar(func); } void CZone::ForEachCharInstance(CBaseEntity* PEntity, std::function const& func) { TracyZoneScoped; + ForEachChar(func); } void CZone::ForEachMob(std::function const& func) { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_zoneEntities->m_mobList, CMobEntity*, PMob) - { - func(PMob); - } + + m_zoneEntities->ForEachMob(func); } void CZone::ForEachMobInstance(CBaseEntity* PEntity, std::function const& func) { TracyZoneScoped; + ForEachMob(func); } +void CZone::ForEachNpc(std::function const& func) +{ + TracyZoneScoped; + + m_zoneEntities->ForEachNpc(func); +} + +void CZone::ForEachNpcInstance(CBaseEntity* PEntity, std::function const& func) +{ + TracyZoneScoped; + + ForEachNpc(func); +} + void CZone::ForEachTrust(std::function const& func) { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_zoneEntities->m_trustList, CTrustEntity*, PTrust) - { - func(PTrust); - } + + m_zoneEntities->ForEachTrust(func); } void CZone::ForEachTrustInstance(CBaseEntity* PEntity, std::function const& func) { TracyZoneScoped; + ForEachTrust(func); } -void CZone::ForEachNpc(std::function const& func) +void CZone::ForEachPet(std::function const& func) { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_zoneEntities->m_npcList, CNpcEntity*, PNpc) - { - func(PNpc); - } + + m_zoneEntities->ForEachPet(func); +} + +void CZone::ForEachPetInstance(CBaseEntity* PEntity, std::function const& func) +{ + TracyZoneScoped; + + ForEachPet(func); +} + +void CZone::ForEachAlly(std::function const& func) +{ + TracyZoneScoped; + + m_zoneEntities->ForEachAlly(func); +} + +void CZone::ForEachAllyInstance(CBaseEntity* PEntity, std::function const& func) +{ + TracyZoneScoped; + + ForEachAlly(func); } void CZone::createZoneTimers() @@ -1017,8 +1017,23 @@ void CZone::createZoneTimers() const auto tickInterval = std::chrono::milliseconds(static_cast(server_tick_interval)); const auto triggerAreaInterval = std::chrono::milliseconds(static_cast(server_trigger_area_interval)); - ZoneTimer = CTaskMgr::getInstance()->AddTask(m_zoneName, server_clock::now(), this, CTaskMgr::TASK_INTERVAL, tickInterval, zone_server); - ZoneTimerTriggerAreas = CTaskMgr::getInstance()->AddTask(m_zoneName + "TriggerAreas", server_clock::now(), this, CTaskMgr::TASK_INTERVAL, triggerAreaInterval, zone_trigger_area); + // clang-format off + ZoneTimer = CTaskMgr::getInstance()->AddTask(m_zoneName, server_clock::now(), this, CTaskMgr::TASK_INTERVAL, tickInterval, + [](time_point tick, CTaskMgr::CTask* PTask) + { + CZone* PZone = std::any_cast(PTask->m_data); + PZone->ZoneServer(tick); + return 0; + }); + + ZoneTimerTriggerAreas = CTaskMgr::getInstance()->AddTask(m_zoneName + "TriggerAreas", server_clock::now(), this, CTaskMgr::TASK_INTERVAL, triggerAreaInterval, + [](time_point tick, CTaskMgr::CTask* PTask) + { + CZone* PZone = std::any_cast(PTask->m_data); + PZone->CheckTriggerAreas(); + return 0; + }); + // clang-format on } void CZone::CharZoneIn(CCharEntity* PChar) @@ -1214,7 +1229,8 @@ void CZone::CheckTriggerAreas() { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_zoneEntities->m_charList, CCharEntity*, PChar) + // clang-format off + ForEachChar([&](CCharEntity* PChar) { // TODO: When we start to use octrees or spatial hashing to split up zones, // : use them here to make the search domain smaller. @@ -1242,5 +1258,6 @@ void CZone::CheckTriggerAreas() } } PChar->m_InsideTriggerAreaID = triggerAreaID; - } + }); + // clang-format on } diff --git a/src/map/zone.h b/src/map/zone.h index 3ce2efbec3a..c27b0ace3eb 100644 --- a/src/map/zone.h +++ b/src/map/zone.h @@ -519,7 +519,9 @@ struct zoneLine_t class CBasicPacket; class CBaseEntity; class CCharEntity; +class CMobEntity; class CNpcEntity; +class CPetEntity; class CBattleEntity; class CTrustEntity; class CTreasurePool; @@ -600,9 +602,6 @@ class CZone virtual void InsertPET(CBaseEntity* PPet); virtual void InsertTRUST(CBaseEntity* PTrust); - virtual void DeletePET(CBaseEntity* PPet); - virtual void DeleteTRUST(CBaseEntity* PTrust); - virtual void FindPartyForMob(CBaseEntity* PEntity); virtual void TransportDepart(uint16 boundary, uint16 zone); // Collect passengers if ship/boat is departing virtual void updateCharLevelRestriction(CCharEntity* PChar); // Removes the character's level restriction. If the zone has a level restriction, it is applied after it is removed. @@ -627,9 +626,14 @@ class CZone virtual void ForEachCharInstance(CBaseEntity* PEntity, std::function const& func); virtual void ForEachMob(std::function const& func); virtual void ForEachMobInstance(CBaseEntity* PEntity, std::function const& func); + virtual void ForEachNpc(std::function const& func); + virtual void ForEachNpcInstance(CBaseEntity* PEntity, std::function const& func); virtual void ForEachTrust(std::function const& func); virtual void ForEachTrustInstance(CBaseEntity* PEntity, std::function const& func); - virtual void ForEachNpc(std::function const& func); + virtual void ForEachPet(std::function const& func); + virtual void ForEachPetInstance(CBaseEntity* PEntity, std::function const& func); + virtual void ForEachAlly(std::function const& func); + virtual void ForEachAllyInstance(CBaseEntity* PEntity, std::function const& func); CZone(ZONEID ZoneID, REGION_TYPE RegionID, CONTINENT_TYPE ContinentID, uint8 levelRestriction); virtual ~CZone(); diff --git a/src/map/zone_entities.cpp b/src/map/zone_entities.cpp index 75c17e8f3dd..431f0173e53 100644 --- a/src/map/zone_entities.cpp +++ b/src/map/zone_entities.cpp @@ -281,14 +281,6 @@ void CZoneEntities::InsertNPC(CBaseEntity* PNpc) } } -void CZoneEntities::DeletePET(CBaseEntity* PPet) -{ - if (PPet != nullptr) - { - m_petList.erase(PPet->targid); - } -} - void CZoneEntities::InsertPET(CBaseEntity* PPet) { TracyZoneScoped; @@ -327,14 +319,6 @@ void CZoneEntities::InsertTRUST(CBaseEntity* PTrust) } } -void CZoneEntities::DeleteTRUST(CBaseEntity* PTrust) -{ - if (PTrust != nullptr) - { - m_trustList.erase(PTrust->id); - } -} - void CZoneEntities::FindPartyForMob(CBaseEntity* PEntity) { TracyZoneScoped; @@ -664,11 +648,76 @@ void CZoneEntities::AssignDynamicTargIDandLongID(CBaseEntity* PEntity) } } +void CZoneEntities::EraseStaleDynamicTargIDs() +{ + for (auto it = m_dynamicTargIdsToDelete.begin(); it != m_dynamicTargIdsToDelete.end();) + { + // Erase dynamic targid if it's stale enough + if ((server_clock::now() - it->second) > 60s) + { + m_dynamicTargIds.erase(it->first); + it = m_dynamicTargIdsToDelete.erase(it); + } + else + { + ++it; + } + } +} + bool CZoneEntities::CharListEmpty() const { return m_charList.empty(); } +void CZoneEntities::ForEachChar(std::function const& func) +{ + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) + { + func(PChar); + } +} + +void CZoneEntities::ForEachMob(std::function const& func) +{ + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) + { + func(PMob); + } +} + +void CZoneEntities::ForEachNpc(std::function const& func) +{ + FOR_EACH_PAIR_CAST_SECOND(m_npcList, CNpcEntity*, PNpc) + { + func(PNpc); + } +} + +void CZoneEntities::ForEachTrust(std::function const& func) +{ + FOR_EACH_PAIR_CAST_SECOND(m_trustList, CTrustEntity*, PTrust) + { + func(PTrust); + } +} + +void CZoneEntities::ForEachPet(std::function const& func) +{ + FOR_EACH_PAIR_CAST_SECOND(m_petList, CPetEntity*, PPet) + { + func(PPet); + } +} + +void CZoneEntities::ForEachAlly(std::function const& func) +{ + FOR_EACH_PAIR_CAST_SECOND(m_allyList, CMobEntity*, PAlly) + { + func(PAlly); + } +} + void CZoneEntities::DespawnPC(CCharEntity* PChar) { TracyZoneScoped; @@ -1617,6 +1666,10 @@ void CZoneEntities::ZoneServer(time_point tick) luautils::OnZoneTick(this->m_zone); + // + // Mob tick logic + // + FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) { if (!PMob) @@ -1711,6 +1764,10 @@ void CZoneEntities::ZoneServer(time_point tick) } } + // + // NPC tick logic + // + FOR_EACH_PAIR_CAST_SECOND(m_npcList, CNpcEntity*, PNpc) { ShowTrace(fmt::format("CZoneEntities::ZoneServer: NPC: {} ({})", PNpc->getName(), PNpc->id).c_str()); @@ -1733,6 +1790,10 @@ void CZoneEntities::ZoneServer(time_point tick) } } + // + // Pet tick logic + // + FOR_EACH_PAIR_CAST_SECOND(m_petList, CPetEntity*, PPet) { // TODO: The static_cast in this loop includes Battlefield Allies. Allies shouldn't be handled here in @@ -1765,6 +1826,10 @@ void CZoneEntities::ZoneServer(time_point tick) PPet->PAI->Tick(tick); } + // + // Trust tick logic + // + FOR_EACH_PAIR_CAST_SECOND(m_trustList, CTrustEntity*, PTrust) { ShowTrace(fmt::format("CZoneEntities::ZoneServer: Trust: {} ({})", PTrust->getName(), PTrust->id).c_str()); @@ -1800,6 +1865,10 @@ void CZoneEntities::ZoneServer(time_point tick) } } + // + // Char tick logic + // + FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) { ShowTrace(fmt::format("CZoneEntities::ZoneServer: Char: {} ({})", PChar->getName(), PChar->id).c_str()); @@ -1838,6 +1907,10 @@ void CZoneEntities::ZoneServer(time_point tick) } } + // + // Cleanup logic + // + for (const auto* PMob : m_mobsToDelete) { if (auto itr = m_mobList.find(PMob->id); itr != m_mobList.end()) diff --git a/src/map/zone_entities.h b/src/map/zone_entities.h index 975db45891d..2c6772563c0 100644 --- a/src/map/zone_entities.h +++ b/src/map/zone_entities.h @@ -37,6 +37,9 @@ class CZoneEntities { public: + CZoneEntities(CZone*); + ~CZoneEntities(); + void HealAllMobs(); void TryAddToNearbySpawnLists(CBaseEntity* PEntity); @@ -67,8 +70,6 @@ class CZoneEntities void InsertMOB(CBaseEntity* PMob); void InsertPET(CBaseEntity* PPet); void InsertTRUST(CBaseEntity* PTrust); - void DeletePET(CBaseEntity* PPet); - void DeleteTRUST(CBaseEntity* PTrust); void FindPartyForMob(CBaseEntity* PEntity); // looking for a party for the monster void TransportDepart(uint16 boundary, uint16 zone); // ship/boat is leaving, passengers need to be collected @@ -87,8 +88,19 @@ class CZoneEntities EntityList_t GetMobList() const; bool CharListEmpty() const; - uint16 GetNewCharTargID(); - void AssignDynamicTargIDandLongID(CBaseEntity* PEntity); + void ForEachChar(std::function const& func); + void ForEachMob(std::function const& func); + void ForEachNpc(std::function const& func); + void ForEachTrust(std::function const& func); + void ForEachPet(std::function const& func); + void ForEachAlly(std::function const& func); + + auto GetNewCharTargID() -> uint16; + void AssignDynamicTargIDandLongID(CBaseEntity* PEntity); + void EraseStaleDynamicTargIDs(); + +private: + CZone* m_zone; EntityList_t m_allyList; EntityList_t m_mobList; @@ -104,11 +116,6 @@ class CZoneEntities std::vector> m_dynamicTargIdsToDelete; // list of targids pending deletion at a later date - CZoneEntities(CZone*); - ~CZoneEntities(); - -private: - CZone* m_zone; time_point m_EffectCheckTime{ server_clock::now() }; time_point m_computeTime{ server_clock::now() }; diff --git a/src/map/zone_instance.cpp b/src/map/zone_instance.cpp index 5eca9694012..630a5b98ca9 100644 --- a/src/map/zone_instance.cpp +++ b/src/map/zone_instance.cpp @@ -105,15 +105,6 @@ void CZoneInstance::InsertNPC(CBaseEntity* PNpc) } } -void CZoneInstance::DeletePET(CBaseEntity* PPet) -{ - TracyZoneScoped; - if (PPet->PInstance) - { - PPet->PInstance->DeletePET(PPet); - } -} - void CZoneInstance::InsertPET(CBaseEntity* PPet) { TracyZoneScoped; @@ -132,15 +123,6 @@ void CZoneInstance::InsertTRUST(CBaseEntity* PTrust) } } -void CZoneInstance::DeleteTRUST(CBaseEntity* PTrust) -{ - TracyZoneScoped; - if (PTrust->PInstance) - { - PTrust->PInstance->DeleteTRUST(PTrust); - } -} - void CZoneInstance::FindPartyForMob(CBaseEntity* PEntity) { TracyZoneScoped; @@ -353,6 +335,7 @@ void CZoneInstance::TOTDChange(TIMETYPE TOTD) void CZoneInstance::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message_type, const std::unique_ptr& packet) { TracyZoneScoped; + if (PEntity) { if (PEntity->PInstance) @@ -372,6 +355,7 @@ void CZoneInstance::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message void CZoneInstance::UpdateCharPacket(CCharEntity* PChar, ENTITYUPDATE type, uint8 updatemask) { TracyZoneScoped; + if (PChar) { if (PChar->PInstance) @@ -391,6 +375,7 @@ void CZoneInstance::UpdateCharPacket(CCharEntity* PChar, ENTITYUPDATE type, uint void CZoneInstance::UpdateEntityPacket(CBaseEntity* PEntity, ENTITYUPDATE type, uint8 updatemask, bool alwaysInclude) { TracyZoneScoped; + if (PEntity) { if (PEntity->PInstance) @@ -410,6 +395,7 @@ void CZoneInstance::UpdateEntityPacket(CBaseEntity* PEntity, ENTITYUPDATE type, void CZoneInstance::WideScan(CCharEntity* PChar, uint16 radius) { TracyZoneScoped; + if (PChar->PInstance) { PChar->PInstance->WideScan(PChar, radius); @@ -419,6 +405,7 @@ void CZoneInstance::WideScan(CCharEntity* PChar, uint16 radius) void CZoneInstance::ZoneServer(time_point tick) { TracyZoneScoped; + std::vector instancesToRemove; for (const auto& PInstance : m_InstanceList) { @@ -447,16 +434,12 @@ void CZoneInstance::ZoneServer(time_point tick) void CZoneInstance::CheckTriggerAreas() { TracyZoneScoped; + for (const auto& PInstance : m_InstanceList) { - for (const auto& [targid, PEntity] : PInstance->m_charList) + // clang-format off + PInstance->ForEachChar([&](CCharEntity* PChar) { - auto* PChar = dynamic_cast(PEntity); - if (!PChar) - { - continue; - } - // TODO: When we start to use octrees or spatial hashing to split up zones, // : use them here to make the search domain smaller. @@ -483,46 +466,128 @@ void CZoneInstance::CheckTriggerAreas() } } PChar->m_InsideTriggerAreaID = triggerAreaID; - } + }); + // clang-format on } } void CZoneInstance::ForEachChar(const std::function& func) { TracyZoneScoped; + for (const auto& PInstance : m_InstanceList) { - for (const auto& [targid, PEntity] : PInstance->GetCharList()) - { - if (auto* PChar = dynamic_cast(PEntity)) - { - func(PChar); - } - } + PInstance->ForEachChar(func); } } void CZoneInstance::ForEachCharInstance(CBaseEntity* PEntity, const std::function& func) { TracyZoneScoped; - for (const auto& [_, PEntity] : PEntity->PInstance->GetCharList()) + + if (PEntity->PInstance) { - if (auto* PChar = dynamic_cast(PEntity)) - { - func(PChar); - } + PEntity->PInstance->ForEachChar(func); + } +} + +void CZoneInstance::ForEachMob(const std::function& func) +{ + TracyZoneScoped; + + for (const auto& PInstance : m_InstanceList) + { + PInstance->ForEachMob(func); } } void CZoneInstance::ForEachMobInstance(CBaseEntity* PEntity, const std::function& func) { TracyZoneScoped; - for (const auto& [_, PEntity] : PEntity->PInstance->m_mobList) + + if (PEntity->PInstance) { - if (auto* PMob = dynamic_cast(PEntity)) - { - func(PMob); - } + PEntity->PInstance->ForEachMob(func); + } +} + +void CZoneInstance::ForEachNpc(const std::function& func) +{ + TracyZoneScoped; + + for (const auto& PInstance : m_InstanceList) + { + PInstance->ForEachNpc(func); + } +} + +void CZoneInstance::ForEachNpcInstance(CBaseEntity* PEntity, const std::function& func) +{ + TracyZoneScoped; + + if (PEntity->PInstance) + { + PEntity->PInstance->ForEachNpc(func); + } +} + +void CZoneInstance::ForEachTrust(const std::function& func) +{ + TracyZoneScoped; + + for (const auto& PInstance : m_InstanceList) + { + PInstance->ForEachTrust(func); + } +} + +void CZoneInstance::ForEachTrustInstance(CBaseEntity* PEntity, const std::function& func) +{ + TracyZoneScoped; + + if (PEntity->PInstance) + { + PEntity->PInstance->ForEachTrust(func); + } +} + +void CZoneInstance::ForEachPet(const std::function& func) +{ + TracyZoneScoped; + + for (const auto& PInstance : m_InstanceList) + { + PInstance->ForEachPet(func); + } +} + +void CZoneInstance::ForEachPetInstance(CBaseEntity* PEntity, const std::function& func) +{ + TracyZoneScoped; + + if (PEntity->PInstance) + { + PEntity->PInstance->ForEachPet(func); + } +} + +void CZoneInstance::ForEachAlly(const std::function& func) +{ + TracyZoneScoped; + + for (const auto& PInstance : m_InstanceList) + { + PInstance->ForEachAlly(func); + } +} + +void CZoneInstance::ForEachAllyInstance(CBaseEntity* PEntity, const std::function& func) +{ + TracyZoneScoped; + + if (PEntity->PInstance) + { + PEntity->PInstance->ForEachAlly(func); } } diff --git a/src/map/zone_instance.h b/src/map/zone_instance.h index b17b7db42da..08d65c143df 100644 --- a/src/map/zone_instance.h +++ b/src/map/zone_instance.h @@ -51,8 +51,6 @@ class CZoneInstance : public CZone virtual void InsertMOB(CBaseEntity* PMob) override; virtual void InsertPET(CBaseEntity* PPet) override; virtual void InsertTRUST(CBaseEntity* PTrust) override; - virtual void DeleteTRUST(CBaseEntity* PTrust) override; - virtual void DeletePET(CBaseEntity* PPet) override; virtual void FindPartyForMob(CBaseEntity* PEntity) override; // looking for a party for the monster virtual void TransportDepart(uint16 boundary, uint16 zone) override; // ship/boat is leaving, passengers need to be collected @@ -66,9 +64,18 @@ class CZoneInstance : public CZone virtual void ZoneServer(time_point tick) override; virtual void CheckTriggerAreas() override; - virtual void ForEachChar(const std::function& func) override; - virtual void ForEachCharInstance(CBaseEntity* PEntity, const std::function& func) override; - virtual void ForEachMobInstance(CBaseEntity* PEntity, const std::function& func) override; + void ForEachChar(std::function const& func) override; + void ForEachCharInstance(CBaseEntity* PEntity, std::function const& func) override; + void ForEachMob(std::function const& func) override; + void ForEachMobInstance(CBaseEntity* PEntity, std::function const& func) override; + void ForEachNpc(std::function const& func) override; + void ForEachNpcInstance(CBaseEntity* PEntity, std::function const& func) override; + void ForEachTrust(std::function const& func) override; + void ForEachTrustInstance(CBaseEntity* PEntity, std::function const& func) override; + void ForEachPet(std::function const& func) override; + void ForEachPetInstance(CBaseEntity* PEntity, std::function const& func) override; + void ForEachAlly(std::function const& func) override; + void ForEachAllyInstance(CBaseEntity* PEntity, std::function const& func) override; CInstance* CreateInstance(uint16 instanceid); From 63180f6f28cfaf951fe04240aa275489cfe92594 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 20 Jan 2025 23:49:04 +0000 Subject: [PATCH 5/5] Core: Change arg order of FOR_EACH_PAIR_CAST_SECOND --- src/common/macros.h | 2 +- src/map/ai/helpers/targetfind.cpp | 6 +- src/map/lua/lua_baseentity.cpp | 2 +- src/map/utils/battleutils.cpp | 2 +- src/map/zone_entities.cpp | 142 +++++++++++++++--------------- src/map/zone_entities.h | 4 +- 6 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/common/macros.h b/src/common/macros.h index a4343c0ccf3..571db0d0c82 100644 --- a/src/common/macros.h +++ b/src/common/macros.h @@ -72,7 +72,7 @@ // // Assembly Analysis: https://github.com/LandSandBoat/server/pull/6751 // -#define FOR_EACH_PAIR_CAST_SECOND(_collection, _type, _var) \ +#define FOR_EACH_PAIR_CAST_SECOND(_type, _var, _collection) \ for (const auto& [_key, _value] : _collection) \ if (auto _var = static_cast<_type>(_value); true) diff --git a/src/map/ai/helpers/targetfind.cpp b/src/map/ai/helpers/targetfind.cpp index 10ad26dbeec..7a4bccc84d1 100644 --- a/src/map/ai/helpers/targetfind.cpp +++ b/src/map/ai/helpers/targetfind.cpp @@ -235,7 +235,7 @@ void CTargetFind::addAllInMobList(CBattleEntity* PTarget, bool withPet) CCharEntity* PChar = dynamic_cast(findMaster(m_PBattleEntity)); if (PChar) { - FOR_EACH_PAIR_CAST_SECOND(PChar->SpawnMOBList, CMobEntity*, PBattleTarget) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PBattleTarget, PChar->SpawnMOBList) { if (PBattleTarget && isMobOwner(PBattleTarget)) { @@ -332,9 +332,9 @@ void CTargetFind::addAllInRange(CBattleEntity* PTarget, float radius, ALLEGIANCE if (PTarget->objtype == TYPE_PC) { CCharEntity* PChar = static_cast(PTarget); - for (auto& list : { PChar->SpawnPCList, PChar->SpawnPETList }) + for (auto& spawnList : { PChar->SpawnPCList, PChar->SpawnPETList }) { - FOR_EACH_PAIR_CAST_SECOND(list, CBattleEntity*, PBattleEntity) + FOR_EACH_PAIR_CAST_SECOND(CBattleEntity*, PBattleEntity, spawnList) { if (PBattleEntity && isWithinArea(&(PBattleEntity->loc.p)) && !PBattleEntity->isDead() && PBattleEntity->allegiance == ALLEGIANCE_TYPE::PLAYER) diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 5cd9876746f..6f7bce6022d 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -12805,7 +12805,7 @@ void CLuaBaseEntity::transferEnmity(CLuaBaseEntity* entity, uint8 percent, float if (PIterEntity) { - FOR_EACH_PAIR_CAST_SECOND(PIterEntity->SpawnMOBList, CMobEntity*, PMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PMob, PIterEntity->SpawnMOBList) { if (isWithinDistance(PMob->loc.p, PEntity->loc.p, range)) { diff --git a/src/map/utils/battleutils.cpp b/src/map/utils/battleutils.cpp index e41935cb90a..f3dd21b374c 100644 --- a/src/map/utils/battleutils.cpp +++ b/src/map/utils/battleutils.cpp @@ -4463,7 +4463,7 @@ namespace battleutils if (PIterSource) { - FOR_EACH_PAIR_CAST_SECOND(PIterSource->SpawnMOBList, CMobEntity*, PCurrentMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PCurrentMob, PIterSource->SpawnMOBList) { if (PCurrentMob->m_HiPCLvl > 0 && PCurrentMob->PEnmityContainer->HasID(PSource->id)) { diff --git a/src/map/zone_entities.cpp b/src/map/zone_entities.cpp index 431f0173e53..ee24112dc9a 100644 --- a/src/map/zone_entities.cpp +++ b/src/map/zone_entities.cpp @@ -60,21 +60,23 @@ namespace { - const float ENTITY_RENDER_DISTANCE = 50.0f; - const float ENTITY_VERTICAL_RENDER_DISTANCE = 20.0f; - const float CHARACTER_SYNC_DISTANCE = 45.0f; - const float CHARACTER_DESPAWN_DISTANCE = 50.0f; - const int CHARACTER_SWAP_MAX = 5; - const int CHARACTER_SYNC_LIMIT_MAX = 32; - const int CHARACTER_SYNC_DISTANCE_SWAP_THRESHOLD = 30; - const int CHARACTER_SYNC_PARTY_SIGNIFICANCE = 100000; - const int CHARACTER_SYNC_ALLI_SIGNIFICANCE = 10000; - const int PERSIST_CHECK_CHARACTERS = 20; + constexpr auto DYNAMIC_ENTITY_TARGID_RANGE_START = 0x700; + constexpr auto ENTITY_RENDER_DISTANCE = 50.0f; + constexpr auto ENTITY_VERTICAL_RENDER_DISTANCE = 20.0f; + constexpr auto VERTICAL_RENDER_DISTANCE_OFFSET = 0.5f; + constexpr auto CHARACTER_SYNC_DISTANCE = 45.0f; + constexpr auto CHARACTER_DESPAWN_DISTANCE = 50.0f; + constexpr auto CHARACTER_SWAP_MAX = 5U; + constexpr auto CHARACTER_SYNC_LIMIT_MAX = 32U; + constexpr auto CHARACTER_SYNC_DISTANCE_SWAP_THRESHOLD = 30U; + constexpr auto CHARACTER_SYNC_PARTY_SIGNIFICANCE = 100000U; + constexpr auto CHARACTER_SYNC_ALLI_SIGNIFICANCE = 10000U; + constexpr auto PERSIST_CHECK_CHARACTERS = 20U; + constexpr auto INTERMEDIATE_CONTAINER_RESERVE_SIZE = 16U; inline bool isWithinVerticalDistance(CBaseEntity* source, CBaseEntity* target) { - constexpr float offset = 0.5f; - const float verticalDistance = target->loc.p.y - source->loc.p.y - offset; + const float verticalDistance = target->loc.p.y - source->loc.p.y - VERTICAL_RENDER_DISTANCE_OFFSET; return std::abs(verticalDistance) <= ENTITY_VERTICAL_RENDER_DISTANCE; } } // namespace @@ -82,20 +84,18 @@ namespace typedef std::pair CharScorePair; CZoneEntities::CZoneEntities(CZone* zone) -: m_nextDynamicTargID(0x700) // Start of dynamic entity range // TODO: Make this into a constexpr somewhere. -, m_zone(zone) -, m_lastCharComputeTargId(0) -, m_lastCharPersistTargId(0) +: m_zone(zone) +, m_nextDynamicTargID(DYNAMIC_ENTITY_TARGID_RANGE_START) { // Ensure internal collections have enough capacity so they won't resize at runtime. - m_mobsToDelete.reserve(16); - m_npcsToDelete.reserve(16); - m_petsToDelete.reserve(16); - m_trustsToDelete.reserve(16); - m_aggroableMobs.reserve(16); - m_charsToLogout.reserve(16); - m_charsToWarp.reserve(16); - m_charsToChangeZone.reserve(16); + m_mobsToDelete.reserve(INTERMEDIATE_CONTAINER_RESERVE_SIZE); + m_npcsToDelete.reserve(INTERMEDIATE_CONTAINER_RESERVE_SIZE); + m_petsToDelete.reserve(INTERMEDIATE_CONTAINER_RESERVE_SIZE); + m_trustsToDelete.reserve(INTERMEDIATE_CONTAINER_RESERVE_SIZE); + m_aggroableMobs.reserve(INTERMEDIATE_CONTAINER_RESERVE_SIZE); + m_charsToLogout.reserve(INTERMEDIATE_CONTAINER_RESERVE_SIZE); + m_charsToWarp.reserve(INTERMEDIATE_CONTAINER_RESERVE_SIZE); + m_charsToChangeZone.reserve(INTERMEDIATE_CONTAINER_RESERVE_SIZE); } CZoneEntities::~CZoneEntities() @@ -140,7 +140,7 @@ void CZoneEntities::HealAllMobs() { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PCurrentMob, m_mobList) { // keep resting until i'm full PCurrentMob->Rest(1); @@ -151,7 +151,7 @@ void CZoneEntities::TryAddToNearbySpawnLists(CBaseEntity* PEntity) { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { const auto isInHeightRange = isWithinVerticalDistance(PEntity, PCurrentChar); const auto isInRange = isWithinDistance(PEntity->loc.p, PCurrentChar->loc.p, ENTITY_RENDER_DISTANCE); @@ -343,7 +343,7 @@ void CZoneEntities::FindPartyForMob(CBaseEntity* PEntity) if ((forceLink || PMob->m_Link || PMob->m_Type & MOBTYPE_BATTLEFIELD) && PMob->PParty == nullptr) { - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PCurrentMob, m_mobList) { if (!forceLink && !PCurrentMob->m_Link) { @@ -370,7 +370,7 @@ void CZoneEntities::TransportDepart(uint16 boundary, uint16 zone) { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { if (PCurrentChar->loc.boundary == boundary) { @@ -402,7 +402,7 @@ void CZoneEntities::WeatherChange(WEATHER weather) const auto element = zoneutils::GetWeatherElement(weather); - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PCurrentMob, m_mobList) { PCurrentMob->PAI->EventHandler.triggerListener("WEATHER_CHANGE", CLuaBaseEntity(PCurrentMob), static_cast(weather), element); @@ -442,7 +442,7 @@ void CZoneEntities::WeatherChange(WEATHER weather) } } - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { PCurrentChar->PLatentEffectContainer->CheckLatentsWeather(weather); PCurrentChar->PAI->EventHandler.triggerListener("WEATHER_CHANGE", CLuaBaseEntity(PCurrentChar), static_cast(weather), element); @@ -453,7 +453,7 @@ void CZoneEntities::MusicChange(uint16 BlockID, uint16 MusicTrackID) { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PChar, m_charList) { PChar->pushPacket(BlockID, MusicTrackID); } @@ -502,7 +502,7 @@ void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) { PChar->PPet->PAI->Disengage(); - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { // inform other players of the pets removal SpawnIDList_t::iterator itr = PCurrentChar->SpawnPETList.find(PChar->PPet->id); @@ -521,7 +521,7 @@ void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) // Remove trusts for (const auto& PTrust : PChar->PTrusts) { - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { // inform other players of the trusts removal PCurrentChar->updateEntityPacket(PTrust, ENTITY_DESPAWN, UPDATE_NONE); @@ -535,7 +535,7 @@ void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) m_zone->m_BattlefieldHandler->RemoveFromBattlefield(PChar, PChar->PBattlefield, BATTLEFIELD_LEAVE_CODE_WARPDC); } - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PCurrentMob, m_mobList) { PCurrentMob->PEnmityContainer->LogoutReset(PChar->id); if (PCurrentMob->m_OwnerID.id == PChar->id) @@ -672,7 +672,7 @@ bool CZoneEntities::CharListEmpty() const void CZoneEntities::ForEachChar(std::function const& func) { - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PChar, m_charList) { func(PChar); } @@ -680,7 +680,7 @@ void CZoneEntities::ForEachChar(std::function const& func) void CZoneEntities::ForEachMob(std::function const& func) { - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PMob, m_mobList) { func(PMob); } @@ -688,7 +688,7 @@ void CZoneEntities::ForEachMob(std::function const& func) void CZoneEntities::ForEachNpc(std::function const& func) { - FOR_EACH_PAIR_CAST_SECOND(m_npcList, CNpcEntity*, PNpc) + FOR_EACH_PAIR_CAST_SECOND(CNpcEntity*, PNpc, m_npcList) { func(PNpc); } @@ -696,7 +696,7 @@ void CZoneEntities::ForEachNpc(std::function const& func) void CZoneEntities::ForEachTrust(std::function const& func) { - FOR_EACH_PAIR_CAST_SECOND(m_trustList, CTrustEntity*, PTrust) + FOR_EACH_PAIR_CAST_SECOND(CTrustEntity*, PTrust, m_trustList) { func(PTrust); } @@ -704,7 +704,7 @@ void CZoneEntities::ForEachTrust(std::function const& func) void CZoneEntities::ForEachPet(std::function const& func) { - FOR_EACH_PAIR_CAST_SECOND(m_petList, CPetEntity*, PPet) + FOR_EACH_PAIR_CAST_SECOND(CPetEntity*, PPet, m_petList) { func(PPet); } @@ -712,7 +712,7 @@ void CZoneEntities::ForEachPet(std::function const& func) void CZoneEntities::ForEachAlly(std::function const& func) { - FOR_EACH_PAIR_CAST_SECOND(m_allyList, CMobEntity*, PAlly) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PAlly, m_allyList) { func(PAlly); } @@ -722,7 +722,7 @@ void CZoneEntities::DespawnPC(CCharEntity* PChar) { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { const auto itr = PCurrentChar->SpawnPCList.find(PChar->id); const auto isInSpawnList = itr != PCurrentChar->SpawnPCList.end(); @@ -739,7 +739,7 @@ void CZoneEntities::SpawnMOBs(CCharEntity* PChar) { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PCurrentMob, m_mobList) { auto& spawnList = PChar->SpawnMOBList; @@ -999,7 +999,7 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) // Provide bonus score to characters targeted by spawned mobs or other conflict players, if in conflict std::unordered_map scoreBonus = std::unordered_map(); - FOR_EACH_PAIR_CAST_SECOND(PChar->SpawnMOBList, CMobEntity*, PMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PMob, PChar->SpawnMOBList) { CState* PState = PMob->PAI->GetCurrentState(); if (!PState) @@ -1018,7 +1018,7 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) MinHeap spawnedCharacters; std::vector toRemove; - FOR_EACH_PAIR_CAST_SECOND(PChar->SpawnPCList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, PChar->SpawnPCList) { // Despawn character if it's a hidden GM, is in a different mog house, or if player is in a conflict while other is not, or too far up/down if (PCurrentChar->m_isGMHidden || @@ -1068,7 +1068,7 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) // Find candidates to spawn MinHeap candidateCharacters; - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { if (PCurrentChar != nullptr && PChar != PCurrentChar && PChar->SpawnPCList.find(PCurrentChar->id) == PChar->SpawnPCList.end()) { @@ -1223,7 +1223,7 @@ void CZoneEntities::SpawnTransport(CCharEntity* PChar) { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_TransportList, CNpcEntity*, PEntity) + FOR_EACH_PAIR_CAST_SECOND(CNpcEntity*, PEntity, m_TransportList) { PChar->updateEntityPacket(PEntity, ENTITY_SPAWN, UPDATE_ALL_MOB); } @@ -1330,7 +1330,7 @@ void CZoneEntities::TOTDChange(TIMETYPE TOTD) break; case TIME_NEWDAY: { - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PMob, m_mobList) { if (PMob->m_SpawnType & SPAWNTYPE_ATNIGHT) { @@ -1344,7 +1344,7 @@ void CZoneEntities::TOTDChange(TIMETYPE TOTD) { ScriptType = SCRIPT_TIME_DAWN; - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PMob, m_mobList) { if (PMob->m_SpawnType & SPAWNTYPE_ATEVENING) { @@ -1368,7 +1368,7 @@ void CZoneEntities::TOTDChange(TIMETYPE TOTD) { ScriptType = SCRIPT_TIME_EVENING; - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PMob, m_mobList) { if (PMob->m_SpawnType & SPAWNTYPE_ATEVENING) { @@ -1381,7 +1381,7 @@ void CZoneEntities::TOTDChange(TIMETYPE TOTD) break; case TIME_NIGHT: { - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PMob, m_mobList) { if (PMob->m_SpawnType & SPAWNTYPE_ATNIGHT) { @@ -1397,7 +1397,7 @@ void CZoneEntities::TOTDChange(TIMETYPE TOTD) } if (ScriptType != SCRIPT_NONE) { - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PChar, m_charList) { charutils::CheckEquipLogic(PChar, ScriptType, TOTD); } @@ -1408,7 +1408,7 @@ void CZoneEntities::SavePlayTime() { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PChar, m_charList) { charutils::SavePlayTime(PChar); } @@ -1418,7 +1418,7 @@ CCharEntity* CZoneEntities::GetCharByName(const std::string& name) { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { if (strcmpi(PCurrentChar->getName().c_str(), name.c_str()) == 0) { @@ -1432,7 +1432,7 @@ CCharEntity* CZoneEntities::GetCharByID(uint32 id) { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { if (PCurrentChar->id == id) { @@ -1452,7 +1452,7 @@ void CZoneEntities::UpdateCharPacket(CCharEntity* PChar, ENTITYUPDATE type, uint return; } - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { if (PCurrentChar == PChar) { @@ -1470,7 +1470,7 @@ void CZoneEntities::UpdateEntityPacket(CBaseEntity* PEntity, ENTITYUPDATE type, { TracyZoneScoped; - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { if (alwaysInclude || type == ENTITY_SPAWN || type == ENTITY_DESPAWN || charutils::hasEntitySpawned(PCurrentChar, PEntity)) { @@ -1522,7 +1522,7 @@ void CZoneEntities::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message // 30 yalms if action packet, 50 otherwise const int checkDistance = packet->getType() == 0x0028 ? 30 : 50; - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { if (PEntity != PCurrentChar) { @@ -1608,7 +1608,7 @@ void CZoneEntities::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message case CHAR_INSHOUT: { TracyZoneCString("CHAR_INSHOUT"); - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { if (PEntity != PCurrentChar) { @@ -1624,7 +1624,7 @@ void CZoneEntities::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message case CHAR_INZONE: { TracyZoneCString("CHAR_INZONE"); - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PCurrentChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PCurrentChar, m_charList) { if (PCurrentChar->m_moghouseID == 0) { @@ -1670,7 +1670,7 @@ void CZoneEntities::ZoneServer(time_point tick) // Mob tick logic // - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PMob, m_mobList) { if (!PMob) { @@ -1707,7 +1707,7 @@ void CZoneEntities::ZoneServer(time_point tick) PMob->PMaster->PPet = nullptr; } - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, POtherMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, POtherMob, m_mobList) { POtherMob->PEnmityContainer->Clear(PMob->id); } @@ -1717,7 +1717,7 @@ void CZoneEntities::ZoneServer(time_point tick) PMob->PParty->RemoveMember(PMob); } - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PChar, m_charList) { if (PChar->PClaimedMob == PMob) { @@ -1748,7 +1748,7 @@ void CZoneEntities::ZoneServer(time_point tick) // Check to see if any aggroable mobs should be aggroed by other mobs for (const auto& PMob : m_aggroableMobs) { - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PCurrentMob, m_mobList) { const auto isInHeightRange = isWithinVerticalDistance(PMob, PCurrentMob); const auto isInRange = isWithinDistance(PMob->loc.p, PCurrentMob->loc.p, ENTITY_RENDER_DISTANCE); @@ -1768,7 +1768,7 @@ void CZoneEntities::ZoneServer(time_point tick) // NPC tick logic // - FOR_EACH_PAIR_CAST_SECOND(m_npcList, CNpcEntity*, PNpc) + FOR_EACH_PAIR_CAST_SECOND(CNpcEntity*, PNpc, m_npcList) { ShowTrace(fmt::format("CZoneEntities::ZoneServer: NPC: {} ({})", PNpc->getName(), PNpc->id).c_str()); @@ -1777,7 +1777,7 @@ void CZoneEntities::ZoneServer(time_point tick) // This is only valid for dynamic entities if (PNpc->status == STATUS_TYPE::DISAPPEAR && PNpc->m_bReleaseTargIDOnDisappear) { - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PChar, m_charList) { if (PChar->SpawnNPCList.find(PNpc->id) != PChar->SpawnNPCList.end()) { @@ -1794,7 +1794,7 @@ void CZoneEntities::ZoneServer(time_point tick) // Pet tick logic // - FOR_EACH_PAIR_CAST_SECOND(m_petList, CPetEntity*, PPet) + FOR_EACH_PAIR_CAST_SECOND(CPetEntity*, PPet, m_petList) { // TODO: The static_cast in this loop includes Battlefield Allies. Allies shouldn't be handled here in // : this way, but we need to do this to keep allies working (for now). @@ -1806,7 +1806,7 @@ void CZoneEntities::ZoneServer(time_point tick) // to prevent a number of issues which can result from a pet having a deleted/nullptr'd PMaster if (PPet->status == STATUS_TYPE::DISAPPEAR) { - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PCurrentMob, m_mobList) { PCurrentMob->PEnmityContainer->Clear(PPet->id); } @@ -1830,7 +1830,7 @@ void CZoneEntities::ZoneServer(time_point tick) // Trust tick logic // - FOR_EACH_PAIR_CAST_SECOND(m_trustList, CTrustEntity*, PTrust) + FOR_EACH_PAIR_CAST_SECOND(CTrustEntity*, PTrust, m_trustList) { ShowTrace(fmt::format("CZoneEntities::ZoneServer: Trust: {} ({})", PTrust->getName(), PTrust->id).c_str()); @@ -1846,12 +1846,12 @@ void CZoneEntities::ZoneServer(time_point tick) if (PTrust->status == STATUS_TYPE::DISAPPEAR) { - FOR_EACH_PAIR_CAST_SECOND(m_mobList, CMobEntity*, PCurrentMob) + FOR_EACH_PAIR_CAST_SECOND(CMobEntity*, PCurrentMob, m_mobList) { PCurrentMob->PEnmityContainer->Clear(PTrust->id); } - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PChar, m_charList) { if (distance(PChar->loc.p, PTrust->loc.p) < ENTITY_RENDER_DISTANCE) { @@ -1869,7 +1869,7 @@ void CZoneEntities::ZoneServer(time_point tick) // Char tick logic // - FOR_EACH_PAIR_CAST_SECOND(m_charList, CCharEntity*, PChar) + FOR_EACH_PAIR_CAST_SECOND(CCharEntity*, PChar, m_charList) { ShowTrace(fmt::format("CZoneEntities::ZoneServer: Char: {} ({})", PChar->getName(), PChar->id).c_str()); diff --git a/src/map/zone_entities.h b/src/map/zone_entities.h index 2c6772563c0..fc5905e8ca7 100644 --- a/src/map/zone_entities.h +++ b/src/map/zone_entities.h @@ -119,10 +119,10 @@ class CZoneEntities time_point m_EffectCheckTime{ server_clock::now() }; time_point m_computeTime{ server_clock::now() }; - uint16 m_lastCharComputeTargId; + uint16 m_lastCharComputeTargId{ 0 }; time_point m_charPersistTime{ server_clock::now() }; - uint16 m_lastCharPersistTargId; + uint16 m_lastCharPersistTargId{ 0 }; // // Intermediate collections for use inside ZoneServer