From bd2658946f48b16d46b5f6522eac9f41d5d434cd Mon Sep 17 00:00:00 2001 From: Image <> Date: Sun, 8 Mar 2020 20:42:54 -0400 Subject: [PATCH] Resolve Deadlock in MSpawnList against CombatProcess, barebones of disarm trap Resolved deadlock in CombatProcess (dead_spawns). Fixes #31. Added base for issue #24 --- EQ2/source/WorldServer/Combat.cpp | 2 +- EQ2/source/WorldServer/Commands/Commands.cpp | 4 +- EQ2/source/WorldServer/Entity.cpp | 3 +- EQ2/source/WorldServer/Entity.h | 7 ++ EQ2/source/WorldServer/LuaFunctions.cpp | 4 +- EQ2/source/WorldServer/Skills.cpp | 30 +++++ EQ2/source/WorldServer/Skills.h | 2 + EQ2/source/WorldServer/WorldDatabase.cpp | 4 +- EQ2/source/WorldServer/client.cpp | 113 ++++++++++++----- EQ2/source/WorldServer/client.h | 1 + EQ2/source/WorldServer/zoneserver.cpp | 122 +++++++++++-------- EQ2/source/WorldServer/zoneserver.h | 4 +- 12 files changed, 207 insertions(+), 89 deletions(-) diff --git a/EQ2/source/WorldServer/Combat.cpp b/EQ2/source/WorldServer/Combat.cpp index 547c41813..882a83078 100644 --- a/EQ2/source/WorldServer/Combat.cpp +++ b/EQ2/source/WorldServer/Combat.cpp @@ -1075,7 +1075,7 @@ void Entity::KillSpawn(Spawn* dead, int8 damage_type, int16 kill_blow_type) { // Kill movement for the dead npc so the corpse doesn't move dead->CalculateRunningLocation(true); - GetZone()->KillSpawn(dead, this, true, damage_type, kill_blow_type); + GetZone()->KillSpawn(true, dead, this, true, damage_type, kill_blow_type); } void Entity::ProcessCombat() { diff --git a/EQ2/source/WorldServer/Commands/Commands.cpp b/EQ2/source/WorldServer/Commands/Commands.cpp index 011cad5a0..539defe73 100644 --- a/EQ2/source/WorldServer/Commands/Commands.cpp +++ b/EQ2/source/WorldServer/Commands/Commands.cpp @@ -1758,7 +1758,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie if(dead && dead->IsPlayer() == false){ dead->SetHP(0); if(sep && sep->arg[0] && sep->IsNumber(0) && atoi(sep->arg[0]) == 1) - client->GetCurrentZone()->RemoveSpawn(dead, true); + client->GetCurrentZone()->RemoveSpawn(false, dead, true); else client->GetPlayer()->KillSpawn(dead); }else{ @@ -3327,7 +3327,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie delete_spawn = true; } if(delete_spawn) - client->GetCurrentZone()->RemoveSpawn(spawn, true, false); + client->GetCurrentZone()->RemoveSpawn(false, spawn, true, false); else spawn->SetSpawnLocationID(0); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Successfully removed spawn from zone"); diff --git a/EQ2/source/WorldServer/Entity.cpp b/EQ2/source/WorldServer/Entity.cpp index 75f664c18..d567389d0 100644 --- a/EQ2/source/WorldServer/Entity.cpp +++ b/EQ2/source/WorldServer/Entity.cpp @@ -48,6 +48,7 @@ Entity::Entity(){ memset(&ranged_combat_data, 0, sizeof(CombatData)); memset(&info_struct, 0, sizeof(InfoStruct)); loot_coins = 0; + trap_triggered = false; memset(&features, 0, sizeof(CharFeatures)); memset(&equipment, 0, sizeof(EQ2_Equipment)); pet = 0; @@ -1196,7 +1197,7 @@ void Entity::DismissPet(NPC* pet, bool from_death) { // remove the spawn from the world if (!from_death && pet->GetPetType() != PET_TYPE_CHARMED) - GetZone()->RemoveSpawn(pet); + GetZone()->RemoveSpawn(false, pet); } float Entity::CalculateBonusMod() { diff --git a/EQ2/source/WorldServer/Entity.h b/EQ2/source/WorldServer/Entity.h index a23f9ab27..8eae23744 100644 --- a/EQ2/source/WorldServer/Entity.h +++ b/EQ2/source/WorldServer/Entity.h @@ -502,6 +502,12 @@ public: void AddLootCoins(int32 coins){ loot_coins += coins; } + bool HasTrapTriggered() { + return trap_triggered; + } + void SetTrapTriggered(bool triggered) { + trap_triggered = triggered; + } void AddLootItem(int32 id, int16 charges = 1){ Item* master_item = master_item_list.GetItem(id); if(master_item){ @@ -817,6 +823,7 @@ private: float max_speed; vector loot_items; int32 loot_coins; + bool trap_triggered; int8 deity; sint16 regen_hp_rate; sint16 regen_power_rate; diff --git a/EQ2/source/WorldServer/LuaFunctions.cpp b/EQ2/source/WorldServer/LuaFunctions.cpp index 601be6c11..6d37deb02 100644 --- a/EQ2/source/WorldServer/LuaFunctions.cpp +++ b/EQ2/source/WorldServer/LuaFunctions.cpp @@ -184,7 +184,7 @@ int EQ2Emu_lua_KillSpawn(lua_State* state) { Spawn* killer = lua_interface->GetSpawn(state, 2); bool send_packet = (lua_interface->GetInt8Value(state, 3) == 1); if(dead && dead->Alive() && dead->GetZone()) - dead->GetZone()->KillSpawn(dead, killer, send_packet); + dead->GetZone()->KillSpawn(true, dead, killer, send_packet); return 0; } @@ -3351,7 +3351,7 @@ int EQ2Emu_lua_Harvest(lua_State* state){ ((GroundSpawn*)node)->ProcessHarvest(client); if(((GroundSpawn*)node)->GetNumberHarvests() == 0) - player->GetZone()->RemoveSpawn(node, true); + player->GetZone()->RemoveSpawn(false, node, true); } } else if(player && player->IsPlayer()){ diff --git a/EQ2/source/WorldServer/Skills.cpp b/EQ2/source/WorldServer/Skills.cpp index 123f40276..70cc97980 100644 --- a/EQ2/source/WorldServer/Skills.cpp +++ b/EQ2/source/WorldServer/Skills.cpp @@ -481,3 +481,33 @@ void PlayerSkillList::RemoveSkillBonus(int32 spell_id) { safe_delete(sb); } } + +int Skill::CheckDisarmSkill(int16 targetLevel, int8 chest_difficulty) +{ + if (chest_difficulty < 2) // no triggers on this chest type + return 1; + + int chest_diff_result = targetLevel * chest_difficulty; + float base_difficulty = 15.0f; + float fail_threshold = 10.0f; + + + float chance = ((100.0f - base_difficulty) * ((float)current_val / (float)chest_diff_result)); + + if (chance > (100.0f - base_difficulty)) + { + chance = 100.0f - base_difficulty; + } + + float d100 = (float)MakeRandomFloat(0, 100); + + if (d100 <= chance) + return 1; + else + { + if (d100 > (chance + fail_threshold)) + return -1; + } + + return 0; +} \ No newline at end of file diff --git a/EQ2/source/WorldServer/Skills.h b/EQ2/source/WorldServer/Skills.h index fede140f5..b83fafb65 100644 --- a/EQ2/source/WorldServer/Skills.h +++ b/EQ2/source/WorldServer/Skills.h @@ -72,6 +72,8 @@ public: EQ2_16BitString name; EQ2_16BitString description; bool save_needed; + + int CheckDisarmSkill(int16 targetLevel, int8 chest_difficulty=0); }; class MasterSkillList{ diff --git a/EQ2/source/WorldServer/WorldDatabase.cpp b/EQ2/source/WorldServer/WorldDatabase.cpp index 8cbd525f7..a80a12148 100644 --- a/EQ2/source/WorldServer/WorldDatabase.cpp +++ b/EQ2/source/WorldServer/WorldDatabase.cpp @@ -191,7 +191,7 @@ int32 WorldDatabase::LoadCharacterSpells(int32 char_id, Player* player) void WorldDatabase::SavePlayerSpells(Client* client) { - if(!client) + if(!client || client->GetCharacterID() < 1) return; LogWrite(SPELL__DEBUG, 3, "Spells", "Saving Spell(s) for Player: '%s'", client->GetPlayer()->GetName()); @@ -3090,7 +3090,7 @@ bool WorldDatabase::SaveCombinedSpawnLocation(ZoneServer* zone, Spawn* in_spawn, } for(itr=spawns->begin();itr!=spawns->end();itr++){ spawn = *itr; - zone->RemoveSpawn(spawn); + zone->RemoveSpawn(false, spawn); } safe_delete(spawns); } diff --git a/EQ2/source/WorldServer/client.cpp b/EQ2/source/WorldServer/client.cpp index fc81b675f..772145d78 100644 --- a/EQ2/source/WorldServer/client.cpp +++ b/EQ2/source/WorldServer/client.cpp @@ -586,7 +586,7 @@ void Client::HandlePlayerRevive(int32 point_id) safe_delete(packet); } - GetCurrentZone()->RemoveSpawn(player, false); + GetCurrentZone()->RemoveSpawn(false, player, false); m_resurrect.writelock(__FUNCTION__, __LINE__); if(current_rez.active) @@ -1299,7 +1299,7 @@ bool Client::HandlePacket(EQApplicationPacket *app) { GetPlayer()->SetCharSheetChanged(true); GetCurrentZone()->SendDamagePacket(0, GetPlayer(), DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE, GetPlayer()->GetInvulnerable() ? DAMAGE_PACKET_RESULT_INVULNERABLE : DAMAGE_PACKET_RESULT_SUCCESSFUL, DAMAGE_PACKET_DAMAGE_TYPE_FALLING, damage, 0); if(GetPlayer()->GetHP() == 0) { - GetCurrentZone()->KillSpawn(GetPlayer(), 0); + GetCurrentZone()->KillSpawn(false, GetPlayer(), 0); } } } @@ -2724,7 +2724,7 @@ void ClientList::Remove(Client* client, bool remove_data) { void Client::SetCurrentZone(int32 id){ if(current_zone){ //current_zone->GetCombat()->RemoveHate(player); - current_zone->RemoveSpawn(player, false); + current_zone->RemoveSpawn(false, player, false); } SetCurrentZone(zone_list.Get(id)); @@ -2733,7 +2733,7 @@ void Client::SetCurrentZone(int32 id){ void Client::SetCurrentZoneByInstanceID(int32 id,int32 zoneid){ if(current_zone){ //current_zone->GetCombat()->RemoveHate(player); - current_zone->RemoveSpawn(player, false); + current_zone->RemoveSpawn(false, player, false); } SetCurrentZone(zone_list.GetByInstanceID(id,zoneid)); @@ -3181,7 +3181,7 @@ void Client::Zone(ZoneServer* new_zone, bool set_coords){ LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Removing player from current zone...", __FUNCTION__); - GetCurrentZone()->RemoveSpawn(player, false); + GetCurrentZone()->RemoveSpawn(false, player, false); LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Setting zone to '%s'...", __FUNCTION__, new_zone->GetZoneName()); SetCurrentZone(new_zone); @@ -3952,7 +3952,7 @@ void Client::Loot(int32 total_coins, vector* items, Entity* entity){ memcpy(data, &packet_size, sizeof(int32)); packet_size += sizeof(int32); EQ2Packet* outapp = new EQ2Packet(OP_ClientCmdMsg, data, packet_size); - //DumpPacket(outapp); + //DumpPacket(outapp); QueuePacket(outapp); safe_delete_array(data); safe_delete(packet); @@ -3967,36 +3967,87 @@ void Client::Loot(Entity* entity){ Loot(total_coins, entity->GetLootItems(), entity); entity->UnlockLoot(); - int32 state = 0; - // Check for the chest and set the action state - /*4034 = small chest | 5864 = treasure chest | 5865 = ornate treasure chest | 4015 = exquisite chest*/ - if (entity->GetModelType() == 4034) { - // small chest, open with copper coins - state = 11899; - } - else if (entity->GetModelType() == 5864) { - // treasure chest, open with silver coins - state = 11901; - } - else if (entity->GetModelType() == 5865) { - // ornate chest, open with gold coins - state = 11900; - } - else if (entity->GetModelType() == 4015) { - // exquisite chest, open with gold coins and jewels as well as a glow effect - state = 11898; - } - - // We set the visual state with out updating so those not in range will see it opened when it is finally sent to them, - // for those in range the SendStateCommand will cause it to animate open. - entity->SetVisualState(state, false); - GetCurrentZone()->SendStateCommand(entity, state); + OpenChest(entity); } else SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not allowed to loot at this time."); } +void Client::OpenChest(Entity* entity) +{ + if (!entity) + return; + + int8 chest_difficulty = 0; + int32 state = 0; + // Check for the chest and set the action state + /*4034 = small chest | 5864 = treasure chest | 5865 = ornate treasure chest | 4015 = exquisite chest*/ + if (entity->GetModelType() == 4034) { + // small chest, open with copper coins + // does not include traps, however can be disarmed + chest_difficulty = 1; + state = 11899; + } + else if (entity->GetModelType() == 5864) { + // treasure chest, open with silver coins + chest_difficulty = 2; + state = 11901; + } + else if (entity->GetModelType() == 5865) { + // ornate chest, open with gold coins + chest_difficulty = 3; + state = 11900; + } + else if (entity->GetModelType() == 4015) { + // exquisite chest, open with gold coins and jewels as well as a glow effect + chest_difficulty = 5; + state = 11898; + } + boolean firstChestOpen = false; + + if (chest_difficulty > 0 && !entity->HasTrapTriggered()) + { + Skill* disarmSkill = GetPlayer()->GetSkillByName("Disarm Trap", false); + firstChestOpen = true; + entity->SetTrapTriggered(true); + if (disarmSkill) + { + if (disarmSkill->CheckDisarmSkill(entity->GetLevel(), chest_difficulty) < 1) + { + //Spell* spell = master_spell_list.GetSpell(spellid, tier); + + //GetPlayer()->GetZone()->GetSpellProcess()->CastInstant(spell, (Entity*)GetPlayer(), (Entity*)GetPlayer()); + + SimpleMessage(CHANNEL_COLOR_YELLOW, "You failed to disarm the chest."); + } + else + { + SimpleMessage(CHANNEL_COLOR_YELLOW, "You have disarmed the chest."); + GetPlayer()->GetSkillByName("Disarm Trap", true); + } + } + else + { + SimpleMessage(CHANNEL_COLOR_YELLOW, "You triggered the chest."); + } + } + else if(!entity->HasTrapTriggered()) + { + firstChestOpen = true; + entity->SetTrapTriggered(true); + } + + // We set the visual state with out updating so those not in range will see it opened when it is finally sent to them, + // for those in range the SendStateCommand will cause it to animate open. + + // TODO: when player enters radius that does not have visual state, update visual state + if (firstChestOpen) + entity->SetVisualState(state, false); + + GetCurrentZone()->SendStateCommand(entity, state); +} + Spawn* Client::GetBanker(){ return banker; } @@ -5006,7 +5057,7 @@ void Client::SaveCombineSpawns(const char* name){ SimpleMessage(CHANNEL_COLOR_YELLOW, "Error: You only have a single Spawn in the group!"); else if(database.SaveCombinedSpawnLocation(GetCurrentZone(), combine_spawn, name)){ Message(CHANNEL_COLOR_YELLOW, "Successfully combined %u spawns into spawn location: %u", count, combine_spawn->GetSpawnLocationID()); - GetCurrentZone()->RemoveSpawn(combine_spawn); + GetCurrentZone()->RemoveSpawn(false, combine_spawn); } else SimpleMessage(CHANNEL_COLOR_YELLOW, "Error saving spawn group, check console for details."); diff --git a/EQ2/source/WorldServer/client.h b/EQ2/source/WorldServer/client.h index 228533d11..d16b7ff6e 100644 --- a/EQ2/source/WorldServer/client.h +++ b/EQ2/source/WorldServer/client.h @@ -244,6 +244,7 @@ public: void SendPendingLoot(int32 total_coins, Entity* entity); void Loot(int32 total_coins, vector* items, Entity* entity); void Loot(Entity* entity); + void OpenChest(Entity* entity); void CheckPlayerQuestsKillUpdate(Spawn* spawn); void CheckPlayerQuestsChatUpdate(Spawn* spawn); void CheckPlayerQuestsItemUpdate(Item* item); diff --git a/EQ2/source/WorldServer/zoneserver.cpp b/EQ2/source/WorldServer/zoneserver.cpp index faba737d7..b947db3cb 100644 --- a/EQ2/source/WorldServer/zoneserver.cpp +++ b/EQ2/source/WorldServer/zoneserver.cpp @@ -1202,9 +1202,9 @@ void ZoneServer::DelayedSpawnRemoval(bool force_delete_all) { } if (spawn->IsPlayer()) - RemoveSpawn(spawn, false); + RemoveSpawn(false, spawn, false); else - RemoveSpawn(spawn); + RemoveSpawn(false, spawn); } } } @@ -1614,7 +1614,7 @@ void ZoneServer::CheckDeadSpawnRemoval() { if (!spawn->IsPlayer()) { dead_spawns.erase(spawn->GetID()); - RemoveSpawn(spawn, true, true, false); + RemoveSpawn(true, spawn, true, true, false); } } } @@ -1831,7 +1831,7 @@ void ZoneServer::ProcessDrowning(){ vector::iterator itr; for(itr = dead_list.begin(); itr != dead_list.end(); itr++){ RemoveDrowningVictim((*itr)->GetPlayer()); - KillSpawn((*itr)->GetPlayer(), 0); + KillSpawn(false, (*itr)->GetPlayer(), 0); (*itr)->SimpleMessage(CHANNEL_COLOR_WHITE, "You are sleeping with the fishes! Glug, glug..."); } } @@ -2863,7 +2863,7 @@ void ZoneServer::RemoveClient(Client* client) client->GetPlayer()->DismissPet((NPC*)client->GetPlayer()->GetDeityPet()); client->GetPlayer()->DismissPet((NPC*)client->GetPlayer()->GetCosmeticPet()); - RemoveSpawn(client->GetPlayer(), false); + RemoveSpawn(false, client->GetPlayer(), false); connected_clients.Remove(client, true, DisconnectClientTimer); //} } @@ -3644,7 +3644,7 @@ void ZoneServer::KillSpawnByDistance(Spawn* spawn, float max_distance, bool incl test_spawn = itr->second; if(test_spawn && test_spawn->IsEntity() && test_spawn != spawn && (!test_spawn->IsPlayer() || include_players)){ if(test_spawn->GetDistance(spawn) < max_distance) - KillSpawn(test_spawn, spawn, send_packet); + KillSpawn(true, test_spawn, spawn, send_packet); } } MSpawnList.releasereadlock(__FUNCTION__, __LINE__); @@ -3681,7 +3681,7 @@ void ZoneServer::RemoveFromRangeMap(Client* client){ } */ -void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool lock) +void ZoneServer::RemoveSpawn(bool spawnListLocked, Spawn* spawn, bool delete_spawn, bool respawn, bool lock) { LogWrite(ZONE__DEBUG, 3, "Zone", "Processing RemoveSpawn function..."); @@ -3690,18 +3690,74 @@ void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool } RemoveSpawnSupportFunctions(spawn); - RemoveDeadEnemyList(spawn); + if (reloading) + RemoveDeadEnemyList(spawn); + + LogWrite(ZONE__DEBUG, 7, "Zone", "Lock DeadSpawns..."); + if (lock) MDeadSpawns.writelock(__FUNCTION__, __LINE__); + + LogWrite(ZONE__DEBUG, 7, "Zone", "Erase DeadSpawns..."); if (dead_spawns.count(spawn->GetID()) > 0) dead_spawns.erase(spawn->GetID()); if (lock) - MDeadSpawns.releasewritelock(__FUNCTION__, __LINE__);; + MDeadSpawns.releasewritelock(__FUNCTION__, __LINE__); + LogWrite(ZONE__DEBUG, 7, "Zone", "End DeadSpawns..."); + + + LogWrite(ZONE__DEBUG, 7, "Zone", "SpawnExpireTimers..."); if (spawn_expire_timers.count(spawn->GetID()) > 0) spawn_expire_timers.erase(spawn->GetID()); + LogWrite(ZONE__DEBUG, 7, "Zone", "SpawnExpireTimers Done..."); RemoveDelayedSpawnRemove(spawn); + LogWrite(ZONE__DEBUG, 7, "Zone", "RemoveDelayedSpawnRemove Done..."); + + // Clear the pointer in the spawn list, spawn thread will remove the key + if (!spawnListLocked) + MSpawnList.writelock(__FUNCTION__, __LINE__); + + LogWrite(ZONE__DEBUG, 7, "Zone", "RemoveSpawnList Start..."); + spawn_list[spawn->GetID()] = 0; + + if (!spawnListLocked) + MSpawnList.releasewritelock(__FUNCTION__, __LINE__); + LogWrite(ZONE__DEBUG, 7, "Zone", "RemoveSpawnList Done..."); + + PacketStruct* packet = 0; + int16 packet_version = 0; + Client* client = 0; + + vector::iterator client_itr; + + LogWrite(ZONE__DEBUG, 7, "Zone", "ClientList Start..."); + MClientList.readlock(__FUNCTION__, __LINE__); + for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) { + client = *client_itr; + + if (client) { + if (client->IsConnected() && (!packet || packet_version != client->GetVersion())) + { + safe_delete(packet); + packet_version = client->GetVersion(); + packet = configReader.getStruct("WS_DestroyGhostCmd", packet_version); + } + + if (client->GetPlayer()->HasTarget() && client->GetPlayer()->GetTarget() == spawn) + client->GetPlayer()->SetTarget(0); + + SendRemoveSpawn(client, spawn, packet, delete_spawn); + if (spawn_range_map.count(client) > 0) + spawn_range_map.Get(client)->erase(spawn->GetID()); + } + } + MClientList.releasereadlock(__FUNCTION__, __LINE__); + + LogWrite(ZONE__DEBUG, 7, "Zone", "ClientList End..."); + + safe_delete(packet); if(respawn && !spawn->IsPlayer() && spawn->GetRespawnTime() > 0 && spawn->GetSpawnLocationID() > 0) { @@ -3725,50 +3781,20 @@ void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool } } else - respawn_timers.Put(spawn->GetSpawnLocationID(), Timer::GetCurrentTime2() + spawn->GetRespawnTime()*1000); - } - - PacketStruct* packet = 0; - int16 packet_version = 0; - Client* client = 0; - - // Clear the pointer in the spawn list, spawn thread will remove the key - MSpawnList.writelock(__FUNCTION__, __LINE__); - spawn_list.erase(spawn->GetID()); - MSpawnList.releasewritelock(__FUNCTION__, __LINE__); - - vector::iterator client_itr; - - MClientList.readlock(__FUNCTION__, __LINE__); - for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) { - client = *client_itr; - - if (client) { - if(client->IsConnected() && (!packet || packet_version != client->GetVersion())) - { - safe_delete(packet); - packet_version = client->GetVersion(); - packet = configReader.getStruct("WS_DestroyGhostCmd", packet_version); - } - - if(client->GetPlayer()->HasTarget() && client->GetPlayer()->GetTarget() == spawn) - client->GetPlayer()->SetTarget(0); - - SendRemoveSpawn(client, spawn, packet, delete_spawn); - if(spawn_range_map.count(client) > 0) - spawn_range_map.Get(client)->erase(spawn->GetID()); + { + int32 spawnLocationID = spawn->GetSpawnLocationID(); + int32 spawnRespawnTime = spawn->GetRespawnTime(); + safe_delete(spawn); + respawn_timers.Put(spawnLocationID, Timer::GetCurrentTime2() + spawnRespawnTime * 1000); } } - MClientList.releasereadlock(__FUNCTION__, __LINE__); - - safe_delete(packet); // Do we really need the mutex locks and check to dead_spawns as we remove it from dead spawns at the start of this function - if (lock) + if (lock && !respawn) MDeadSpawns.readlock(__FUNCTION__, __LINE__); - if(delete_spawn && dead_spawns.count(spawn->GetID()) == 0) + if(!respawn && delete_spawn && dead_spawns.count(spawn->GetID()) == 0) AddPendingDelete(spawn); - if (lock) + if (lock && !respawn) MDeadSpawns.releasereadlock(__FUNCTION__, __LINE__); LogWrite(ZONE__DEBUG, 3, "Zone", "Done processing RemoveSpawn function..."); @@ -4017,7 +4043,7 @@ void ZoneServer::Despawn(Spawn* spawn, int32 timer){ AddDeadSpawn(spawn, timer); } -void ZoneServer::KillSpawn(Spawn* dead, Spawn* killer, bool send_packet, int8 damage_type, int16 kill_blow_type) +void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, bool send_packet, int8 damage_type, int16 kill_blow_type) { MDeadSpawns.readlock(__FUNCTION__, __LINE__); if(!dead || this->dead_spawns.count(dead->GetID()) > 0) { diff --git a/EQ2/source/WorldServer/zoneserver.h b/EQ2/source/WorldServer/zoneserver.h index 96082eb96..17b5f45ee 100644 --- a/EQ2/source/WorldServer/zoneserver.h +++ b/EQ2/source/WorldServer/zoneserver.h @@ -267,7 +267,7 @@ public: void AddSpawnGroupChance(int32 group_id, float percent); - void RemoveSpawn(Spawn* spawn, bool delete_spawn = true, bool respawn = true, bool lock = true); + void RemoveSpawn(bool spawnListLocked, Spawn* spawn, bool delete_spawn = true, bool respawn = true, bool lock = true); void ProcessSpawnLocations(); void SendQuestUpdates(Client* client, Spawn* spawn = 0); @@ -295,7 +295,7 @@ public: vector GetPlayers(); - void KillSpawn(Spawn* dead, Spawn* killer, bool send_packet = true, int8 damage_type = 0, int16 kill_blow_type = 0); + void KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, bool send_packet = true, int8 damage_type = 0, int16 kill_blow_type = 0); void SendDamagePacket(Spawn* attacker, Spawn* victim, int8 type1, int8 type2, int8 damage_type, int16 damage, const char* spell_name); void SendHealPacket(Spawn* caster, Spawn* target, int16 type, int32 heal_amt, const char* spell_name);