Browse Source

Spell stability fixes (various crashes, bad pointer handling)

Fix #171
Fix #173
image 3 years ago
parent
commit
7a86840df0

+ 4 - 3
EQ2/source/WorldServer/Combat.cpp

@@ -1267,6 +1267,7 @@ void Entity::AddProc(int8 type, float chance, Item* item, LuaSpell* spell) {
 	proc->chance = chance;
 	proc->item = item;
 	proc->spell = spell;
+	proc->spellid = spell->spell->GetSpellID();
 	m_procList[type].push_back(proc);
 	MProcList.releasewritelock(__FUNCTION__, __LINE__);
 }
@@ -1286,8 +1287,8 @@ void Entity::RemoveProc(Item* item, LuaSpell* spell) {
 			Proc* proc = *itr;
 
 			if ((item && proc->item == item) || (spell && proc->spell == spell)) {
-				safe_delete(*itr);
 				itr = proc_itr->second.erase(itr);
+				safe_delete(proc);
 			}
 			else
 				itr++;
@@ -1408,8 +1409,8 @@ void Entity::ClearProcs() {
 	for (proc_itr = m_procList.begin(); proc_itr != m_procList.end(); proc_itr++) {
 		itr = proc_itr->second.begin();
 		while (itr != proc_itr->second.end()) {
-				safe_delete(*itr);
-				itr = proc_itr->second.erase(itr);
+			safe_delete(*itr);
+			itr = proc_itr->second.erase(itr);
 		}
 		proc_itr->second.clear();
 	}

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

@@ -276,6 +276,7 @@ struct Proc {
 	LuaSpell*	spell;
 	Item*		item;
 	float		chance;
+	int32		spellid;
 };
 
 #define PROC_TYPE_OFFENSIVE				1

+ 33 - 8
EQ2/source/WorldServer/LuaInterface.cpp

@@ -20,6 +20,7 @@
 #include "LuaInterface.h"
 #include "LuaFunctions.h"
 #include "WorldDatabase.h"
+#include "SpellProcess.h"
 #include "../common/Log.h"
 
 #ifndef WIN32
@@ -519,7 +520,10 @@ LuaSpell* LuaInterface::GetCurrentSpell(lua_State* state) {
 bool LuaInterface::CallSpellProcess(LuaSpell* spell, int8 num_parameters) {
 	if(shutting_down || !spell || !spell->caster)
 		return false;
+
+	MSpells.lock();
 	current_spells[spell->state] = spell;
+	MSpells.unlock();
 	if(lua_pcall(spell->state, num_parameters, 0, 0) != 0){
 		LogError("Error running %s", lua_tostring(spell->state, -1));
 		lua_pop(spell->state, 1);
@@ -605,6 +609,7 @@ lua_State* LuaInterface::LoadLuaFile(const char* name) {
 void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool can_delete) {
 	if(shutting_down)
 		return;
+
 	if(call_remove_function){
 		lua_getglobal(spell->state, "remove");
 		LUASpawnWrapper* spawn_wrapper = new LUASpawnWrapper();
@@ -623,13 +628,19 @@ void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool
 		else
 			lua_pushlightuserdata(spell->state, 0);
 
+		MSpells.lock();
 		current_spells[spell->state] = spell;
+		MSpells.unlock();
 		lua_pcall(spell->state, 2, 0, 0);
 	}
 	if (can_delete) {
-		MSpellDelete.lock();
-		spells_pending_delete[spell] = Timer::GetCurrentTime2() + 10000;
-		MSpellDelete.unlock();
+		AddPendingSpellDelete(spell);
+	}
+	if (spell->caster)
+	{
+		spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(spell, false);
+		spell->caster->RemoveProc(0, spell);
+		spell->caster->RemoveMaintainedSpell(spell);
 	}
 }
 
@@ -1078,17 +1089,17 @@ void LuaInterface::AddUserDataPtr(LUAUserData* data) {
 
 void LuaInterface::DeletePendingSpells(bool all) {
 	MSpellDelete.lock();
-	if(spells_pending_delete.size() > 0){
+	if (spells_pending_delete.size() > 0) {
 		int32 time = Timer::GetCurrentTime2();
 		map<LuaSpell*, int32>::iterator itr;
 		vector<LuaSpell*> tmp_deletes;
 		vector<LuaSpell*>::iterator del_itr;
-		for(itr = spells_pending_delete.begin(); itr != spells_pending_delete.end(); itr++){
-			if(all || time >= itr->second)
+		for (itr = spells_pending_delete.begin(); itr != spells_pending_delete.end(); itr++) {
+			if (all || time >= itr->second)
 				tmp_deletes.push_back(itr->first);
 		}
 		LuaSpell* spell = 0;
-		for(del_itr = tmp_deletes.begin(); del_itr != tmp_deletes.end(); del_itr++){
+		for (del_itr = tmp_deletes.begin(); del_itr != tmp_deletes.end(); del_itr++) {
 			spell = *del_itr;
 			spells_pending_delete.erase(spell);
 			safe_delete(spell);
@@ -1097,6 +1108,16 @@ void LuaInterface::DeletePendingSpells(bool all) {
 	MSpellDelete.unlock();
 }
 
+void LuaInterface::DeletePendingSpell(LuaSpell* spell) {
+	MSpellDelete.lock();
+	if (spells_pending_delete.size() > 0) {
+		map<LuaSpell*, int32>::iterator itr = spells_pending_delete.find(spell);
+		if (itr != spells_pending_delete.end())
+			spells_pending_delete.erase(itr);
+	}
+	MSpellDelete.unlock();
+}
+
 void LuaInterface::DeleteUserDataPtrs(bool all) {
 	MLUAUserData.lock();
 	if(user_data.size() > 0){
@@ -1399,6 +1420,9 @@ LuaSpell* LuaInterface::GetSpell(const char* name)  {
 		new_spell->slot_pos = 0;
 		new_spell->damage_remaining = 0;
 		new_spell->effect_bitmask = 0;
+		new_spell->caster = 0;
+		new_spell->initial_target = 0;
+		new_spell->spell = 0;
 		return new_spell;
 	}
 	else{
@@ -1706,7 +1730,8 @@ bool LuaInterface::RunZoneScript(string script_name, const char* function_name,
 
 void LuaInterface::AddPendingSpellDelete(LuaSpell* spell) {
 	MSpellDelete.lock();
-	spells_pending_delete[spell] = Timer::GetCurrentTime2() + 10000;
+	if ( spells_pending_delete.count(spell) == 0 )
+		spells_pending_delete[spell] = Timer::GetCurrentTime2() + 10000;
 	MSpellDelete.unlock();
 }
 

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

@@ -241,6 +241,7 @@ public:
 	void			AddUserDataPtr(LUAUserData* data);
 	void			DeleteUserDataPtrs(bool all);
 	void			DeletePendingSpells(bool all);
+	void			DeletePendingSpell(LuaSpell* spell);
 	Mutex*			GetSpawnScriptMutex(const char* name);
 	Mutex*			GetItemScriptMutex(const char* name);
 	Mutex*			GetZoneScriptMutex(const char* name);

+ 19 - 12
EQ2/source/WorldServer/PlayerGroups.cpp

@@ -597,13 +597,23 @@ void PlayerGroupManager::UpdateGroupBuffs() {
 			if (!caster->GetMaintainedSpellBySlot(0))
 				continue;
 
+			caster->GetMaintainedMutex()->readlock(__FUNCTION__, __LINE__);
 			// go through the player's maintained spells
 			me = caster->GetMaintainedSpells();
-			caster->GetMaintainedMutex()->readlock(__FUNCTION__, __LINE__);
-			for (i = 0; i < NUM_MAINTAINED_EFFECTS; i++){
+			for (i = 0; i < NUM_MAINTAINED_EFFECTS; i++) {
 				if (me[i].spell_id == 0xFFFFFFFF)
 					continue;
 				luaspell = me[i].spell;
+
+				if (!luaspell)
+					continue;
+				
+				if (!luaspell->caster)
+				{
+					LogWrite(PLAYER__ERROR, 0, "Player", "Bad luaspell, caster is NULL, spellid: %u", me[i].spell_id);
+					continue;
+				}
+
 				spell = luaspell->spell;
 
 				if (spell && spell->GetSpellData()->group_spell && spell->GetSpellData()->friendly_spell &&
@@ -628,8 +638,8 @@ void PlayerGroupManager::UpdateGroupBuffs() {
 							has_effect = true;
 
 						// Check if player is within range of the caster
-						if (group_member->GetZone() != caster->GetZone() || caster->GetDistance(group_member) > spell->GetSpellData()->radius){
-							if (has_effect){
+						if (group_member->GetZone() != caster->GetZone() || caster->GetDistance(group_member) > spell->GetSpellData()->radius) {
+							if (has_effect) {
 								group_member->RemoveSpellEffect(luaspell);
 								group_member->RemoveSpellBonus(luaspell);
 								group_member->RemoveSkillBonus(spell->GetSpellID());
@@ -639,14 +649,14 @@ void PlayerGroupManager::UpdateGroupBuffs() {
 										client->QueuePacket(packet);
 								}
 								//Also remove group buffs from pet
-								if (group_member->HasPet()){
+								if (group_member->HasPet()) {
 									pet = group_member->GetPet();
 									charmed_pet = group_member->GetCharmedPet();
-									if (pet){
+									if (pet) {
 										pet->RemoveSpellEffect(luaspell);
 										pet->RemoveSpellBonus(luaspell);
 									}
-									if (charmed_pet){
+									if (charmed_pet) {
 										charmed_pet->RemoveSpellEffect(luaspell);
 										charmed_pet->RemoveSpellBonus(luaspell);
 									}
@@ -665,7 +675,7 @@ void PlayerGroupManager::UpdateGroupBuffs() {
 						pet = 0;
 						charmed_pet = 0;
 
-						if (group_member->HasPet()){
+						if (group_member->HasPet()) {
 							pet = group_member->GetPet();
 							charmed_pet = group_member->GetCharmedPet();
 						}
@@ -676,9 +686,6 @@ void PlayerGroupManager::UpdateGroupBuffs() {
 						if (charmed_pet)
 							charmed_pet->AddSpellEffect(luaspell);
 
-
-						//add group member to list of targets for caster's spell if true
-						new_target_list.push_back(group_member->GetID());
 						if (pet)
 							new_target_list.push_back(pet->GetID());
 						if (charmed_pet)
@@ -687,7 +694,7 @@ void PlayerGroupManager::UpdateGroupBuffs() {
 
 						// look for a spell bonus on caster's spell
 						sb_list = caster->GetAllSpellBonuses(luaspell);
-						for (int32 x = 0; x < sb_list->size(); x++){
+						for (int32 x = 0; x < sb_list->size(); x++) {
 							bv = sb_list->at(x);
 							group_member->AddSpellBonus(luaspell, bv->type, bv->value, bv->class_req, bv->race_req, bv->faction_req);
 							if (pet)

+ 76 - 6
EQ2/source/WorldServer/SpellProcess.cpp

@@ -366,6 +366,7 @@ bool SpellProcess::DeleteCasterSpell(LuaSpell* spell){
 				if (spell->caster && spell->caster->IsPlayer())
 					SendSpellBookUpdate(spell->caster->GetZone()->GetClientBySpawn(spell->caster));
 			}
+			spell->caster->RemoveProc(0, spell);
 			spell->caster->RemoveMaintainedSpell(spell);
 			CheckRemoveTargetFromSpell(spell, false);
 			ZoneServer* zone = spell->caster->GetZone();
@@ -854,6 +855,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 						caster->GetZone()->TriggerCharSheetTimer();
 				}
 
+				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				safe_delete(lua_spell);
 				return;
 			}
@@ -880,6 +882,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					
 					// make sure to release the lock before we return out
 					zone->GetTradeskillMgr()->ReleaseReadLock(__FUNCTION__, __LINE__);
+					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					safe_delete(lua_spell);
 					return;
 				}
@@ -890,6 +893,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			{
 				LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot cast a tradeskill spell (%s) while not crafting.", caster->GetName(), spell->GetName());
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_ONLY_WHEN_CRAFTING);
+				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				safe_delete(lua_spell);
 				return;
 			}
@@ -899,6 +903,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		{
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot see target %s.", caster->GetName(), target->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_CANT_SEE_TARGET);
+			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			safe_delete(lua_spell);
 			return;
 		}
@@ -907,6 +912,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		{
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot cast (mezzed or stunned).", caster->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_CANNOT_CAST_STUNNED);
+			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			safe_delete(lua_spell);
 			return;
 		}
@@ -915,6 +921,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		{
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot cast (stifled).", caster->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_CANNOT_CAST_STIFFLED);
+			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			safe_delete(lua_spell);
 			return;
 		}
@@ -923,6 +930,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		{
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot cast (feared).", caster->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_CANNOT_CAST_FEARED);
+			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			safe_delete(lua_spell);
 			return;
 		}
@@ -931,6 +939,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		{
 			LogWrite(SPELL__DEBUG, 1, "Spell", "Queuing spell for %s.", caster->GetName());
 			CheckSpellQueue(spell, caster);
+			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			safe_delete(lua_spell);
 			return;
 		}
@@ -943,6 +952,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			{
 				LogWrite(SPELL__DEBUG, 1, "Spell", "%s: No target.", spell->GetName());
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
+				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				safe_delete(lua_spell);
 				return;
 			}
@@ -979,6 +989,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			{
 				LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Too far.", spell->GetName());
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_TOO_FAR_AWAY);
+				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				safe_delete(lua_spell);
 				return;
 			}
@@ -987,6 +998,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			{
 				LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Too close.", spell->GetName());
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_TOO_CLOSE);
+				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				safe_delete(lua_spell);
 				return;
 			}
@@ -1002,6 +1014,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				{
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: No target or not groundspawn.", spell->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
+					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					safe_delete(lua_spell);
 					return;
 				}
@@ -1026,6 +1039,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				{
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: No target.", spell->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
+					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					safe_delete(lua_spell);
 					return;
 				}
@@ -1043,6 +1057,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					if (((NPC*)target)->GetOwner()->IsNPC())
 					{
 						zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_A_FRIEND);
+						lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 						safe_delete(lua_spell);
 						return;
 					}
@@ -1053,6 +1068,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				else
 				{
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_A_FRIEND);
+					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					safe_delete(lua_spell);
 					return;
 				}
@@ -1069,6 +1085,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				{
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: No target.", spell->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
+					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					safe_delete(lua_spell);
 					return;
 				}
@@ -1077,6 +1094,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				{
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Not an Enemy (Target: %s).", spell->GetName(), target->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
+					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					safe_delete(lua_spell);
 					return;
 				}
@@ -1085,6 +1103,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				{
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Target is not alive (Target: %s).", spell->GetName(), target->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ALIVE);
+					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					safe_delete(lua_spell);
 					return;
 				}
@@ -1093,6 +1112,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				{
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Target is invulnerable (Target: %s).", spell->GetName(), target->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_TARGET_INVULNERABLE);
+					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					safe_delete(lua_spell);
 					return;
 				}
@@ -1104,6 +1124,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					{
 						LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Target (%s) is player and not attackable.", spell->GetName(), target->GetName());
 						zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
+						lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 						safe_delete(lua_spell);
 						return;
 					}
@@ -1112,6 +1133,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				if (target->IsPet() && ((NPC*)target)->GetOwner() && ((NPC*)target)->GetOwner() == caster) {
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Target (%s) is casters pet and not attackable by caster.", spell->GetName(), target->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
+					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					safe_delete(lua_spell);
 					return;
 				}
@@ -1121,6 +1143,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		if (lua_spell->targets.size() == 0 && spell->GetSpellData()->max_aoe_targets == 0) 
 		{
 			LogWrite(SPELL__ERROR, 0, "Spell", "SpellProcess::ProcessSpell Unable to find any spell targets for spell '%s'.", spell->GetName());
+			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			safe_delete(lua_spell);
 			return;
 		}
@@ -1130,11 +1153,13 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			if(target->Alive())
 			{
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_DEAD);
+				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				safe_delete(lua_spell);
 				return;
 			}
 			if(target->IsPlayer() && zone->GetClientBySpawn(target)->GetCurrentRez()->active){
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_ALREADY_CAST);
+				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				safe_delete(lua_spell);
 				return;
 			}
@@ -1143,6 +1168,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		if(!CheckPower(lua_spell)) 
 		{
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_POWER);
+			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			safe_delete(lua_spell);
 			return;
 		}
@@ -1150,6 +1176,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		if (!CheckHP(lua_spell)) 
 		{ 
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_HEALTH);
+			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			safe_delete(lua_spell);
 			return; 
 		}
@@ -1157,6 +1184,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		if (!CheckSavagery(lua_spell))
 		{
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_SAVAGERY);
+			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			safe_delete(lua_spell);
 			return;
 		}
@@ -1164,6 +1192,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		if (!CheckDissonance(lua_spell))
 		{
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_DISSONANCE);
+			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			safe_delete(lua_spell);
 			return;
 		}
@@ -1171,6 +1200,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		if (!CheckConcentration(lua_spell)) 
 		{
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_CONC);
+			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			safe_delete(lua_spell);
 			return;
 		}
@@ -1186,6 +1216,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 
 				if (!result) {
 					zone->SendSpellFailedPacket(client, error);
+					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					safe_delete(lua_spell);
 					return;
 				}
@@ -1227,6 +1258,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		{
 			if(!CastProcessedSpell(lua_spell))
 			{
+				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				safe_delete(lua_spell);
 				return;
 			}
@@ -2090,18 +2122,54 @@ void SpellProcess::AddSpellScriptTimer(SpellScriptTimer* timer) {
 	MSpellScriptTimers.releasewritelock(__FUNCTION__, __LINE__);
 }
 
-void SpellProcess::RemoveSpellScriptTimer(SpellScriptTimer* timer) {
+void SpellProcess::RemoveSpellScriptTimer(SpellScriptTimer* timer, bool locked) {
+	if (m_spellScriptList.size() == 0)
+		return;
+
 	vector<SpellScriptTimer*>::iterator itr;
-	MSpellScriptTimers.writelock(__FUNCTION__, __LINE__);
+	if(!locked)
+		MSpellScriptTimers.writelock(__FUNCTION__, __LINE__);
 	for (itr = m_spellScriptList.begin(); itr != m_spellScriptList.end(); itr++) {
 		if ((*itr) == timer) {
+			SpellScriptTimer* timer = *itr;
 			if ((*itr) && (*itr)->deleteWhenDone && lua_interface) {
 				lua_interface->AddPendingSpellDelete(timer->spell);
 			}
 			m_spellScriptList.erase(itr);
+			safe_delete(timer);
 			break;
 		}
 	}
+
+	if(!locked)
+		MSpellScriptTimers.releasewritelock(__FUNCTION__, __LINE__);
+}
+
+void SpellProcess::RemoveSpellScriptTimerBySpell(LuaSpell* spell, bool clearPendingDeletes) {
+	vector<SpellScriptTimer*>::iterator itr;
+	MSpellScriptTimers.writelock(__FUNCTION__, __LINE__);
+
+	if (lua_interface && clearPendingDeletes)
+		lua_interface->DeletePendingSpell(spell);
+
+	if (m_spellScriptList.size() == 0)
+	{
+		MSpellScriptTimers.releasewritelock(__FUNCTION__, __LINE__);
+		return;
+	}
+
+	for (itr = m_spellScriptList.begin(); itr != m_spellScriptList.end(); ) {
+		if ((*itr)->spell == spell)
+		{
+			vector<SpellScriptTimer*>::iterator cur = itr;
+			SpellScriptTimer* timer = *itr;
+			m_spellScriptList.erase(cur);
+			safe_delete(timer);
+			break;
+		}
+		else
+			itr++;
+	}
 	MSpellScriptTimers.releasewritelock(__FUNCTION__, __LINE__);
 }
 
@@ -2109,19 +2177,18 @@ void SpellProcess::CheckSpellScriptTimers() {
 	vector<SpellScriptTimer*>::iterator itr;
 	vector<SpellScriptTimer*> temp_list;
 	
-	MSpellScriptTimers.readlock(__FUNCTION__, __LINE__);
+	MSpellScriptTimers.writelock(__FUNCTION__, __LINE__);
 	for (itr = m_spellScriptList.begin(); itr != m_spellScriptList.end(); itr++) {
 		if (Timer::GetCurrentTime2() >= (*itr)->time) {
 			temp_list.push_back((*itr));
 			ProcessSpell((*itr)->spell, false, (*itr)->customFunction.c_str(), (*itr));
 		}
 	}
-	MSpellScriptTimers.releasereadlock(__FUNCTION__, __LINE__);
 
 	for (itr = temp_list.begin(); itr != temp_list.end(); itr++) {
-		RemoveSpellScriptTimer(*itr);
-		safe_delete(*itr);
+		RemoveSpellScriptTimer(*itr, true);
 	}
+	MSpellScriptTimers.releasewritelock(__FUNCTION__, __LINE__);
 }
 
 bool SpellProcess::SpellScriptTimersHasSpell(LuaSpell* spell) {
@@ -2191,6 +2258,9 @@ void SpellProcess::CheckRemoveTargetFromSpell(LuaSpell* spell, bool allow_delete
 						if (remove_spawn){
 							spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
 							for (target_itr = targets->begin(); target_itr != targets->end(); target_itr++){
+								((Entity*)remove_spawn)->RemoveProc(0, spell);
+								((Entity*)remove_spawn)->RemoveMaintainedSpell(spell);
+
 								if (remove_spawn->GetID() == (*target_itr)){
 									targets->erase(target_itr);
 									if (remove_spawn->IsEntity())

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

@@ -347,7 +347,8 @@ public:
 
 	/// <summary>Removes a spell script timer from the list</summary>
 	/// <param name='timer'>Timer to remove</param>
-	void RemoveSpellScriptTimer(SpellScriptTimer* timer);
+	void RemoveSpellScriptTimer(SpellScriptTimer* timer, bool locked=false);
+	void RemoveSpellScriptTimerBySpell(LuaSpell* spell, bool clearPendingDeletes=true);
 
 	/// <summary>Checks the spell script timers</summary>
 	void CheckSpellScriptTimers();