Browse Source

Resolve Deadlock in MSpawnList against CombatProcess, barebones of disarm trap

Resolved deadlock in CombatProcess (dead_spawns).  Fixes #31.

Added base for issue #24
Image 4 years ago
parent
commit
bd2658946f

+ 1 - 1
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() {

+ 2 - 2
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");

+ 2 - 1
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() {

+ 7 - 0
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<Item*>	loot_items;
 	int32			loot_coins;
+	bool			trap_triggered;
 	int8	deity;
 	sint16	regen_hp_rate;
 	sint16	regen_power_rate;

+ 2 - 2
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()){

+ 30 - 0
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;
+}

+ 2 - 0
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{

+ 2 - 2
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);
 	}

+ 81 - 30
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<Item*>* 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,34 +3967,85 @@ 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;
+		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 if (entity->GetModelType() == 4015) {
-			// exquisite chest, open with gold coins and jewels as well as a glow effect
-			state = 11898;
+		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.
 
-		// 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);
-	}
-	else
-		SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not allowed to loot at this time.");
 
+	GetCurrentZone()->SendStateCommand(entity, state);
 }
 
 Spawn* Client::GetBanker(){
@@ -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.");

+ 1 - 0
EQ2/source/WorldServer/client.h

@@ -244,6 +244,7 @@ public:
 	void	SendPendingLoot(int32 total_coins, Entity* entity);
 	void	Loot(int32 total_coins, vector<Item*>* items, Entity* entity);
 	void	Loot(Entity* entity);
+	void	OpenChest(Entity* entity);
 	void	CheckPlayerQuestsKillUpdate(Spawn* spawn);
 	void	CheckPlayerQuestsChatUpdate(Spawn* spawn);
 	void	CheckPlayerQuestsItemUpdate(Item* item);

+ 71 - 45
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<Client*>::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,85 +3690,111 @@ 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...");
 
-	if(respawn && !spawn->IsPlayer() && spawn->GetRespawnTime() > 0 && spawn->GetSpawnLocationID() > 0)
-	{
-		LogWrite(ZONE__DEBUG, 3, "Zone", "Handle NPC Respawn for '%s'.", spawn->GetName());
-
-		// handle instance spawn db info
-		// we don't care if a NPC or a client kills the spawn, we could have events that cause NPCs to kill NPCs.
-		if(spawn->GetZone()->GetInstanceID() > 0 && spawn->GetSpawnLocationID() > 0)
-		{
-			LogWrite(ZONE__DEBUG, 3, "Zone", "Handle NPC Respawn in an Instance.");
-			// use respawn time to either insert/update entry (likely insert in this situation)
-			if ( spawn->IsNPC() )
-			{
-				database.CreateInstanceSpawnRemoved(spawn->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_NPC, 
-				spawn->GetRespawnTime(),spawn->GetZone()->GetInstanceID());
-			}
-			else if ( spawn->IsObject ( ) )
-			{
-				database.CreateInstanceSpawnRemoved(spawn->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_OBJECT, 
-				spawn->GetRespawnTime(),spawn->GetZone()->GetInstanceID());
-			}
-		}
-		else
-			respawn_timers.Put(spawn->GetSpawnLocationID(), Timer::GetCurrentTime2() + spawn->GetRespawnTime()*1000);
-	}
+	// 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;
 
-	// 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<Client*>::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()))
+			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)
+			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)
+			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)
+	{
+		LogWrite(ZONE__DEBUG, 3, "Zone", "Handle NPC Respawn for '%s'.", spawn->GetName());
+
+		// handle instance spawn db info
+		// we don't care if a NPC or a client kills the spawn, we could have events that cause NPCs to kill NPCs.
+		if(spawn->GetZone()->GetInstanceID() > 0 && spawn->GetSpawnLocationID() > 0)
+		{
+			LogWrite(ZONE__DEBUG, 3, "Zone", "Handle NPC Respawn in an Instance.");
+			// use respawn time to either insert/update entry (likely insert in this situation)
+			if ( spawn->IsNPC() )
+			{
+				database.CreateInstanceSpawnRemoved(spawn->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_NPC, 
+				spawn->GetRespawnTime(),spawn->GetZone()->GetInstanceID());
+			}
+			else if ( spawn->IsObject ( ) )
+			{
+				database.CreateInstanceSpawnRemoved(spawn->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_OBJECT, 
+				spawn->GetRespawnTime(),spawn->GetZone()->GetInstanceID());
+			}
+		}
+		else
+		{
+			int32 spawnLocationID = spawn->GetSpawnLocationID();
+			int32 spawnRespawnTime = spawn->GetRespawnTime();
+			safe_delete(spawn);
+			respawn_timers.Put(spawnLocationID, Timer::GetCurrentTime2() + spawnRespawnTime * 1000);
+		}
+	}
+
 	// 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) {

+ 2 - 2
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<Entity*> 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);