Browse Source

better mutex handling for spell process, grid now has broken down spawns, KillSpawnByDistance and SetSpawnByDistance is now limited to its grid, future prospect of cross grid support

Emagi 1 year ago
parent
commit
cd17261b83

+ 5 - 1
EQ2/source/WorldServer/Bots/BotCommands.cpp

@@ -198,7 +198,11 @@ void Commands::Command_Bot(Client* client, Seperator* sep) {
 								continue;
 							
 							if (member->IsBot() && ((Bot*)member)->GetOwner() == player) {
-								member->appearance.pos.grid_id = player->appearance.pos.grid_id;
+								if(member->GetZone() && member->appearance.pos.grid_id != player->appearance.pos.grid_id) {
+									member->GetZone()->RemoveSpawnFromGrid(member, member->appearance.pos.grid_id);
+									member->appearance.pos.grid_id = player->appearance.pos.grid_id;
+									member->GetZone()->AddSpawnToGrid(member, member->appearance.pos.grid_id);
+								}
 								member->SetX(player->GetX());
 								member->SetY(player->GetY());
 								member->SetZ(player->GetZ());

+ 28 - 9
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -3350,16 +3350,21 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 
 			if (sep && sep->arg[0] && strlen(sep->arg[0]) > 1) {
 				Client* new_leader = zone_list.GetClientByCharName(sep->arg[0]);
-				if (new_leader) {
+				if (new_leader && new_leader->GetPlayer()->GetGroupMemberInfo() && new_leader->GetPlayer()->GetGroupMemberInfo()->group_id == client->GetPlayer()->GetGroupMemberInfo()->group_id) {
 					if (client->GetPlayer()->IsGroupMember(new_leader->GetPlayer())) {
-						world.GetGroupManager()->MakeLeader(client->GetPlayer()->GetGroupMemberInfo()->group_id, new_leader->GetPlayer());
+						if(world.GetGroupManager()->MakeLeader(client->GetPlayer()->GetGroupMemberInfo()->group_id, new_leader->GetPlayer())) {
+							world.GetGroupManager()->GroupMessage(client->GetPlayer()->GetGroupMemberInfo()->group_id, "%s is now leader of the group.", new_leader->GetPlayer()->GetName());
+						}
 					}
 				}
 				else
 					client->SimpleMessage(CHANNEL_COMMANDS, "Unable to find the given player.");
 			}
-			else if (cmdTarget && cmdTarget->IsPlayer())
-				world.GetGroupManager()->MakeLeader(client->GetPlayer()->GetGroupMemberInfo()->group_id, (Entity*)cmdTarget);
+			else if (cmdTarget && cmdTarget->IsPlayer() && ((Player*)cmdTarget)->GetGroupMemberInfo() && ((Player*)cmdTarget)->GetGroupMemberInfo()->group_id == client->GetPlayer()->GetGroupMemberInfo()->group_id) {
+				if(client->GetPlayer()->IsGroupMember((Player*)cmdTarget) && world.GetGroupManager()->MakeLeader(client->GetPlayer()->GetGroupMemberInfo()->group_id, (Entity*)cmdTarget)) {
+					world.GetGroupManager()->GroupMessage(client->GetPlayer()->GetGroupMemberInfo()->group_id, "%s is now leader of the group.", cmdTarget->GetName());
+				}
+			}
 			else
 				client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to change group leader.");
 
@@ -5491,7 +5496,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 		case COMMAND_LOCATION_DELETE	: { Command_LocationDelete(client, sep); break; }
 		case COMMAND_LOCATION_LIST		: { Command_LocationList(client, sep); break; }
 		case COMMAND_LOCATION_REMOVE	: { Command_LocationRemove(client, sep); break; }
-		case COMMAND_GRID				: { Command_Grid(client); break; }
+		case COMMAND_GRID				: { Command_Grid(client, sep); break; }
 		case COMMAND_TRY_ON				: { Command_TryOn(client, sep); break; }
 		case COMMAND_RANDOMIZE			: { Command_Randomize(client, sep); break; }
 		case COMMAND_AFK				: { Command_AFK(client, sep); break; }
@@ -6114,18 +6119,32 @@ void Commands::Command_StopFollow(Client* client, Seperator* sep)
 	Dev		: Scatman
 	Example	: /grid
 */ 
-void Commands::Command_Grid(Client* client)
+void Commands::Command_Grid(Client* client, Seperator* sep)
 {
-	client->Message(CHANNEL_COLOR_YELLOW, "Your Grid ID is %u", client->GetPlayer()->appearance.pos.grid_id);
-
 	if (client->GetPlayer()->GetMap() != nullptr) {
+		if(sep && sep->arg[0][0] && strncasecmp("spawns", sep->arg[0], 6) == 0) {
+			int32 grid = client->GetPlayer()->GetLocation();
+				
+			if(sep->IsNumber(1))
+				grid = atoul(sep->arg[1]);
+			std::vector<Spawn*> spawns = client->GetPlayer()->GetZone()->GetSpawnsInGrid(grid);
+			client->Message(CHANNEL_COLOR_RED, "Grid ID %u has %u spawns.", grid, spawns.size());
+			for(int i=0;i<spawns.size();i++) {
+				Spawn* spawn = spawns.at(i);
+				client->Message(CHANNEL_COLOR_YELLOW, "Spawn %s (%u), Loc X/Y/Z: %f/%f/%f.", spawn->GetName(), spawn->GetID(), spawn->GetX(), spawn->GetY(), spawn->GetZ());
+			}
+		}
+		else {
+			client->Message(CHANNEL_COLOR_YELLOW, "Your Grid ID is %u", client->GetPlayer()->appearance.pos.grid_id);
 			auto loc = glm::vec3(client->GetPlayer()->GetX(), client->GetPlayer()->GetZ(), client->GetPlayer()->GetY());
 			uint32 GridID = 0;
 			uint32 WidgetID = 0;
 			float new_z = client->GetPlayer()->GetMap()->FindBestZ(loc, nullptr, &GridID, &WidgetID);
 			float minY = client->GetPlayer()->GetMap()->GetMinY();
 			float maxY = client->GetPlayer()->GetMap()->GetMaxY();
-			client->Message(CHANNEL_COLOR_YELLOW, "Grid result is %u, at EQ2 Y coordinate %f.  Min/Max Y %f/%f.  Widget ID: %u", GridID, new_z, minY, maxY, WidgetID);
+			int32 grid_spawn_count = client->GetPlayer()->GetZone()->GetSpawnCountInGrid(GridID);
+			client->Message(CHANNEL_COLOR_YELLOW, "Grid result is %u, at EQ2 Y coordinate %f.  Spawns on grid: %u.  Min/Max Y %f/%f.  Widget ID: %u", GridID, new_z, grid_spawn_count, minY, maxY, WidgetID);
+		}
 	}
 }
 

+ 1 - 1
EQ2/source/WorldServer/Commands/Commands.h

@@ -309,7 +309,7 @@ public:
 	void Command_EntityCommand(Client* client, Seperator* sep, int handler);
 	void Command_Follow(Client* client, Seperator* sep);
 	void Command_StopFollow(Client* client, Seperator* sep);
-	void Command_Grid(Client* client);
+	void Command_Grid(Client* client, Seperator* sep);
 	void Command_Guild(Client* client, Seperator* sep);
 	void Command_CreateGuild(Client* client);
 	void Command_SetGuildOfficerNote(Client* client, Seperator* sep);

+ 1 - 1
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -10313,7 +10313,7 @@ int EQ2Emu_lua_SetGridID(lua_State* state) {
 		return 0;
 	}
 
-	spawn->SetPos(&(spawn->appearance.pos.grid_id), grid);
+	spawn->SetLocation(grid);
 	return 0;
 }
 

+ 9 - 4
EQ2/source/WorldServer/Player.cpp

@@ -3415,12 +3415,17 @@ void Player::PrepareIncomingMovementPacket(int32 len, uchar* data, int16 version
 		LogWrite(PLAYER__DEBUG, 0, "Player", "%s left grid %u and entered grid %u", appearance.name, appearance.pos.grid_id, grid_id);
 		const char* zone_script = world.GetZoneScript(GetZone()->GetZoneID());
 
-		if (zone_script && lua_interface)
-		{
+		if (zone_script && lua_interface) {
+			lua_interface->RunZoneScript(zone_script, "leave_location", GetZone(), this, GetLocation());
+		}
+		
+			GetZone()->RemoveSpawnFromGrid(this, GetLocation());
+			GetZone()->AddSpawnToGrid(this, grid_id);
+			
+		if (zone_script && lua_interface) {
 			lua_interface->RunZoneScript(zone_script, "enter_location", GetZone(), this, grid_id);
-			lua_interface->RunZoneScript(zone_script, "leave_location", GetZone(), this, appearance.pos.grid_id);
 		}
-
+		
 		appearance.pos.grid_id = grid_id;
 	}
 	if (activity == UPDATE_ACTIVITY_IN_WATER_ABOVE || activity == UPDATE_ACTIVITY_IN_WATER_BELOW) {

+ 9 - 3
EQ2/source/WorldServer/PlayerGroups.cpp

@@ -174,7 +174,7 @@ void PlayerGroup::GroupChatMessage(Spawn* from, int32 language, const char* mess
 	MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
 }
 
-void PlayerGroup::MakeLeader(Entity* new_leader) {
+bool PlayerGroup::MakeLeader(Entity* new_leader) {
 	deque<GroupMemberInfo*>::iterator itr;
 	MGroupMembers.readlock(__FUNCTION__, __LINE__);
 	for (itr = m_members.begin(); itr != m_members.end(); itr++) {
@@ -188,6 +188,8 @@ void PlayerGroup::MakeLeader(Entity* new_leader) {
 
 	new_leader->GetGroupMemberInfo()->leader = true;
 	SendGroupUpdate();
+	
+	return true;
 }
 
 
@@ -377,6 +379,8 @@ int8 PlayerGroupManager::AcceptInvite(Entity* member) {
 
 			// Remove from invite list and add to the group
 			m_pendingInvites.erase(member->GetName());
+			
+			GroupMessage(client_leader->GetPlayer()->GetGroupMemberInfo()->group_id, "%s has joined the group.", member->GetName());
 			AddGroupMember(client_leader->GetPlayer()->GetGroupMemberInfo()->group_id, member);
 			ret = 0; // success
 		}
@@ -585,11 +589,13 @@ void PlayerGroupManager::GroupChatMessage(int32 group_id, Spawn* from, int32 lan
 		m_groups[group_id]->GroupChatMessage(from, language, message);
 }
 
-void PlayerGroupManager::MakeLeader(int32 group_id, Entity* new_leader) {
+bool PlayerGroupManager::MakeLeader(int32 group_id, Entity* new_leader) {
 	std::shared_lock lock(MGroups);
 
 	if (m_groups.count(group_id) > 0)
-		m_groups[group_id]->MakeLeader(new_leader);
+		return m_groups[group_id]->MakeLeader(new_leader);
+	
+	return false;
 }
 
 void PlayerGroupManager::UpdateGroupBuffs() {

+ 2 - 2
EQ2/source/WorldServer/PlayerGroups.h

@@ -84,7 +84,7 @@ public:
 
 	void SimpleGroupMessage(const char* message);
 	void GroupChatMessage(Spawn* from, int32 language, const char* message);
-	void MakeLeader(Entity* new_leader);
+	bool MakeLeader(Entity* new_leader);
 
 	void RemoveClientReference(Client* remove);
 	void UpdateGroupMemberInfo(Entity* ent, bool groupMembersLocked=false);
@@ -175,7 +175,7 @@ public:
 	void SimpleGroupMessage(int32 group_id, const char* message);
 	void GroupMessage(int32 group_id, const char* message, ...);
 	void GroupChatMessage(int32 group_id, Spawn* from, int32 language, const char* message);
-	void MakeLeader(int32 group_id, Entity* new_leader);
+	bool MakeLeader(int32 group_id, Entity* new_leader);
 	void UpdateGroupBuffs();
 
 	bool IsInGroup(int32 group_id, Entity* member);

+ 21 - 5
EQ2/source/WorldServer/Spawn.cpp

@@ -3383,7 +3383,7 @@ bool Spawn::CalculateChange(){
 		}
 
 		if ((!IsFlyingCreature() || IsTransportSpawn()) && newGrid != 0 && newGrid != appearance.pos.grid_id)
-			SetPos(&(appearance.pos.grid_id), newGrid);
+			SetLocation(newGrid);
 	}
 	return remove_needed;
 }
@@ -3948,12 +3948,18 @@ void Spawn::FixZ(bool forceUpdate) {
 			
 			const char* zone_script = world.GetZoneScript(GetZone()->GetZoneID());
 
-			if (zone_script && lua_interface)
-			{
+			if (zone_script && lua_interface) {
+				lua_interface->RunZoneScript(zone_script, "leave_location", GetZone(), this, GetLocation());
+			}
+			
+				GetZone()->RemoveSpawnFromGrid(this, GetLocation());
+				GetZone()->AddSpawnToGrid(this, GridID);
+				
+			if (zone_script && lua_interface) {
 				lua_interface->RunZoneScript(zone_script, "enter_location", GetZone(), this, GridID);
-				lua_interface->RunZoneScript(zone_script, "leave_location", GetZone(), this, appearance.pos.grid_id);
 			}
-			SetPos(&(appearance.pos.grid_id), GridID);
+			
+			SetPos(&appearance.pos.grid_id, GridID);
 		}
 		trigger_widget_id = WidgetID;
 	}
@@ -4492,4 +4498,14 @@ bool Spawn::HasRegionTracked(Region_Node* node, ZBSP_Node* bsp_root, bool in_reg
 	}
 	
 	return false;
+}
+
+
+void Spawn::SetLocation(int32 id, bool setUpdateFlags)
+{
+	if(GetZone()) {
+		GetZone()->RemoveSpawnFromGrid(this, appearance.pos.grid_id);
+		GetZone()->AddSpawnToGrid(this, id);
+	}
+	SetPos(&appearance.pos.grid_id, id, setUpdateFlags);
 }

+ 1 - 3
EQ2/source/WorldServer/Spawn.h

@@ -506,9 +506,7 @@ public:
 	void SetAttackable(int8 new_val, bool setUpdateFlags = true){
 		SetInfo(&appearance.attackable, new_val, setUpdateFlags);
 	}
-	void SetLocation(int32 id, bool setUpdateFlags = true){
-		SetPos(&appearance.pos.grid_id, id, setUpdateFlags);
-	}
+	void SetLocation(int32 id, bool setUpdateFlags = true);
 	void SetRace(int8 race, bool setUpdateFlags = true){
 		SetInfo(&appearance.race, race, setUpdateFlags);
 	}

+ 5 - 8
EQ2/source/WorldServer/SpellProcess.cpp

@@ -33,7 +33,6 @@ extern RuleManager rule_manager;
 
 SpellProcess::SpellProcess(){
 	last_checked_time = 0;
-	MSpellProcess.SetName("SpellProcess::MSpellProcess");
 	MRemoveTargetList.SetName("SpellProcess::MRemoveTargetList");
 	MSoloHO.SetName("SpellProcess::m_soloHO");
 	MGroupHO.SetName("SpellProcess:m_groupHO");
@@ -45,7 +44,7 @@ SpellProcess::~SpellProcess(){
 }
 
 void SpellProcess::RemoveAllSpells(){
-	MSpellProcess.lock();	
+    std::unique_lock lock(MSpellProcess);	
 	ClearSpellScriptTimerList();
 
 	MutexList<LuaSpell*>::iterator active_spells_itr = active_spells.begin();
@@ -105,15 +104,13 @@ void SpellProcess::RemoveAllSpells(){
 	MSpellCancelList.writelock(__FUNCTION__, __LINE__);
 	SpellCancelList.clear();
 	MSpellCancelList.releasewritelock(__FUNCTION__, __LINE__);
-
-	MSpellProcess.unlock();
 }
 
 void SpellProcess::Process(){
 	if(last_checked_time > Timer::GetCurrentTime2())
 		return;
 	last_checked_time = Timer::GetCurrentTime2() + 50;
-	MSpellProcess.lock();
+	MSpellProcess.lock_shared();
 	CheckSpellScriptTimers();
 	if(active_spells.size(true) > 0){		
 		LuaSpell* spell = 0;
@@ -279,7 +276,7 @@ void SpellProcess::Process(){
 	}
 	MGroupHO.releasewritelock(__FUNCTION__, __LINE__);
 
-	MSpellProcess.unlock();
+	MSpellProcess.unlock_shared();
 }
 bool SpellProcess::IsReady(Spell* spell, Entity* caster){
 	if(caster->IsCasting()) {
@@ -1946,7 +1943,7 @@ void SpellProcess::Interrupted(Entity* caster, Spawn* interruptor, int16 error_c
 
 void SpellProcess::RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, bool delete_recast, bool call_expire_function, bool lock_spell_process){
 	if(lock_spell_process)
-		MSpellProcess.lock();
+		MSpellProcess.lock_shared();
 
 	int32 i = 0;
 	if(cast_timers.size() > 0){		
@@ -2012,7 +2009,7 @@ void SpellProcess::RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, boo
 	}
 	
 	if(lock_spell_process)
-		MSpellProcess.unlock();
+		MSpellProcess.unlock_shared();
 }
 
 void SpellProcess::GetSpellTargets(LuaSpell* luaspell) 

+ 4 - 1
EQ2/source/WorldServer/SpellProcess.h

@@ -19,6 +19,9 @@
 */
 #ifndef __EQ2_SPELL_PROCESS__
 #define __EQ2_SPELL_PROCESS__
+#include <mutex>
+#include <shared_mutex>
+
 #include "client.h"
 #include "Spells.h"
 #include "zoneserver.h"
@@ -393,7 +396,7 @@ public:
 	void DeleteActiveSpell(LuaSpell* spell);
 	static bool AddLuaSpellTarget(LuaSpell* lua_spell, int32 id, bool lock_spell_targets = true);
 private:
-	Mutex MSpellProcess;
+	mutable std::shared_mutex MSpellProcess;
 	MutexMap<Entity*,Spell*> spell_que;
 	MutexList<LuaSpell*> active_spells;
 	MutexList<CastTimer*> cast_timers;

+ 127 - 24
EQ2/source/WorldServer/zoneserver.cpp

@@ -220,6 +220,16 @@ ZoneServer::~ZoneServer() {
 	// moved to the bottom as we want spawns deleted first, this used to be above Spawn deletion which is a big no no
 	safe_delete(spellProcess);
 
+
+    MGridMaps.lock();
+	std::map<int32, GridMap*>::iterator grids;
+	for(grids = grid_maps.begin(); grids != grid_maps.end(); grids++) {
+		GridMap* gm = grids->second;
+		safe_delete(gm);
+	}
+	grid_maps.clear();
+    MGridMaps.unlock();
+	
 	LogWrite(ZONE__INFO, 0, "Zone", "Completed zone shutdown of '%s'", zone_name);
 	--numzones;
 	UpdateWindowTitle(0);
@@ -3191,6 +3201,7 @@ void ZoneServer::AddSpawn(Spawn* spawn) {
 
 	AddSpawnProximities(spawn);
 
+	AddSpawnToGrid(spawn, spawn->GetLocation());
 	spawn->SetAddedToWorldTimestamp(Timer::GetCurrentTime2());
 }
 
@@ -4124,36 +4135,40 @@ void ZoneServer::CheckSpawnScriptTimers(){
 }
 
 void ZoneServer::KillSpawnByDistance(Spawn* spawn, float max_distance, bool include_players, bool send_packet){
+	if(!spawn)
+		return;
+	
 	Spawn* test_spawn = 0;
-	map<int32, Spawn*>::iterator itr;
-	MSpawnList.readlock(__FUNCTION__, __LINE__);
-	for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
-		test_spawn = itr->second;
+	std::vector<Spawn*> ret_list = GetSpawnsInGrid(spawn->GetLocation());
+	std::vector<Spawn*>::iterator itr;
+	for (itr = ret_list.begin(); itr != ret_list.end(); itr++) {
+		test_spawn = *itr;
 		if(test_spawn && test_spawn->IsEntity() && test_spawn != spawn && (!test_spawn->IsPlayer() || include_players)){
 			if(test_spawn->GetDistance(spawn) < max_distance)
 				KillSpawn(true, test_spawn, spawn, send_packet);
 		}
 	}
-	MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
 }
 
 void ZoneServer::SpawnSetByDistance(Spawn* spawn, float max_distance, string field, string value){
+	if(!spawn)
+		return;
+	
 	Spawn* test_spawn = 0;
 	int32 type = commands.GetSpawnSetType(field);
 	if(type == 0xFFFFFFFF)
 		return;
 
-	map<int32, Spawn*>::iterator itr;
-	MSpawnList.readlock(__FUNCTION__, __LINE__);
-	for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
-		test_spawn = itr->second;
+	std::vector<Spawn*> ret_list = GetSpawnsInGrid(spawn->GetLocation());
+	std::vector<Spawn*>::iterator itr;
+	for (itr = ret_list.begin(); itr != ret_list.end(); itr++) {
+		test_spawn = *itr;
 		if(test_spawn && test_spawn != spawn && !test_spawn->IsPlayer()){
 			if(test_spawn->GetDistance(spawn) < max_distance){
 				commands.SetSpawnCommand(0, test_spawn, type, value.c_str());
 			}
 		}
 	}
-	MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
 }
 
 void ZoneServer::AddSpawnScriptTimer(SpawnScriptTimer* timer){
@@ -5086,10 +5101,22 @@ void ZoneServer::SendCastSpellPacket(LuaSpell* spell, Entity* caster){
 			continue;
 		packet = configReader.getStruct("WS_HearCastSpell", client->GetVersion());
 		if(packet){
-			packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(caster));
+			int32 caster_id = client->GetPlayer()->GetIDWithPlayerSpawn(caster);
+			
+			if(!caster_id)
+				continue;
+			
+			packet->setDataByName("spawn_id", caster_id);
 			packet->setArrayLengthByName("num_targets", spell->targets.size());
-			for (int32 i = 0; i < spell->targets.size(); i++)
-				packet->setArrayDataByName("target", client->GetPlayer()->GetIDWithPlayerSpawn(spell->caster->GetZone()->GetSpawnByID(spell->targets[i])), i);
+			for (int32 i = 0; i < spell->targets.size(); i++) {
+				int32 target_id = client->GetPlayer()->GetIDWithPlayerSpawn(spell->caster->GetZone()->GetSpawnByID(spell->targets[i]));
+				if(target_id) {
+					packet->setArrayDataByName("target", target_id, i);
+				}
+				else {
+					packet->setArrayDataByName("target", 0xFFFFFFFF, i);
+				}
+			}
 			packet->setDataByName("spell_visual", spell->spell->GetSpellData()->spell_visual); //result
 			packet->setDataByName("cast_time", spell->spell->GetSpellData()->cast_time*.01); //delay
 			packet->setDataByName("spell_id", spell->spell->GetSpellID());
@@ -5115,17 +5142,24 @@ void ZoneServer::SendCastSpellPacket(int32 spell_visual, Spawn* target, Spawn* c
 				continue;
 			PacketStruct* packet = configReader.getStruct("WS_HearCastSpell", client->GetVersion());
 			if (packet) {
+				
+				int32 target_id = client->GetPlayer()->GetIDWithPlayerSpawn(target);
+				if(!target_id) // client is not aware of spawn
+					continue;
+				
 				if (!caster) {
 					packet->setDataByName("spawn_id", 0xFFFFFFFF);
-					packet->setDataByName("invoker_id", 0xFFFFFFFF);
 				}
 				else {
 					int32 caster_id = client->GetPlayer()->GetIDWithPlayerSpawn(caster);
+					
+					if(!caster_id) // client is not aware of spawn
+						continue;
+					
 					packet->setDataByName("spawn_id", caster_id);
-					packet->setDataByName("invoker_id", caster_id);
 				}
 				packet->setArrayLengthByName("num_targets", 1);
-				packet->setArrayDataByName("target", client->GetPlayer()->GetIDWithPlayerSpawn(target));
+				packet->setArrayDataByName("target", target_id);
 				packet->setDataByName("spell_visual", spell_visual);
 				packet->setDataByName("cast_time", 0);
 				packet->setDataByName("spell_id", 0);
@@ -5156,9 +5190,15 @@ void ZoneServer::SendCastEntityCommandPacket(EntityCommand* entity_command, int3
 				continue;
 			PacketStruct* packet = configReader.getStruct("WS_HearCastSpell", client->GetVersion());
 			if (packet) {
-				packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(spawn));
+				int32 caster_id = client->GetPlayer()->GetIDWithPlayerSpawn(spawn);
+				int32 target_id = client->GetPlayer()->GetIDWithPlayerSpawn(target);
+				
+				if(!caster_id || !target_id)
+					continue;
+				
+				packet->setDataByName("spawn_id", caster_id);
 				packet->setArrayLengthByName("num_targets", 1);
-				packet->setArrayDataByName("target", client->GetPlayer()->GetIDWithPlayerSpawn(target));
+				packet->setArrayDataByName("target", target_id);
 				packet->setDataByName("num_targets", 1);
 				packet->setDataByName("spell_visual", entity_command->spell_visual); //result
 				packet->setDataByName("cast_time", entity_command->cast_time * 0.01); //delay
@@ -6114,6 +6154,8 @@ void ZoneServer::RemoveSpawnSupportFunctions(Spawn* spawn, bool lock_spell_proce
 	if(!spawn)
 		return;	
 	
+	RemoveSpawnFromGrid(spawn, spawn->GetLocation());
+	
 	if(spawn->IsPlayer() && spawn->GetZone())
 		spawn->GetZone()->RemovePlayerPassenger(((Player*)spawn)->GetCharacterID());
 	if(spawn->IsEntity())
@@ -6957,14 +6999,13 @@ void ZoneServer::RemovePlayerPassenger(int32 char_id) {
 vector<Spawn*> ZoneServer::GetAttackableSpawnsByDistance(Spawn* caster, float distance) {
 	vector<Spawn*> ret;
 	Spawn* spawn = 0;
-	map<int32, Spawn*>::iterator itr;
-	MSpawnList.readlock(__FUNCTION__, __LINE__);
-	for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
-		spawn = itr->second;
+	std::vector<Spawn*> ret_list = GetSpawnsInGrid(caster->GetLocation());
+	std::vector<Spawn*>::iterator itr;
+	for (itr = ret_list.begin(); itr != ret_list.end(); itr++) {
+		spawn = *itr;
 		if (spawn && spawn->IsNPC() && spawn->appearance.attackable > 0 && spawn != caster && spawn->Alive() && spawn->GetDistance(caster, true) <= distance) 
 			ret.push_back(spawn);
 	}
-	MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
 	return ret;
 }
 
@@ -8431,4 +8472,66 @@ void ZoneServer::ProcessPendingSpawns() {
 	pending_spawn_list_add.clear();
 	MPendingSpawnListAdd.releasewritelock(__FUNCTION__, __LINE__);
 	MSpawnList.releasewritelock(__FUNCTION__, __LINE__);
-}
+}
+
+void ZoneServer::AddSpawnToGrid(Spawn* spawn, int32 grid_id) {
+    MGridMaps.lock_shared();
+	std::map<int32, GridMap*>::iterator grids = grid_maps.find(grid_id);
+	if(grids != grid_maps.end()) {
+		grids->second->MSpawns.lock();
+		grids->second->spawns.insert(make_pair(spawn->GetID(), spawn));
+		grids->second->MSpawns.unlock();
+	}
+	else {
+		MGridMaps.unlock_shared();
+		GridMap* gm = new GridMap;
+		gm->grid_id = grid_id;
+		gm->spawns.insert(make_pair(spawn->GetID(), spawn));
+		MGridMaps.lock();
+		grid_maps.insert(make_pair(grid_id, gm));
+		MGridMaps.unlock();
+		return;
+	}
+	
+	MGridMaps.unlock_shared();
+}
+
+void ZoneServer::RemoveSpawnFromGrid(Spawn* spawn, int32 grid_id) {
+    std::shared_lock lock(MGridMaps);
+	std::map<int32, GridMap*>::iterator grids = grid_maps.find(grid_id);
+	if(grids != grid_maps.end() && grids->second->spawns.count(spawn->GetID()) > 0) {
+		grids->second->MSpawns.lock();
+		grids->second->spawns.erase(spawn->GetID());
+		grids->second->MSpawns.unlock();
+	}
+}
+
+int32 ZoneServer::GetSpawnCountInGrid(int32 grid_id) {
+	int32 count = 0;
+    std::shared_lock lock(MGridMaps);
+	std::map<int32, GridMap*>::iterator grids = grid_maps.find(grid_id);
+	if(grids != grid_maps.end()) {
+		grids->second->MSpawns.lock_shared();
+		count = grids->second->spawns.size();
+		grids->second->MSpawns.unlock_shared();
+	}
+	
+	return count;
+}
+
+std::vector<Spawn*> ZoneServer::GetSpawnsInGrid(int32 grid_id) {
+	std::vector<Spawn*> ret;
+	int32 count = 0;
+    std::shared_lock lock(MGridMaps);
+	std::map<int32, GridMap*>::iterator grids = grid_maps.find(grid_id);
+	if(grids != grid_maps.end()) {
+		grids->second->MSpawns.lock_shared();
+		typedef map <int32, Spawn*> SpawnMapType;
+		for( SpawnMapType::iterator it = grids->second->spawns.begin(); it != grids->second->spawns.end(); ++it ) {
+			ret.push_back( it->second );
+		}
+		grids->second->MSpawns.unlock_shared();
+	}
+	
+	return ret;
+}

+ 17 - 0
EQ2/source/WorldServer/zoneserver.h

@@ -20,6 +20,9 @@
 #ifndef ZONESERVER_H
 #define ZONESERVER_H
 
+#include <mutex>
+#include <shared_mutex>
+
 #include "../common/linked_list.h"
 #include "../common/timer.h"
 #include "../common/queue.h"
@@ -153,6 +156,12 @@ struct LocationGrid {
 	MutexMap<Player*, bool>	players;
 };
 
+struct GridMap {
+	int32					grid_id;
+	std::map<int32, Spawn*> spawns;
+	mutable std::shared_mutex MSpawns;
+};
+
 struct TrackedSpawn {
 	Spawn* spawn;
 	float distance;
@@ -701,6 +710,10 @@ public:
 	void	SendSubSpawnUpdates(SUBSPAWN_TYPES subtype);
 	bool	HouseItemSpawnExists(int32 item_id);
 	void	ProcessPendingSpawns();
+	void 	AddSpawnToGrid(Spawn* spawn, int32 grid_id);
+	void	RemoveSpawnFromGrid(Spawn* spawn, int32 grid_id);
+	int32	GetSpawnCountInGrid(int32 grid_id);
+	std::vector<Spawn*>	GetSpawnsInGrid(int32 grid_id);
 private:
 #ifndef WIN32
 	pthread_t ZoneThread;
@@ -816,7 +829,11 @@ private:
 	Mutex MWidgetTimers;
 	map<int32, int32>								widget_timers;									// 1st int32 = spawn id
 
+	std::map<int32, GridMap*> grid_maps;
+	
 	/* Mutexs */
+	mutable std::shared_mutex MGridMaps;
+	
 	Mutex	m_enemy_faction_list;
 	Mutex	m_npc_faction_list;
 	Mutex	m_reverse_enemy_faction_list;