From 4e43c73f9c9157765ddadb76aac5cc3f9ce61a4a Mon Sep 17 00:00:00 2001 From: Emagi Date: Tue, 3 Jun 2025 10:01:25 -0400 Subject: [PATCH] Fix #23 - Wards deleted while in use causing crash --- source/WorldServer/Entity.cpp | 53 +++++++++++++++++++++-------- source/WorldServer/Entity.h | 3 ++ source/WorldServer/LuaFunctions.cpp | 3 +- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/source/WorldServer/Entity.cpp b/source/WorldServer/Entity.cpp index 6cde2f0..8267283 100644 --- a/source/WorldServer/Entity.cpp +++ b/source/WorldServer/Entity.cpp @@ -2221,7 +2221,13 @@ float Entity::CalculateDPSMultiplier(){ } void Entity::AddWard(int32 spellID, WardInfo* ward) { - if (m_wardList.count(spellID) == 0) { + if(MWardList.try_lock()) { + if (m_wardList.count(spellID) == 0) { + m_wardList[spellID] = ward; + } + MWardList.unlock(); + } + else if (m_wardList.count(spellID) == 0) { m_wardList[spellID] = ward; } } @@ -2229,22 +2235,32 @@ void Entity::AddWard(int32 spellID, WardInfo* ward) { WardInfo* Entity::GetWard(int32 spellID) { WardInfo* ret = 0; - if (m_wardList.count(spellID) > 0) + if (MWardList.try_lock_shared()) { + if (m_wardList.count(spellID) > 0) + ret = m_wardList[spellID]; + MWardList.unlock_shared(); + } + else if (m_wardList.count(spellID) > 0) ret = m_wardList[spellID]; - + return ret; } void Entity::RemoveWard(int32 spellID) { - if (m_wardList.count(spellID) > 0) { - // Delete the ward info - safe_delete(m_wardList[spellID]); - // Remove from the ward list - m_wardList.erase(spellID); + if(MWardList.try_lock()) { + if (m_wardList.count(spellID) > 0) { + WardInfo* info = m_wardList[spellID]; + info->DeleteWard = true; + } + MWardList.unlock(); + } + else if (m_wardList.count(spellID) > 0) { + m_wardList[spellID]->DeleteWard = true; } } int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) { + std::unique_lock lock(MWardList); map::iterator itr; WardInfo* ward = 0; LuaSpell* spell = 0; @@ -2252,7 +2268,7 @@ int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) { while (m_wardList.size() > 0 && damage > 0) { // Get the ward with the lowest base damage for (itr = m_wardList.begin(); itr != m_wardList.end(); itr++) { - if(itr->second->RoundTriggered) + if(itr->second->RoundTriggered || itr->second->DeleteWard) continue; if (!ward || itr->second->BaseDamage < ward->BaseDamage) { @@ -2319,9 +2335,8 @@ int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) { GetZone()->SendHealPacket(spell->caster, this, HEAL_PACKET_TYPE_ABSORB, ward->DamageLeft, spell->spell->GetName()); if (!ward->keepWard) { + ward->DeleteWard = true; hasSpellBeenRemoved = true; - RemoveWard(spell->spell->GetSpellID()); - GetZone()->GetSpellProcess()->DeleteCasterSpell(spell, "purged"); } } else { @@ -2379,16 +2394,24 @@ int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) { if (shouldRemoveSpell && !hasSpellBeenRemoved) { - RemoveWard(spell->spell->GetSpellID()); - GetZone()->GetSpellProcess()->DeleteCasterSpell(spell, "purged"); + ward->DeleteWard = true; } // Reset ward pointer ward = 0; } - for (itr = m_wardList.begin(); itr != m_wardList.end(); itr++) { - itr->second->RoundTriggered = false; + for (itr = m_wardList.begin(); itr != m_wardList.end();) { + if(itr->second->DeleteWard) { + WardInfo* info = itr->second; + itr = m_wardList.erase(itr); + GetZone()->GetSpellProcess()->DeleteCasterSpell(info->Spell, "purged"); + safe_delete(info); + } + else { + itr->second->RoundTriggered = false; + itr++; + } } return damage; diff --git a/source/WorldServer/Entity.h b/source/WorldServer/Entity.h index 61a6ef6..980d5de 100644 --- a/source/WorldServer/Entity.h +++ b/source/WorldServer/Entity.h @@ -1308,6 +1308,8 @@ struct WardInfo { bool AbsorbAllDamage; // damage is always absorbed, usually spells based on hits, when we pass damage in AddWard as 0 this will be set to true bool RoundTriggered; + + bool DeleteWard; // removal after process CheckWard while loop }; #define WARD_TYPE_ALL 0 @@ -2111,6 +2113,7 @@ private: // int32 = spell id, WardInfo* = pointer to ward info map m_wardList; + mutable std::shared_mutex MWardList; // int8 = type, vector = list of pointers to proc info map > m_procList; diff --git a/source/WorldServer/LuaFunctions.cpp b/source/WorldServer/LuaFunctions.cpp index 1e73dc0..b358581 100644 --- a/source/WorldServer/LuaFunctions.cpp +++ b/source/WorldServer/LuaFunctions.cpp @@ -6577,7 +6577,8 @@ int EQ2Emu_lua_AddWard(lua_State* state) { if (wardType == WARD_TYPE_MAGICAL) ward->DamageType = damageTypes; - + + ward->DeleteWard = false; // Add the ward to the entity ((Entity*)target)->AddWard(spell->spell->GetSpellID(), ward); ward_was_added = true;