Prechádzať zdrojové kódy

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 rok pred
rodič
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;