Browse Source

Spawn Sending/Removal handling to resolve ghost_map crash

Also resolves crashes related to group members
Trying to target a spawn the client doesnt' have (index wise) crash fix
resetting hp on runback only when previously in combat
Image 2 years ago
parent
commit
502ebefe71

+ 0 - 1
EQ2/source/WorldServer/ClientPacketFunctions.cpp

@@ -95,7 +95,6 @@ void ClientPacketFunctions::SendCharacterData ( Client* client ){
 	EQ2Packet* outapp = client->GetPlayer()->serialize(client->GetPlayer(), client->GetVersion());
 	//DumpPacket(outapp);
 	client->QueuePacket(outapp);
-	//client->GetPlayer()->ClearRemovedSpawn(client->GetPlayer());
 }
 
 void ClientPacketFunctions::SendCharacterSheet ( Client* client ){

+ 8 - 2
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -178,7 +178,7 @@ int EQ2Emu_lua_PlayFlavor(lua_State* state) {
 		if (player && player->IsPlayer())
 			client = spawn->GetZone()->GetClientBySpawn(player);
 		if (client) {
-			if (((Player*)player)->WasSentSpawn(spawn->GetID()) && !((Player*)player)->WasSpawnRemoved(spawn))
+			if (((Player*)player)->WasSentSpawn(spawn->GetID()))
 				spawn->GetZone()->PlayFlavor(client, spawn, mp3, text, emote, key1, key2, language);
 		}
 		else
@@ -775,7 +775,7 @@ int EQ2Emu_lua_PlayVoice(lua_State* state) {
 		if (player && player->IsPlayer())
 			client = spawn->GetZone()->GetClientBySpawn(player);
 		if (client) {
-			if (((Player*)player)->WasSentSpawn(spawn->GetID()) && !((Player*)player)->WasSpawnRemoved(spawn))
+			if (((Player*)player)->WasSentSpawn(spawn->GetID()))
 				spawn->GetZone()->PlayVoice(client, spawn, mp3_string.c_str(), key1, key2);
 		}
 		else
@@ -10332,6 +10332,12 @@ int EQ2Emu_lua_Evac(lua_State* state) {
 					PacketStruct* packet = configReader.getStruct("WS_TeleportWithinZone", client->GetVersion());
 					if (packet)
 					{
+						PacketStruct* packet2 = configReader.getStruct("WS_DestroyGhostCmd", client->GetVersion());
+						packet2->setDataByName("spawn_index", client->GetPlayer()->GetIndexForSpawn(target2));
+						packet2->setDataByName("delete", 1);	
+										
+						client->QueuePacket(packet2->serialize(), true);
+						safe_delete(packet2);
 						client->SetReloadingZone(true);
 						packet->setDataByName("x", x);
 						packet->setDataByName("y", y);

+ 2 - 1
EQ2/source/WorldServer/NPC.cpp

@@ -287,7 +287,8 @@ void NPC::InCombat(bool val){
 		bool hadRunback = (GetRunbackLocation() != nullptr);
 		if(hadRunback) {
 			pause_timer.Disable();
-			ClearRunback();
+			if(!GetRunbackLocation()->reset_hp_on_runback) // if we aren't resetting hp it isn't a real reset point, just face target swings
+				ClearRunback();
 		}
 		if(!IsPet()) {
 			StartRunback(true);

+ 149 - 43
EQ2/source/WorldServer/Player.cpp

@@ -116,6 +116,7 @@ Player::Player(){
 	vis_mutex.SetName("Player::vis_mutex");
 	info_mutex.SetName("Player::info_mutex");
 	index_mutex.SetName("Player::index_mutex");
+	spawn_mutex.SetName("Player::spawn_mutex");
 	m_playerSpawnQuestsRequired.SetName("Player::player_spawn_quests_required");
 	m_playerSpawnHistoryRequired.SetName("Player::player_spawn_history_required");
 	gm_vision = false;
@@ -177,7 +178,6 @@ Player::~Player(){
 	safe_delete(info);
 	index_mutex.writelock(__FUNCTION__, __LINE__);
 	player_spawn_reverse_id_map.clear();
-	player_removed_spawns.clear();
 	player_spawn_id_map.clear();
 	index_mutex.releasewritelock(__FUNCTION__, __LINE__);
 
@@ -3257,9 +3257,18 @@ SpellEffects* Player::GetSpellEffects() {
 	return GetInfoStruct()->spell_effects;
 }
 
+// call inside info_mutex
+void Player::ClearRemovalTimers(){
+	map<int32, SpawnRemoval*>::iterator itr;
+	for(itr = spawn_removal_list.begin(); itr != spawn_removal_list.end();) {
+		SpawnRemoval* sr = itr->second;
+		itr = spawn_removal_list.erase(itr);
+		safe_delete(sr);
+	}
+}
+
 void Player::ClearEverything(){
 	index_mutex.writelock(__FUNCTION__, __LINE__);
-	player_removed_spawns.clear();
 	player_spawn_id_map.clear();
 	player_spawn_reverse_id_map.clear();
 	index_mutex.releasewritelock(__FUNCTION__, __LINE__);
@@ -3278,6 +3287,11 @@ void Player::ClearEverything(){
 	player_spawn_history_required.clear();
 	m_playerSpawnHistoryRequired.releasewritelock(__FUNCTION__, __LINE__);
 
+	spawn_mutex.writelock(__FUNCTION__, __LINE__);
+	ClearRemovalTimers();
+	spawn_packet_sent.clear();
+	spawn_mutex.releasewritelock(__FUNCTION__, __LINE__);
+
 	info_mutex.writelock(__FUNCTION__, __LINE__);
 	spawn_info_packet_list.clear();
 	info_mutex.releasewritelock(__FUNCTION__, __LINE__);
@@ -3621,9 +3635,10 @@ void Player::PrepareIncomingMovementPacket(int32 len, uchar* data, int16 version
 		if (GetBoatSpawn() == 0 && GetZone()) {
 			boat = GetZone()->GetClosestTransportSpawn(GetX(), GetY(), GetZ());
 			SetBoatSpawn(boat);
-			printf("Set boat: %s\n", boat ? boat->GetName() : "notset");
 			if(boat)
 			{
+				LogWrite(PLAYER__DEBUG, 0, "Player", "Set Player %s (%u) on Boat: %s", 
+					GetName(), GetCharacterID(), boat ? boat->GetName() : "notset");
 				boat->AddRailPassenger(GetCharacterID());
 				GetZone()->CallSpawnScript(boat, SPAWN_SCRIPT_BOARD, this);
 			}
@@ -3654,12 +3669,13 @@ void Player::PrepareIncomingMovementPacket(int32 len, uchar* data, int16 version
 	}
 	else if(lift_cooldown.Check())
 	{
-		printf("Disable boat\n");
 		if(GetBoatSpawn())
 		{
 			Spawn* boat = GetZone()->GetSpawnByID(GetBoatSpawn());
 			if(boat)
 			{
+				LogWrite(PLAYER__DEBUG, 0, "Player", "Remove Player %s (%u) from Boat: %s", 
+					GetName(), GetCharacterID(), boat ? boat->GetName() : "notset");
 				boat->RemoveRailPassenger(GetCharacterID());
 				GetZone()->CallSpawnScript(boat, SPAWN_SCRIPT_DEBOARD, this);
 			}
@@ -3782,15 +3798,114 @@ bool Player::CheckPlayerInfo(){
 	return info != 0;
 }
 
-bool Player::NeedsSpawnResent(Spawn* spawn){
-	return WasSentSpawn(spawn->GetID()) && WasSpawnRemoved(spawn);
+bool Player::SetSpawnSentState(Spawn* spawn, SpawnState state) {
+	bool val = true;
+	spawn_mutex.writelock(__FUNCTION__, __LINE__);
+	int16 index = GetIndexForSpawn(spawn);
+	if(index > 0 && state == SpawnState::SPAWN_STATE_SENDING) {
+		LogWrite(PLAYER__WARNING, 0, "Player", "Spawn ALREADY INDEXED for Player %s (%u).  Spawn %s (index %u) in state %u.", 
+			GetName(), GetCharacterID(), spawn->GetName(), index, state);
+		// we don't do anything this spawn is already populated by the player 
+	}
+	else {
+		LogWrite(PLAYER__DEBUG, 0, "Player", "Spawn for Player %s (%u).  Spawn %s (index %u) in state %u.", 
+			GetName(), GetCharacterID(), spawn->GetName(), index, state);
+		
+		map<int32,int8>::iterator itr = spawn_packet_sent.find(spawn->GetID());
+		if(itr != spawn_packet_sent.end())
+			itr->second = state;
+		else
+			spawn_packet_sent.insert(make_pair(spawn->GetID(), state));
+
+		if(state == SpawnState::SPAWN_STATE_REMOVING && 
+			spawn_removal_list.count(spawn->GetID()) == 0) {
+			spawn_removal_list.erase(spawn->GetID());
+			SpawnRemoval* removal = new SpawnRemoval;
+			removal->index_id = index;
+			removal->spawn_removal_timer = Timer(1000, true);
+			removal->spawn_removal_timer.Start();
+			spawn_removal_list.insert(make_pair(spawn->GetID(),removal));
+		}
+	}
+	spawn_mutex.releasewritelock(__FUNCTION__, __LINE__);
+	return val;
+}
+
+void Player::CheckSpawnRemovalQueue() {
+	if(!GetClient()->IsReadyForUpdates())
+		return;
+
+	spawn_mutex.writelock(__FUNCTION__, __LINE__);
+	map<int32, SpawnRemoval*>::iterator itr;
+	for(itr = spawn_removal_list.begin(); itr != spawn_removal_list.end();) {
+		if(itr->second->spawn_removal_timer.Check()) {
+			map<int32, int8>::iterator sent_itr = spawn_packet_sent.find(itr->first);
+			LogWrite(PLAYER__DEBUG, 0, "Player", "Spawn for Player %s (%u).  Spawn index %u in state %u.", 
+				GetName(), GetCharacterID(), itr->second->index_id, sent_itr->second);
+			switch(sent_itr->second) {
+				case SpawnState::SPAWN_STATE_REMOVING: {
+					if(itr->second->index_id) {
+						PacketStruct* packet = packet = configReader.getStruct("WS_DestroyGhostCmd", GetClient()->GetVersion());
+						packet->setDataByName("spawn_index", itr->second->index_id);
+						packet->setDataByName("delete", 1);	
+						
+						GetClient()->QueuePacket(packet->serialize(), true);
+						safe_delete(packet);
+					}
+					sent_itr->second = SpawnState::SPAWN_STATE_REMOVING_SLEEP;
+					itr++;
+					break;
+				}
+				case SpawnState::SPAWN_STATE_REMOVING_SLEEP: {
+					map<int32, int8>::iterator sent_itr = spawn_packet_sent.find(itr->first);
+					sent_itr->second = SpawnState::SPAWN_STATE_REMOVED;
+					SpawnRemoval* sr = itr->second;
+					itr = spawn_removal_list.erase(itr);
+					safe_delete(sr);
+					break;
+				}
+			}
+		}
+		else
+			itr++;
+	}
+	spawn_mutex.releasewritelock(__FUNCTION__, __LINE__);
 }
 
 bool Player::WasSentSpawn(int32 spawn_id){
-	bool ret;
-	info_mutex.readlock(__FUNCTION__, __LINE__);
-	ret = spawn_info_packet_list.count(spawn_id) == 1;
-	info_mutex.releasereadlock(__FUNCTION__, __LINE__);
+	if(GetID() == spawn_id)
+		return true;
+
+	bool ret = false;
+	spawn_mutex.readlock(__FUNCTION__, __LINE__);
+	map<int32, int8>::iterator itr = spawn_packet_sent.find(spawn_id);
+	if(itr != spawn_packet_sent.end() && itr->second == SpawnState::SPAWN_STATE_SENT) {
+		ret = true;
+	}
+	spawn_mutex.releasereadlock(__FUNCTION__, __LINE__);
+	return ret;
+}
+
+bool Player::IsSendingSpawn(int32 spawn_id){
+	bool ret = false;
+	spawn_mutex.readlock(__FUNCTION__, __LINE__);
+	map<int32, int8>::iterator itr = spawn_packet_sent.find(spawn_id);
+	if(itr != spawn_packet_sent.end() && itr->second == SpawnState::SPAWN_STATE_SENDING) {
+		ret = true;
+	}
+	spawn_mutex.releasereadlock(__FUNCTION__, __LINE__);
+	return ret;
+}
+
+bool Player::IsRemovingSpawn(int32 spawn_id){
+	bool ret = false;
+	spawn_mutex.readlock(__FUNCTION__, __LINE__);
+	map<int32, int8>::iterator itr = spawn_packet_sent.find(spawn_id);
+	if(itr != spawn_packet_sent.end() && 
+		(itr->second == SpawnState::SPAWN_STATE_REMOVING || itr->second == SpawnState::SPAWN_STATE_REMOVING_SLEEP)) {
+		ret = true;
+	}
+	spawn_mutex.releasereadlock(__FUNCTION__, __LINE__);
 	return ret;
 }
 
@@ -4277,10 +4392,15 @@ int16 Player::GetIndexForSpawn(Spawn* spawn) {
 bool Player::WasSpawnRemoved(Spawn* spawn){
 	bool wasRemoved = false;
 
-	index_mutex.readlock(__FUNCTION__, __LINE__);
-	if(player_removed_spawns.count(spawn) > 0)
+	if(IsRemovingSpawn(spawn->GetID()))
+		return false;
+	
+	spawn_mutex.readlock(__FUNCTION__, __LINE__);
+	map<int32, int8>::iterator itr = spawn_packet_sent.find(spawn_id);
+	if(itr != spawn_packet_sent.end() && itr->second == SpawnState::SPAWN_STATE_REMOVED) {
 		wasRemoved = true;
-	index_mutex.releasereadlock(__FUNCTION__, __LINE__);
+	}
+	spawn_mutex.releasereadlock(__FUNCTION__, __LINE__);
 
 	return wasRemoved;
 }
@@ -4289,14 +4409,14 @@ void Player::RemoveSpawn(Spawn* spawn)
 {
 	LogWrite(PLAYER__DEBUG, 3, "Player", "Remove Spawn '%s' (%u)", spawn->GetName(), spawn->GetID());
 
+	SetSpawnSentState(spawn, SpawnState::SPAWN_STATE_REMOVING);
+	
 	info_mutex.writelock(__FUNCTION__, __LINE__);
 	vis_mutex.writelock(__FUNCTION__, __LINE__);
 	pos_mutex.writelock(__FUNCTION__, __LINE__);
 
 	index_mutex.writelock(__FUNCTION__, __LINE__);
 
-	player_removed_spawns[spawn] = 1;
-
 	if (player_spawn_reverse_id_map[spawn] && player_spawn_id_map.count(player_spawn_reverse_id_map[spawn]) > 0)
 		player_spawn_id_map.erase(player_spawn_reverse_id_map[spawn]);
 
@@ -4306,9 +4426,6 @@ void Player::RemoveSpawn(Spawn* spawn)
 	if (player_spawn_id_map.count(spawn->GetID()) && player_spawn_id_map[spawn->GetID()] == spawn)
 		player_spawn_id_map.erase(spawn->GetID());
 
-	if (player_spawn_reverse_id_map.count(spawn))
-		player_spawn_reverse_id_map.erase(spawn);
-
 	int32 id = spawn->GetID();
 	if (spawn_info_packet_list.count(id))
 		spawn_info_packet_list.erase(id);
@@ -5015,27 +5132,12 @@ bool Player::CanReceiveQuest(int32 quest_id){
 	return passed;
 }
 
-void Player::ClearRemovedSpawn(Spawn* spawn){
-	index_mutex.writelock(__FUNCTION__, __LINE__);
-	if(player_removed_spawns.count(spawn) > 0)
-		player_removed_spawns.erase(spawn);
-	index_mutex.releasewritelock(__FUNCTION__, __LINE__);
-}
-
 bool Player::ShouldSendSpawn(Spawn* spawn){
-	// Added a try catch to attempt to prevent a zone crash when an invalid spawn is passed to this function.
-	// Think invalid spawns are coming from the mutex list, if spawn is invalid return false.
-	try
-	{
-		if(spawn->IsDeletedSpawn())
-			return false;
-		else if((WasSentSpawn(spawn->GetID()) == false || NeedsSpawnResent(spawn)) && (!spawn->IsPrivateSpawn() || spawn->AllowedAccess(this)))
-			return true;
-	}
-	catch (...)
-	{
-		LogWrite(SPAWN__ERROR, 0, "Spawn", "Invalid spawn passes to player->ShouldSendSpawn()");
-	}
+	if(spawn->IsDeletedSpawn() || IsSendingSpawn(spawn->GetID()) || IsRemovingSpawn(spawn->GetID()))
+		return false;
+	else if((WasSentSpawn(spawn->GetID()) == false) && (!spawn->IsPrivateSpawn() || spawn->AllowedAccess(this)))
+		return true;
+	
 	return false;
 }
 
@@ -5386,11 +5488,11 @@ PlayerItemList*	Player::GetPlayerItemList(){
 	return &item_list;
 }
 
-void Player::ResetRemovedSpawns(){
-	player_removed_spawns.clear();
-}
 void Player::ResetSavedSpawns(){
-	ResetRemovedSpawns();
+	spawn_mutex.writelock(__FUNCTION__, __LINE__);
+	ClearRemovalTimers();
+	spawn_packet_sent.clear();
+	spawn_mutex.releasewritelock(__FUNCTION__, __LINE__);
 
 	info_mutex.writelock(__FUNCTION__, __LINE__);
 	spawn_info_packet_list.clear();
@@ -6548,8 +6650,11 @@ void Player::RemoveTargetInvisHistory(int32 targetID)
 	target_invis_history.erase(targetID);
 }
 
-void Player::SetSpawnMap(Spawn* spawn)
+bool Player::SetSpawnMap(Spawn* spawn)
 {
+	if(!client->GetPlayer()->SetSpawnSentState(spawn, SpawnState::SPAWN_STATE_SENDING))
+		return false;
+
 	index_mutex.writelock(__FUNCTION__, __LINE__);
 	spawn_id += 1;
 	if (spawn_index == 255)
@@ -6564,6 +6669,7 @@ void Player::SetSpawnMap(Spawn* spawn)
 
 	player_spawn_reverse_id_map.insert(make_pair(spawn,tmp_id));
 	index_mutex.releasewritelock(__FUNCTION__, __LINE__);
+	return true;
 }
 
 NPC* Player::InstantiateSpiritShard(float origX, float origY, float origZ, float origHeading, int32 origGridID, ZoneServer* origZone)

+ 23 - 6
EQ2/source/WorldServer/Player.h

@@ -167,6 +167,14 @@ struct SpellBookEntry{
 	bool visible;
 };
 
+enum SpawnState{
+	SPAWN_STATE_NONE=0,
+	SPAWN_STATE_SENDING=1,
+	SPAWN_STATE_SENT=2,
+	SPAWN_STATE_REMOVING=3,
+	SPAWN_STATE_REMOVING_SLEEP=4,
+	SPAWN_STATE_REMOVED=5
+};
 #define QUICKBAR_NORMAL		1
 #define QUICKBAR_INV_SLOT	2
 #define QUICKBAR_MACRO		3
@@ -203,6 +211,11 @@ struct LoginAppearances {
 	bool	update_needed;
 };
 
+struct SpawnRemoval {
+	Timer spawn_removal_timer;
+	int16 index_id;
+};
+
 class PlayerLoginAppearance {
 public:
 	PlayerLoginAppearance() { appearanceList = new map<int8, LoginAppearances>; }
@@ -418,7 +431,10 @@ public:
 		art_level = new_lvl;
 		}*/
 	bool WasSentSpawn(int32 spawn_id);
-	bool NeedsSpawnResent(Spawn* spawn);
+	bool IsSendingSpawn(int32 spawn_id);
+	bool IsRemovingSpawn(int32 spawn_id);
+	bool SetSpawnSentState(Spawn* spawn, SpawnState state);
+	void CheckSpawnRemovalQueue();
 	void SetSideSpeed(float side_speed, bool updateFlags = true) {
 		SetPos(&appearance.pos.SideSpeed, side_speed, updateFlags);
 	}
@@ -570,6 +586,7 @@ public:
 	void	CalculateLocation();
 	void	SetSpawnDeleteTime(int32 id, int32 time);
 	int32	GetSpawnDeleteTime(int32 id);
+	void	ClearRemovalTimers();
 	void	ClearEverything();
 	bool	IsFullyLoggedIn();
 	void	SetFullyLoggedIn(bool val);
@@ -581,7 +598,6 @@ public:
 	int16	GetIndexForSpawn(Spawn* spawn);
 	bool	WasSpawnRemoved(Spawn* spawn);
 	void	RemoveSpawn(Spawn* spawn);
-	void	ClearRemovedSpawn(Spawn* spawn);
 	bool	ShouldSendSpawn(Spawn* spawn);
 	Client* client = 0;
 	void SetLevel(int16 level, bool setUpdateFlags = true);
@@ -606,7 +622,7 @@ public:
 		return id;
 	}
 
-	void SetSpawnMap(Spawn* spawn);
+	bool SetSpawnMap(Spawn* spawn);
 
 	void SetSpawnMapIndex(Spawn* spawn, int32 index)
 	{
@@ -695,7 +711,6 @@ public:
 	void				SetGroupInformation(PacketStruct* packet);
 
 
-	void				ResetRemovedSpawns();
 	void				ResetSavedSpawns();
 	bool				IsReturningFromLD();
 	void				SetReturningFromLD(bool val);
@@ -925,6 +940,7 @@ public:
 	Mutex pos_mutex;
 	Mutex vis_mutex;
 	Mutex index_mutex;
+	Mutex spawn_mutex;
 
 	void SetTempMount(int32 id) { tmp_mount_model = id; }
 	int32 GetTempMount() { return tmp_mount_model; }
@@ -1010,10 +1026,12 @@ private:
 	map<Spawn*, bool>	current_quest_flagged;
 	PlayerFaction		factions;
 	map<int32, Quest*>	completed_quests;
-	bool				charsheet_changed;
+		bool				charsheet_changed;
 	map<int32, string>	spawn_vis_packet_list;
 	map<int32, string>	spawn_info_packet_list;
 	map<int32, string>	spawn_pos_packet_list;
+	map<int32, int8> spawn_packet_sent;
+	map<int32, SpawnRemoval*> spawn_removal_list;
 	uchar*				movement_packet;
 	uchar*				old_movement_packet;
 	uchar*				spell_orig_packet;
@@ -1117,7 +1135,6 @@ private:
 
 	map<int32, Spawn*>	player_spawn_id_map;
 	map<Spawn*, int32>	player_spawn_reverse_id_map;
-	map<Spawn*, int8>	player_removed_spawns;
 
 	bool all_spells_locked;
 	Timer lift_cooldown;

+ 1 - 0
EQ2/source/WorldServer/PlayerGroups.cpp

@@ -108,6 +108,7 @@ bool PlayerGroup::RemoveMember(Entity* member) {
 	}
 	MGroupMembers.releasewritelock();
 
+	member->SetGroupMemberInfo(nullptr);
 	safe_delete(gmi);
 	if (member->IsBot())
 		((Bot*)member)->Camp();

+ 2 - 1
EQ2/source/WorldServer/Spawn.cpp

@@ -932,7 +932,7 @@ EQ2Packet* Spawn::player_position_update_packet(Player* player, int16 version){
 	ptr += info_size;
 	memcpy(ptr, pos_changes, tmp_pos_packet_size);
 	EQ2Packet* ret_packet = new EQ2Packet(OP_ClientCmdMsg, tmp, size);
-	DumpPacket(ret_packet);
+//	DumpPacket(ret_packet);
 	delete[] tmp;
 	delete[] pos_changes;
 	return ret_packet;
@@ -1037,6 +1037,7 @@ EQ2Packet* Spawn::spawn_update_packet(Player* player, int16 version, bool overri
 	safe_delete_array(vis_changes);
 	safe_delete_array(pos_changes);
 	m_Update.releasewritelock(__FUNCTION__, __LINE__);
+	
 	return ret_packet;
 }
 

+ 43 - 8
EQ2/source/WorldServer/client.cpp

@@ -122,7 +122,7 @@ extern ChestTrapList chest_trap_list;
 
 using namespace std;
 
-Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_debug_timer(30000), delayTimer(500), transmuteID(0), temp_placement_timer(10) {
+Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_debug_timer(30000), delayTimer(500), transmuteID(0), temp_placement_timer(10), spawn_removal_timer(250) {
 	eqs = ieqs;
 	ip = eqs->GetrIP();
 	port = ntohs(eqs->GetrPort());
@@ -208,6 +208,7 @@ Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_deb
 	MItemDetails.SetName("Client::MItemDetails");
 	MSpellDetails.SetName("Client::MSpellDetails");
 	hasSentTempPlacementSpawn = false;
+	spawn_removal_timer.Start();
 }
 
 Client::~Client() {
@@ -693,9 +694,7 @@ void Client::HandlePlayerRevive(int32 point_id)
 void Client::SendCharInfo() {
 	EQ2Packet* app;
 
-	if(IsReloadingZone())
-	{
-		GetPlayer()->ResetRemovedSpawns();
+	if(IsReloadingZone()){
 		SetReloadingZone(false);
 	}
 
@@ -1756,8 +1755,14 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 		memcpy(&index, app->pBuffer, sizeof(int16));
 		if (index == 0xFFFF)
 			GetPlayer()->SetTarget(0);
-		else
-			GetPlayer()->SetTarget(GetPlayer()->GetSpawnByIndex(index));
+		else {
+			Spawn* spawn = GetPlayer()->GetSpawnByIndex(index);
+			if(spawn)
+				GetPlayer()->SetTarget(spawn);
+			else {
+		LogWrite(PLAYER__ERROR, 1, "Player", "Player %s tried to target %u index, but that index was not valid.", GetPlayer()->GetName(), index);
+			}
+		}
 		if (GetPlayer()->GetTarget())
 			GetCurrentZone()->CallSpawnScript(GetPlayer()->GetTarget(), SPAWN_SCRIPT_TARGETED, GetPlayer());
 		//player->SetTarget((int16*)app->pBuffer);
@@ -3009,6 +3014,10 @@ bool Client::Process(bool zone_process) {
 		should_load_spells = false;
 	}
 
+	if(spawn_removal_timer.Check() && GetPlayer()) {
+		GetPlayer()->CheckSpawnRemovalQueue();
+	}
+
 	if (delayedLogin && delayTimer.Enabled() && delayTimer.Check())
 	{
 		if (!HandleNewLogin(delayedAccountID, delayedAccessKey))
@@ -9814,6 +9823,7 @@ bool Client::HandleNewLogin(int32 account_id, int32 access_code)
 				if (client->GetCurrentZone() && !client->IsZoning()) {
 					//swap players, allowing the client to resume his LD'd player (ONLY if same version of the client)
 					if (client->GetVersion() == version) {
+						client->ReplaceGroupClient(this);
 						Player* current_player = GetPlayer();
 						SetPlayer(client->GetPlayer());
 						GetPlayer()->SetClient(this);
@@ -9886,7 +9896,7 @@ void Client::SendSpawnChanges(set<Spawn*>& spawns) {
 
 	for (const auto& spawn : spawns) {
 		int16 index = GetPlayer()->GetIndexForSpawn(spawn);
-		if (index == 0 || !GetPlayer()->WasSentSpawn(spawn->GetID()) || GetPlayer()->NeedsSpawnResent(spawn))
+		if (index == 0 || !GetPlayer()->WasSentSpawn(spawn->GetID()))
 			continue;
 
 		if (spawn->vis_changed)
@@ -10120,7 +10130,7 @@ void Client::SendHailCommand(Spawn* spawn)
 
 void Client::SendDefaultCommand(Spawn* spawn, const char* command, float distance)
 {
-	if (GetPlayer()->WasSentSpawn(spawn->GetID()) && GetPlayer()->WasSpawnRemoved(spawn) == false) {
+	if (GetPlayer()->WasSentSpawn(spawn->GetID())) {
 		PacketStruct* packet = configReader.getStruct("WS_SetDefaultCommand", GetVersion());
 		if (packet) {
 			packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(spawn));
@@ -10409,6 +10419,31 @@ void Client::SendShowBook(Spawn* sender, string title, vector<Item::BookPage*> p
 	safe_delete(packet);
 }
 
+void Client::ReplaceGroupClient(Client* new_client)
+{
+	if (this->GetPlayer()->GetGroupMemberInfo())
+	{
+		world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
+		PlayerGroup* group = world.GetGroupManager()->GetGroup(this->GetPlayer()->GetGroupMemberInfo()->group_id);
+		if(group)
+		{
+			group->MGroupMembers.writelock();
+			rejoin_group_id = this->GetPlayer()->GetGroupMemberInfo()->group_id;
+			this->GetPlayer()->GetGroupMemberInfo()->client = new_client;
+			this->GetPlayer()->GetGroupMemberInfo()->member = GetPlayer();
+			group->MGroupMembers.releasewritelock();
+		}
+		else
+		{
+			this->GetPlayer()->GetGroupMemberInfo()->client = 0;
+			this->GetPlayer()->GetGroupMemberInfo()->member = 0;
+			this->GetPlayer()->SetGroupMemberInfo(0);
+		}
+
+		world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
+	}
+}
+
 void Client::TempRemoveGroup()
 {
 	if (this->GetPlayer()->GetGroupMemberInfo())

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

@@ -453,6 +453,7 @@ public:
 	void SetRejoinGroupID(int32 id) { rejoin_group_id = id; }
 
 	void TempRemoveGroup();
+	void ReplaceGroupClient(Client* new_client);
 
 	void SendWaypoints();
 
@@ -589,6 +590,7 @@ private:
 	Timer	quest_pos_timer;
 	Timer	lua_debug_timer;
 	Timer	temp_placement_timer;
+	Timer	spawn_removal_timer;
 	bool	player_pos_changed;
 	bool HandlePacket(EQApplicationPacket *app);
 	EQStream* eqs;

+ 75 - 75
EQ2/source/WorldServer/zoneserver.cpp

@@ -1076,8 +1076,8 @@ void ZoneServer::CheckSpawnRange(Spawn* spawn){
 	MClientList.releasereadlock(__FUNCTION__, __LINE__);
 }
 
-void ZoneServer::PrepareSpawnID(Player* player, Spawn* spawn){
-	player->SetSpawnMap(spawn);
+bool ZoneServer::PrepareSpawnID(Player* player, Spawn* spawn){
+	return player->SetSpawnMap(spawn);
 }
 
 void ZoneServer::CheckSendSpawnToClient(Client* client, bool initial_login) {
@@ -1108,10 +1108,11 @@ void ZoneServer::CheckSendSpawnToClient(Client* client, bool initial_login) {
 				}
 				if (spawn && spawn != client->GetPlayer() && client->GetPlayer()->ShouldSendSpawn(spawn)) {
 					if ((!initial_login && spawn_iter->second <= SEND_SPAWN_DISTANCE) || (initial_login && (spawn_iter->second <= (SEND_SPAWN_DISTANCE / 2) || spawn->IsWidget()))) {
-						if (closest_spawns.count(spawn_iter->second) == 0)
-							closest_spawns[spawn_iter->second] = new vector<Spawn*>();
-						closest_spawns[spawn_iter->second]->push_back(spawn);
-						PrepareSpawnID(client->GetPlayer(), spawn);
+						if(PrepareSpawnID(client->GetPlayer(), spawn)) {
+							if (closest_spawns.count(spawn_iter->second) == 0)
+								closest_spawns[spawn_iter->second] = new vector<Spawn*>();
+							closest_spawns[spawn_iter->second]->push_back(spawn);
+						}
 					}
 				}
 			}
@@ -1121,7 +1122,6 @@ void ZoneServer::CheckSendSpawnToClient(Client* client, bool initial_login) {
 		for (itr = closest_spawns.begin(); itr != closest_spawns.end(); ) {
 			for (spawn_iter2 = itr->second->begin(); spawn_iter2 != itr->second->end(); spawn_iter2++) {
 				spawn = *spawn_iter2;
-				client->GetPlayer()->ClearRemovedSpawn(spawn);
 				SendSpawn(spawn, client);
 				if (client->ShouldTarget() && client->GetCombineSpawn() == spawn)
 					client->TargetSpawn(spawn);
@@ -1171,6 +1171,7 @@ void ZoneServer::CheckRemoveSpawnFromClient(Spawn* spawn) {
 
 			if(spawn && spawn != client->GetPlayer() && 
 				client->GetPlayer()->WasSentSpawn(spawn->GetID()) && 
+				!client->GetPlayer()->IsRemovingSpawn(spawn->GetID()) && 
 				client->GetPlayer()->WasSpawnRemoved(spawn) == false && 
 				(spawn_range_map.Get(client)->Get(spawn->GetID()) > REMOVE_SPAWN_DISTANCE &&
 					!spawn->IsSign() && !spawn->IsObject() && !spawn->IsWidget() && !spawn->IsTransportSpawn())){
@@ -1768,7 +1769,7 @@ void ZoneServer::SendSpawnChangesByDBID(int32 db_id, Client* client, bool overri
 }
 
 void ZoneServer::SendSpawnChanges(Spawn* spawn, Client* client, bool override_changes, bool override_vis_changes){
-	if(client && client->IsReadyForUpdates() && client->GetPlayer()->WasSentSpawn(spawn->GetID()) && !client->GetPlayer()->WasSpawnRemoved(spawn) && (spawn->IsTransportSpawn() || client->GetPlayer()->GetDistance(spawn) < SEND_SPAWN_DISTANCE)){
+	if(client && client->IsReadyForUpdates() && client->GetPlayer()->WasSentSpawn(spawn->GetID()) && (spawn->IsTransportSpawn() || client->GetPlayer()->GetDistance(spawn) < SEND_SPAWN_DISTANCE)){
 		EQ2Packet* outapp = spawn->spawn_update_packet(client->GetPlayer(), client->GetVersion(), override_changes, override_vis_changes);
 		if(outapp)
 			client->QueuePacket(outapp);
@@ -1942,7 +1943,7 @@ void ZoneServer::SendPlayerPositionChanges(Player* player){
 		MClientList.readlock(__FUNCTION__, __LINE__);
 		for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
 			client = *client_itr;
-			if(player != client->GetPlayer() && client->GetPlayer()->WasSentSpawn(player->GetID()) && client->GetPlayer()->WasSpawnRemoved(player) == false){
+			if(player != client->GetPlayer() && client->GetPlayer()->WasSentSpawn(player->GetID())){
 				EQ2Packet* outapp = player->player_position_update_packet(client->GetPlayer(), client->GetVersion());
 				if(outapp)
 					client->QueuePacket(outapp);
@@ -3076,14 +3077,21 @@ void ZoneServer::RemoveClient(Client* client)
 
 		if(!zoneShuttingDown && !client->IsZoning())
 		{
+			world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
 			GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
+			int32 group_id = 0;
 			if (gmi) {
-				int32 size = world.GetGroupManager()->GetGroupSize(gmi->group_id);
+				group_id = gmi->group_id;
+			}
+			world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
+			if (group_id) {
+				int32 size = world.GetGroupManager()->GetGroupSize(group_id);
 				if (size > 1) {
 					bool send_left_message = size > 2;
-					world.GetGroupManager()->RemoveGroupMember(gmi->group_id, client->GetPlayer());
+					// removegroupmember can delete the gmi, so make sure we still have a group_id after
+					world.GetGroupManager()->RemoveGroupMember(group_id, client->GetPlayer());
 					if (send_left_message)
-						world.GetGroupManager()->GroupMessage(gmi->group_id, "%s has left the group.", client->GetPlayer()->GetName());
+						world.GetGroupManager()->GroupMessage(group_id, "%s has left the group.", client->GetPlayer()->GetName());
 				}
 			}
 			if( (client->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) > 0)
@@ -3140,7 +3148,7 @@ void ZoneServer::RemoveClientImmediately(Client* client) {
 		{
 			if (!client->IsZoning() && (guild = client->GetPlayer()->GetGuild()))
 				guild->GuildMemberLogoff(client->GetPlayer());
-
+			
 			MClientList.writelock(__FUNCTION__, __LINE__);
 			std::vector<Client*>::iterator itr = find(clients.begin(), clients.end(), client);
 			if (itr != clients.end())
@@ -3259,7 +3267,7 @@ void ZoneServer::HandleChatMessage(Client* client, Spawn* from, const char* to,
 			if (client->GetPlayer() != from)
 				packet->setMediumStringByName("to", client->GetPlayer()->GetName());
 			packet->setDataByName("channel", client->GetMessageChannelColor(channel));
-			if (from && ((from == client->GetPlayer()) || (client->GetPlayer()->WasSentSpawn(from->GetID()) && !client->GetPlayer()->WasSpawnRemoved(from))))
+			if (from && ((from == client->GetPlayer()) || (client->GetPlayer()->WasSentSpawn(from->GetID()))))
 				packet->setDataByName("from_spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(from));
 			else
 				packet->setDataByName("from_spawn_id", 0xFFFFFFFF);
@@ -3371,9 +3379,17 @@ void ZoneServer::UpdateVitality(float amount){
 void ZoneServer::SendSpawn(Spawn* spawn, Client* client){
 	EQ2Packet* outapp = spawn->serialize(client->GetPlayer(), client->GetVersion());
 
-	LogWrite(ZONE__DEBUG, 7, "Zone", "%s: Processing SendSpawn for spawn index %u (%s)...", client->GetPlayer()->GetName(), client->GetPlayer()->GetIndexForSpawn(spawn), spawn->GetName());
-	if(outapp)
-		client->QueuePacket(outapp);
+	if(!client->GetPlayer()->IsSendingSpawn(spawn->GetID())) {
+		safe_delete(outapp);
+	}
+	else {
+		LogWrite(ZONE__DEBUG, 7, "Zone", "%s: Processing SendSpawn for spawn index %u (%s)...", client->GetPlayer()->GetName(), client->GetPlayer()->GetIndexForSpawn(spawn), spawn->GetName());
+		if(outapp)
+			client->QueuePacket(outapp);
+
+		client->GetPlayer()->SetSpawnSentState(spawn, SpawnState::SPAWN_STATE_SENT);
+	}
+
 	/*
 	vis flags:
 	2 = show icon
@@ -3516,7 +3532,7 @@ void ZoneServer::PlayFlavor(Spawn* spawn, const char* mp3, const char* text, con
 	MClientList.readlock(__FUNCTION__, __LINE__);
 	for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
 		client = *client_itr;
-		if(!client || !client->IsConnected() || !client->GetPlayer()->WasSentSpawn(spawn->GetID()) || client->GetPlayer()->WasSpawnRemoved(spawn) || client->GetPlayer()->GetDistance(spawn) > 30)
+		if(!client || !client->IsConnected() || !client->GetPlayer()->WasSentSpawn(spawn->GetID()) || client->GetPlayer()->GetDistance(spawn) > 30)
 			continue;
 		PlayFlavor(client, spawn, mp3, text, emote, key1, key2, language);
 	}
@@ -3533,7 +3549,7 @@ void ZoneServer::PlayVoice(Spawn* spawn, const char* mp3, int32 key1, int32 key2
 	MClientList.readlock(__FUNCTION__, __LINE__);
 	for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
 		client = *client_itr;
-		if(!client || !client->IsConnected() || !client->GetPlayer()->WasSentSpawn(spawn->GetID()) || client->GetPlayer()->WasSpawnRemoved(spawn))
+		if(!client || !client->IsConnected() || !client->GetPlayer()->WasSentSpawn(spawn->GetID()))
 			continue;
 		PlayVoice(client, spawn, mp3, key1, key2);
 	}
@@ -3720,11 +3736,9 @@ bool ZoneServer::SendRemoveSpawn(Client* client, Spawn* spawn, PacketStruct* pac
 	if(!client || !spawn)
 		return false;
 
-	int16 index = client->GetPlayer()->GetIndexForSpawn(spawn);
-	int32 cur_id = client->GetPlayer()->GetIDWithPlayerSpawn(spawn);
-	bool wasRemoved = client->GetPlayer()->WasSpawnRemoved(spawn);
-	LogWrite(ZONE__DEBUG, 7, "Zone", "%s: Processing SendRemoveSpawn for spawn index %u (%s)...cur_id: %i, wasremoved:: %i", client->GetPlayer()->GetName(), index, spawn->GetName(), cur_id, wasRemoved);
-	if(packet && index > 0 && !wasRemoved)
+	spawn->RemoveSpawnFromPlayer(client->GetPlayer());
+//	LogWrite(ZONE__DEBUG, 7, "Zone", "%s: Processing SendRemoveSpawn for spawn index %u (%s)...cur_id: %i, wasremoved:: %i", client->GetPlayer()->GetName(), index, spawn->GetName(), cur_id, wasRemoved);
+/*	if(packet && index > 0 && !wasRemoved)
 	{
 		packet->ResetData();
 
@@ -3737,7 +3751,8 @@ bool ZoneServer::SendRemoveSpawn(Client* client, Spawn* spawn, PacketStruct* pac
 		client->QueuePacket(packet->serialize());
 		return true;
 	}
-	return false;
+	return false;*/
+	return true;
 }
 
 void ZoneServer::SetSpawnCommand(Spawn* spawn, int8 type, char* value, Client* client){
@@ -3932,6 +3947,29 @@ void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool
 {
 	LogWrite(ZONE__DEBUG, 3, "Zone", "Processing RemoveSpawn function for %s (%i)...", spawn->GetName(),spawn->GetID());
 
+	PacketStruct* packet = 0;
+	int16 packet_version = 0;
+	Client* client = 0;
+
+	vector<Client*>::iterator client_itr;
+
+	MClientList.readlock(__FUNCTION__, __LINE__);
+	for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
+		client = *client_itr;
+
+		if (client && (client->GetVersion() > 283 || !client->IsZoning() || client->GetPlayer() != spawn)) { //don't send destroy ghost of 283 client when zoning
+			if (client->GetPlayer()->HasTarget() && client->GetPlayer()->GetTarget() == spawn)
+				client->GetPlayer()->SetTarget(0);
+			if(client->GetPlayer()->WasSentSpawn(spawn->GetID()) || client->GetPlayer()->IsSendingSpawn(spawn->GetID()))
+				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__);
+
+	safe_delete(packet);
+
 	spawn->RemoveSpawnProximities();
 	RemoveSpawnProximities(spawn);
 
@@ -3960,35 +3998,6 @@ void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool
 	if(erase_from_spawn_list)
 		AddPendingSpawnRemove(spawn->GetID());
 
-	PacketStruct* packet = 0;
-	int16 packet_version = 0;
-	Client* client = 0;
-
-	vector<Client*>::iterator client_itr;
-
-	MClientList.readlock(__FUNCTION__, __LINE__);
-	for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
-		client = *client_itr;
-
-		if (client && (client->GetVersion() > 283 || !client->IsZoning() || client->GetPlayer() != spawn)) { //don't send destroy ghost of 283 client when zoning
-			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__);
-
-	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());
@@ -4084,7 +4093,7 @@ void ZoneServer::SendQuestUpdates(Client* client, Spawn* spawn){
 		return;
 
 	if(spawn){
-		if(client->GetPlayer()->WasSentSpawn(spawn->GetID()) && !client->GetPlayer()->WasSpawnRemoved(spawn))
+		if(client->GetPlayer()->WasSentSpawn(spawn->GetID()))
 			SendSpawnChanges(spawn, client, false, true);
 	}
 	else{
@@ -4098,7 +4107,7 @@ void ZoneServer::SendAllSpawnsForLevelChange(Client* client) {
 		MutexMap<int32, float >::iterator itr = spawn_range_map.Get(client)->begin();
 		while (itr.Next()) {
 			spawn = GetSpawnByID(itr->first);
-			if (spawn && client->GetPlayer()->WasSentSpawn(spawn->GetID()) && !client->GetPlayer()->WasSpawnRemoved(spawn)) {
+			if (spawn && client->GetPlayer()->WasSentSpawn(spawn->GetID())) {
 				SendSpawnChanges(spawn, client, false, true);
 				// Attempt to slow down the packet spam sent to the client
 				// who the bloody fuck put a Sleep here
@@ -4115,7 +4124,7 @@ void ZoneServer::SendAllSpawnsForSeeInvisChange(Client* client) {
 		MutexMap<int32, float >::iterator itr = spawn_range_map.Get(client)->begin();
 		while (itr.Next()) {
 			spawn = GetSpawnByID(itr->first);
-			if (spawn && spawn->IsEntity() && (((Entity*)spawn)->IsInvis() || ((Entity*)spawn)->IsStealthed()) && client->GetPlayer()->WasSentSpawn(spawn->GetID()) && !client->GetPlayer()->WasSpawnRemoved(spawn)) {
+			if (spawn && spawn->IsEntity() && (((Entity*)spawn)->IsInvis() || ((Entity*)spawn)->IsStealthed()) && client->GetPlayer()->WasSentSpawn(spawn->GetID())) {
 				SendSpawnChanges(spawn, client, true, true);
 			}
 		}
@@ -4129,7 +4138,7 @@ void ZoneServer::SendAllSpawnsForVisChange(Client* client, bool limitToEntities)
 		MutexMap<int32, float >::iterator itr = spawn_range_map.Get(client)->begin();
 		while (itr.Next()) {
 			spawn = GetSpawnByID(itr->first);
-			if (spawn && (!limitToEntities || (limitToEntities && spawn->IsEntity())) && client->GetPlayer()->WasSentSpawn(spawn->GetID()) && !client->GetPlayer()->WasSpawnRemoved(spawn)) {
+			if (spawn && (!limitToEntities || (limitToEntities && spawn->IsEntity())) && client->GetPlayer()->WasSentSpawn(spawn->GetID())) {
 				SendSpawnChanges(spawn, client, false, true);
 			}
 		}
@@ -4527,8 +4536,6 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
 			client = *client_itr;
 			if(!client->GetPlayer()->WasSentSpawn(victim_id) || (attacker_id != 0xFFFFFFFF && !client->GetPlayer()->WasSentSpawn(attacker_id)) )
 				continue;
-			else if(client->GetPlayer()->WasSpawnRemoved(dead) || (attacker_id != 0xFFFFFFFF && client->GetPlayer()->WasSpawnRemoved(killer)))
-				continue;
 			else if(killer && killer->GetDistance(client->GetPlayer()) > HEAR_SPAWN_DISTANCE)
 				continue;
 
@@ -4609,8 +4616,6 @@ void ZoneServer::SendDamagePacket(Spawn* attacker, Spawn* victim, int8 type1, in
 		client = *client_itr;
 		if (!client || (client->GetPlayer() != attacker && client->GetPlayer() != victim && ((attacker && client->GetPlayer()->WasSentSpawn(attacker->GetID()) == false) || (victim && client->GetPlayer()->WasSentSpawn(victim->GetID()) == false))))
 			continue;
-		if ((attacker && client->GetPlayer()->WasSpawnRemoved(attacker)) || (victim && client->GetPlayer()->WasSpawnRemoved(victim)))
-			continue;
 		if (attacker && attacker->GetDistance(client->GetPlayer()) > 50)
 			continue;
 		if (victim && victim->GetDistance(client->GetPlayer()) > 50)
@@ -4725,8 +4730,6 @@ void ZoneServer::SendHealPacket(Spawn* caster, Spawn* target, int16 heal_type, i
 		client = *client_itr;
 		if(!client || (client->GetPlayer() != caster && ((caster && client->GetPlayer()->WasSentSpawn(caster->GetID()) == false) || (target && client->GetPlayer()->WasSentSpawn(target->GetID()) == false))))
 			continue;
-		if((caster && client->GetPlayer()->WasSpawnRemoved(caster)) || (caster && client->GetPlayer()->WasSpawnRemoved(target)))
-			continue;
 		if(caster && caster->GetDistance(client->GetPlayer()) > 50)
 			continue;
 		if(target && target->GetDistance(client->GetPlayer()) > 50)
@@ -4759,8 +4762,6 @@ void ZoneServer::SendThreatPacket(Spawn* caster, Spawn* target, int32 threat_amt
 		client = *client_itr;
 		if(!client || (client->GetPlayer() != caster && ((caster && client->GetPlayer()->WasSentSpawn(caster->GetID()) == false) || (target && client->GetPlayer()->WasSentSpawn(target->GetID()) == false))))
 			continue;
-		if((caster && client->GetPlayer()->WasSpawnRemoved(caster)) || (caster && client->GetPlayer()->WasSpawnRemoved(target)))
-			continue;
 		if(caster && caster->GetDistance(client->GetPlayer()) > 50)
 			continue;
 		if(target && target->GetDistance(client->GetPlayer()) > 50)
@@ -4808,7 +4809,7 @@ void ZoneServer::SendInterruptPacket(Spawn* interrupted, LuaSpell* spell, bool f
 	MClientList.readlock(__FUNCTION__, __LINE__);
 	for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
 		client = *client_itr;
-		if(!client || !client->GetPlayer()->WasSentSpawn(interrupted->GetID()) || client->GetPlayer()->WasSpawnRemoved(interrupted))
+		if(!client || !client->GetPlayer()->WasSentSpawn(interrupted->GetID()))
 			continue;
 		packet = configReader.getStruct(fizzle ? "WS_SpellFizzle" : "WS_Interrupt", client->GetVersion());
 		if(packet){
@@ -4908,7 +4909,7 @@ void ZoneServer::SendCastEntityCommandPacket(EntityCommand* entity_command, int3
 		MClientList.readlock(__FUNCTION__, __LINE__);
 		for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
 			client = *client_itr;
-			if (!client || !client->GetPlayer()->WasSentSpawn(spawn_id) || client->GetPlayer()->WasSpawnRemoved(spawn) || !client->GetPlayer()->WasSentSpawn(target_id) || client->GetPlayer()->WasSpawnRemoved(target))
+			if (!client || !client->GetPlayer()->WasSentSpawn(spawn_id) || !client->GetPlayer()->WasSentSpawn(target_id))
 				continue;
 			PacketStruct* packet = configReader.getStruct("WS_HearCastSpell", client->GetVersion());
 			if (packet) {
@@ -4970,7 +4971,8 @@ void ZoneServer::SendZoneSpawns(Client* client){
 		if (spawn) {
 			if(spawn == client->GetPlayer() && (client->IsReloadingZone() || client->GetPlayer()->IsReturningFromLD()))
 			{
-				client->GetPlayer()->SetSpawnMap(spawn);
+				if(!client->GetPlayer()->SetSpawnMap(spawn))
+					continue;
 			}
 			
 			CheckSpawnRange(client, spawn, true);
@@ -6564,7 +6566,7 @@ void ZoneServer::HidePrivateSpawn(Spawn* spawn) {
 	while (itr->Next()) {
 		client = itr->value;
 		player = client->GetPlayer();
-		if (player->WasSentSpawn(spawn->GetID()) && !player->WasSpawnRemoved(spawn)) {
+		if (player->WasSentSpawn(spawn->GetID())) {
 			if (!packet || packet_version != client->GetVersion()) {
 				safe_delete(packet);
 				packet_version = client->GetVersion();
@@ -6813,8 +6815,6 @@ void ZoneServer::SendDispellPacket(Entity* caster, Spawn* target, string dispell
 		client = *client_itr;
 		if(!client || !(player = client->GetPlayer()) || (player != caster && ((caster && player->WasSentSpawn(caster->GetID()) == false) || (target && player->WasSentSpawn(target->GetID()) == false))))
 			continue;
-		if((caster && player->WasSpawnRemoved(caster)) || (caster && player->WasSpawnRemoved(target)))
-			continue;
 		if(caster && caster->GetDistance(player) > 50)
 			continue;
 		if(target && target->GetDistance(player) > 50)
@@ -7683,7 +7683,7 @@ void ZoneServer::SendStateCommand(Spawn* spawn, int32 state) {
 	MClientList.readlock(__FUNCTION__, __LINE__);
 	for (itr = clients.begin(); itr != clients.end(); itr++) {
 		Client* client = *itr;
-		if (client && client->GetPlayer()->WasSentSpawn(spawn->GetID()) && !client->GetPlayer()->WasSpawnRemoved(spawn))
+		if (client && client->GetPlayer()->WasSentSpawn(spawn->GetID()))
 			ClientPacketFunctions::SendStateCommand(client, client->GetPlayer()->GetIDWithPlayerSpawn(spawn), state);
 	}
 	MClientList.releasereadlock(__FUNCTION__, __LINE__);
@@ -8087,7 +8087,7 @@ void ZoneServer::ProcessQueuedStateCommands() // in a client list lock only
 			MClientList.readlock(__FUNCTION__, __LINE__);
 			for (itr = clients.begin(); itr != clients.end(); itr++) {
 				Client* client = *itr;
-				if (client && client->GetPlayer()->WasSentSpawn(spawn->GetID()) && !client->GetPlayer()->WasSpawnRemoved(spawn))
+				if (client && client->GetPlayer()->WasSentSpawn(spawn->GetID()))
 					ClientPacketFunctions::SendStateCommand(client, client->GetPlayer()->GetIDWithPlayerSpawn(spawn), statecmds->second);
 			}
 			MClientList.releasereadlock(__FUNCTION__, __LINE__);
@@ -8110,7 +8110,7 @@ void ZoneServer::ProcessQueuedStateCommands() // in a client list lock only
 				MClientList.readlock(__FUNCTION__, __LINE__);
 				for (itr = clients.begin(); itr != clients.end(); itr++) {
 					Client* client = *itr;
-					if (client && client->GetPlayer()->WasSentSpawn(spawn->GetID()) && !client->GetPlayer()->WasSpawnRemoved(spawn))
+					if (client && client->GetPlayer()->WasSentSpawn(spawn->GetID()))
 						client->SendDefaultCommand(spawn, innermap->first.c_str(), innermap->second);
 				}
 				MClientList.releasereadlock(__FUNCTION__, __LINE__);