Prevent accessing spawn list outside of read lock, removal of pending spawn removals (redundant)

This commit is contained in:
Emagi 2025-07-31 13:23:42 -04:00
parent 9ef48d3bab
commit b7f56e3b73
4 changed files with 20 additions and 95 deletions

View File

@ -294,7 +294,7 @@ void Entity::MeleeAttack(Spawn* victim, float distance, bool primary, bool multi
DamageSpawn((Entity*)victim, DAMAGE_PACKET_TYPE_SIMPLE_CRIT_DMG, damage_type, min_damage, max_damage, 0);
}
else*/
DamageSpawn((Entity*)victim, DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE, damage_type, min_damage, max_damage, 0);
DamageSpawn((Entity*)victim, DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE, damage_type, min_damage, max_damage, 0, 0, false, false, false, false, nullptr, false, true);
if (!multi_attack) {
CheckProcs(PROC_TYPE_OFFENSIVE, victim);
CheckProcs(PROC_TYPE_PHYSICAL_OFFENSIVE, victim);
@ -1057,7 +1057,7 @@ Skill* Entity::GetSkillByWeaponType(int8 type, int8 damage_type, bool update) {
return 0;
}
bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_damage, int32 high_damage, const char* spell_name, int8 crit_mod, bool is_tick, bool no_calcs, bool ignore_attacker, bool take_power, LuaSpell* spell, bool skip_check_wards) {
bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_damage, int32 high_damage, const char* spell_name, int8 crit_mod, bool is_tick, bool no_calcs, bool ignore_attacker, bool take_power, LuaSpell* spell, bool skip_check_wards, bool is_melee_spawn) {
if(spell) {
spell->is_damage_spell = true;
}
@ -1236,7 +1236,7 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_
}
if (victim->GetHP() <= 0)
KillSpawn(victim, type, damage_type, blow_type);
KillSpawn(victim, type, damage_type, blow_type, is_melee_spawn);
else {
victim->CheckProcs(PROC_TYPE_DEFENSIVE, this);
if (spell_name)
@ -1439,7 +1439,7 @@ bool Entity::CheckInterruptSpell(Entity* attacker) {
return false;
}
void Entity::KillSpawn(Spawn* dead, int8 type, int8 damage_type, int16 kill_blow_type) {
void Entity::KillSpawn(Spawn* dead, int8 type, int8 damage_type, int16 kill_blow_type, bool is_melee_spawn) {
if(!dead)
return;
@ -1479,7 +1479,7 @@ void Entity::KillSpawn(Spawn* dead, int8 type, int8 damage_type, int16 kill_blow
dead->ClearRunningLocations();
dead->CalculateRunningLocation(true);
GetZone()->KillSpawn(true, dead, this, true, type, damage_type, kill_blow_type);
GetZone()->KillSpawn(is_melee_spawn, dead, this, true, type, damage_type, kill_blow_type);
}
void Entity::HandleDeathExperienceDebt(Spawn* killer)
@ -1552,7 +1552,7 @@ void Player::ProcessCombat() {
return;
//If no target delete combat_target and return out
Spawn* Target = GetZone()->GetSpawnByID(target);
Spawn* Target = GetZone()->GetSpawnByID(target, true);
if (!Target) {
combat_target = 0;
if (target > 0) {

View File

@ -1563,12 +1563,12 @@ public:
int8 DetermineHit(Spawn* victim, int8 type, int8 damage_type, float ToHitBonus, bool is_caster_spell, LuaSpell* lua_spell = nullptr);
float GetDamageTypeResistPercentage(int8 damage_type);
Skill* GetSkillByWeaponType(int8 type, int8 damage_type, bool update);
bool DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_damage, int32 high_damage, const char* spell_name, int8 crit_mod = 0, bool is_tick = false, bool no_damage_calcs = false, bool ignore_attacker = false, bool take_power = false, LuaSpell* spell = 0, bool skip_check_wards = false);
bool DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_damage, int32 high_damage, const char* spell_name, int8 crit_mod = 0, bool is_tick = false, bool no_damage_calcs = false, bool ignore_attacker = false, bool take_power = false, LuaSpell* spell = 0, bool skip_check_wards = false, bool is_melee_spawn = false);
float CalculateMitigation(int8 type = DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE, int8 damage_type = 0, int16 attacker_level = 0, bool for_pvp = false);
void AddHate(Entity* attacker, sint32 hate, bool ignore_pet_behavior = false);
bool CheckInterruptSpell(Entity* attacker);
bool CheckFizzleSpell(LuaSpell* spell);
void KillSpawn(Spawn* dead, int8 type = 0, int8 damage_type = 0, int16 kill_blow_type = 0);
void KillSpawn(Spawn* dead, int8 type = 0, int8 damage_type = 0, int16 kill_blow_type = 0, bool is_melee_spawn = false);
void HandleDeathExperienceDebt(Spawn* killer);
void SetAttackDelay(bool primary = false, bool ranged = false);
float CalculateAttackSpeedMod();

View File

@ -175,8 +175,6 @@ ZoneServer::ZoneServer(const char* name) {
tradeskillMgr = nullptr;
watchdogTimestamp = Timer::GetCurrentTime2();
MPendingSpawnRemoval.SetName("ZoneServer::MPendingSpawnRemoval");
lifetime_client_count = 0;
groupraidMinLevel = 0;
@ -311,6 +309,7 @@ void ZoneServer::Init()
shutdownTimer.Disable();
spawn_range.Start(rule_manager.GetGlobalRule(R_Zone, CheckAttackPlayer)->GetInt32());
aggro_timer.Start(rule_manager.GetGlobalRule(R_Zone, CheckAttackNPC)->GetInt32());
delete_timer.Start(1000);
/* Weather stuff */
InitWeather();
@ -1528,21 +1527,13 @@ void ZoneServer::DeleteSpawns(bool delete_all) {
continue;
}
MPendingSpawnRemoval.readlock(__FUNCTION__, __LINE__);
if(!delete_all && m_pendingSpawnRemove.count(spawn->GetID())) {
to_keep.emplace_back(entry);
MPendingSpawnRemoval.releasereadlock(__FUNCTION__, __LINE__);
continue;
}
MPendingSpawnRemoval.releasereadlock(__FUNCTION__, __LINE__);
lua_interface->SetLuaUserDataStale(spawn);
if (spellProcess) {
spellProcess->RemoveCaster(spawn, true);
}
if(movementMgr != nullptr) {
if(spawn->IsEntity() && movementMgr != nullptr) {
movementMgr->RemoveMob((Entity*)spawn);
}
@ -1890,10 +1881,7 @@ bool ZoneServer::SpawnProcess(){
bool spawnRange = spawn_range.Check();
bool checkRemove = spawn_check_remove.Check();
bool aggroCheck = aggro_timer.Check();
vector<int32> pending_spawn_list_remove;
// Check to see if there are any spawn id's that need to be removed from the spawn list, if so remove them all
ProcessSpawnRemovals();
bool deleteTimer = delete_timer.Check();
map<int32, Spawn*>::iterator itr;
if (spawnRange || checkRemove)
@ -1960,40 +1948,16 @@ bool ZoneServer::SpawnProcess(){
CombatProcess(spawn);
}
}
else {
// unable to get a valid spawn, lets add the id to another list to remove from the spawn list after this loop is finished
pending_spawn_list_remove.push_back(itr->first);
}
}
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
}
// Check to see if there are any spawn id's that need to be removed from the spawn list, if so remove them all
if (pending_spawn_list_remove.size() > 0) {
MSpawnList.writelock(__FUNCTION__, __LINE__);
vector<int32>::iterator itr2;
for (itr2 = pending_spawn_list_remove.begin(); itr2 != pending_spawn_list_remove.end(); itr2++) {
spawn_list.erase(*itr2);
subspawn_list[SUBSPAWN_TYPES::COLLECTOR].erase(*itr2);
std::map<int32,int32>::iterator hsmitr = housing_spawn_map.find(*itr2);
if(hsmitr != housing_spawn_map.end()) {
subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].erase(hsmitr->second);
housing_spawn_map.erase(hsmitr);
}
}
pending_spawn_list_remove.clear();
MSpawnList.releasewritelock(__FUNCTION__, __LINE__);
}
// Double Check to see if there are any spawn id's that need to be removed from the spawn list, if so remove them before we replace with pending spawns
// and also potentially further down when we delete the Spawn* in DeleteSpawns(false)
ProcessSpawnRemovals();
// Check to see if there are spawns waiting to be added to the spawn list, if so add them all
if (pending_spawn_list_add.size() > 0) {
MPendingSpawnListAdd.readlock(__FUNCTION__, __LINE__);
int32 pending_count = pending_spawn_list_add.size();
MPendingSpawnListAdd.releasereadlock(__FUNCTION__, __LINE__);
if (pending_count > 0) {
ProcessPendingSpawns();
}
@ -2011,7 +1975,7 @@ bool ZoneServer::SpawnProcess(){
// Delete unused spawns, do this last
if(!zoneShuttingDown)
if(!zoneShuttingDown && deleteTimer)
DeleteSpawns(false);
// Nothing should come after this
@ -4706,7 +4670,7 @@ void ZoneServer::KillSpawnByDistance(Spawn* spawn, float max_distance, bool incl
if(test_spawn && test_spawn->Alive() && test_spawn->GetID() > 0 && test_spawn->GetID() != spawn->GetID() && test_spawn->IsEntity() &&
(!test_spawn->IsPlayer() || include_players)){
if(test_spawn->GetDistance(spawn) < max_distance)
KillSpawn(true, test_spawn, spawn, send_packet);
KillSpawn(false, test_spawn, spawn, send_packet);
}
}
grids->second->MSpawns.unlock_shared();
@ -4832,10 +4796,6 @@ void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool
spawn->SetDeletedSpawn(true);
// we will remove the spawn ptr and entry in the spawn_list later.. it is not safe right now (lua? client process? spawn process? etc? too many factors)
if(erase_from_spawn_list)
AddPendingSpawnRemove(spawn->GetID());
if(respawn && !spawn->IsPlayer() && spawn->GetSpawnLocationID() > 0) {
LogWrite(ZONE__DEBUG, 3, "Zone", "Handle NPC Respawn for '%s'.", spawn->GetName());
AddRespawn(spawn);
@ -5131,7 +5091,7 @@ void ZoneServer::ProcessFaction(Spawn* spawn, Client* client)
}
void ZoneServer::Despawn(Spawn* spawn, int32 timer){
if (spawn && movementMgr != nullptr) {
if (spawn && spawn->IsEntity() && movementMgr != nullptr) {
movementMgr->RemoveMob((Entity*)spawn);
}
if(!spawn || spawn->IsPlayer())
@ -5453,7 +5413,7 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
spellProcess->RemoveSpellFromQueue((Player*)dead, true);
if (dead->IsNPC())
((NPC*)dead)->Brain()->ClearHate(!spawnListLocked);
((NPC*)dead)->Brain()->ClearHate(spawnListLocked);
safe_delete(encounter);
@ -9164,36 +9124,6 @@ Spawn* ZoneServer::GetSpawnFromUniqueItemID(int32 unique_id)
return nullptr;
}
void ZoneServer::AddPendingSpawnRemove(int32 id)
{
MPendingSpawnRemoval.writelock(__FUNCTION__, __LINE__);
m_pendingSpawnRemove.insert(make_pair(id,true));
MPendingSpawnRemoval.releasewritelock(__FUNCTION__, __LINE__);
}
void ZoneServer::ProcessSpawnRemovals()
{
MSpawnList.writelock(__FUNCTION__, __LINE__);
MPendingSpawnRemoval.writelock(__FUNCTION__, __LINE__);
if (m_pendingSpawnRemove.size() > 0) {
map<int32,bool>::iterator itr2;
for (itr2 = m_pendingSpawnRemove.begin(); itr2 != m_pendingSpawnRemove.end(); itr2++) {
spawn_list.erase(itr2->first);
subspawn_list[SUBSPAWN_TYPES::COLLECTOR].erase(itr2->first);
std::map<int32,int32>::iterator hsmitr = housing_spawn_map.find(itr2->first);
if(hsmitr != housing_spawn_map.end()) {
subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].erase(hsmitr->second);
housing_spawn_map.erase(hsmitr);
}
}
m_pendingSpawnRemove.clear();
}
MPendingSpawnRemoval.releasewritelock(__FUNCTION__, __LINE__);
MSpawnList.releasewritelock(__FUNCTION__, __LINE__);
}
void ZoneServer::AddSpawnToGroup(Spawn* spawn, int32 group_id)
{
if( spawn->GetSpawnGroupID() > 0 )

View File

@ -706,9 +706,6 @@ public:
void SetWatchdogTime(int32 time) { watchdogTimestamp = time; }
void CancelThreads();
void AddPendingSpawnRemove(int32 id);
void ProcessSpawnRemovals();
bool SendRemoveSpawn(Client* client, Spawn* spawn, PacketStruct* packet = 0, bool delete_spawn = false);
void AddSpawnToGroup(Spawn* spawn, int32 group_id);
@ -924,6 +921,7 @@ private:
Timer widget_timer;
Timer queue_updates;
Timer shutdownDelayTimer;
Timer delete_timer;
/* Enums */
Instance_Type InstanceType;
@ -1029,9 +1027,6 @@ private:
int32 watchdogTimestamp;
std::map<int32, bool> m_pendingSpawnRemove;
Mutex MPendingSpawnRemoval;
std::map<int32, int32> lua_queued_state_commands;
std::map<int32, std::map<std::string, float>> lua_spawn_update_command;
std::mutex MLuaQueueStateCmd;