소스 검색

BETA: Using `ReentrantLock` instead of pure synchronization in doAttack method to prevent from possible deadlocks.

Rumen Nikiforov 11 년 전
부모
커밋
b37c516515
1개의 변경된 파일282개의 추가작업 그리고 271개의 파일을 삭제
  1. 282 271
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/L2Character.java

+ 282 - 271
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/L2Character.java

@@ -227,7 +227,8 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
 	
 	private L2Character _debugger = null;
 	
-	private final ReentrantLock _teleportLock;
+	private final ReentrantLock _teleportLock = new ReentrantLock();
+	private final ReentrantLock _attackLock = new ReentrantLock();
 	
 	private Team _team = Team.NONE;
 	
@@ -506,7 +507,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
 		}
 		
 		setIsInvul(true);
-		_teleportLock = new ReentrantLock();
 	}
 	
 	protected void initCharStatusUpdateValues()
@@ -829,366 +829,377 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
 	 * </ul>
 	 * @param target The L2Character targeted
 	 */
-	protected synchronized void doAttack(L2Character target)
+	protected void doAttack(L2Character target)
 	{
-		if ((target == null) || isAttackingDisabled())
+		if (!_attackLock.tryLock())
 		{
 			return;
 		}
-		
-		// Notify to scripts
-		EventDispatcher.getInstance().notifyEventAsync(new OnCreatureAttack(this, target), this);
-		
-		if (!isAlikeDead())
+		try
 		{
-			if ((isNpc() && target.isAlikeDead()) || !getKnownList().knowsObject(target))
+			if ((target == null) || isAttackingDisabled())
 			{
-				getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
-				sendPacket(ActionFailed.STATIC_PACKET);
 				return;
 			}
-			else if (isPlayer())
+			
+			// Notify to scripts
+			EventDispatcher.getInstance().notifyEventAsync(new OnCreatureAttack(this, target), this);
+			
+			if (!isAlikeDead())
 			{
-				if (target.isDead())
+				if ((isNpc() && target.isAlikeDead()) || !getKnownList().knowsObject(target))
 				{
 					getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
 					sendPacket(ActionFailed.STATIC_PACKET);
 					return;
 				}
-				
-				final L2PcInstance actor = getActingPlayer();
-				if (actor.isTransformed() && !actor.getTransformation().canAttack())
+				else if (isPlayer())
 				{
-					sendPacket(ActionFailed.STATIC_PACKET);
-					return;
+					if (target.isDead())
+					{
+						getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
+						sendPacket(ActionFailed.STATIC_PACKET);
+						return;
+					}
+					
+					final L2PcInstance actor = getActingPlayer();
+					if (actor.isTransformed() && !actor.getTransformation().canAttack())
+					{
+						sendPacket(ActionFailed.STATIC_PACKET);
+						return;
+					}
 				}
 			}
-		}
-		
-		// Check if attacker's weapon can attack
-		if (getActiveWeaponItem() != null)
-		{
-			L2Weapon wpn = getActiveWeaponItem();
-			if (!wpn.isAttackWeapon() && !isGM())
+			
+			// Check if attacker's weapon can attack
+			if (getActiveWeaponItem() != null)
 			{
-				if (wpn.getItemType() == WeaponType.FISHINGROD)
-				{
-					sendPacket(SystemMessageId.CANNOT_ATTACK_WITH_FISHING_POLE);
-				}
-				else
+				L2Weapon wpn = getActiveWeaponItem();
+				if (!wpn.isAttackWeapon() && !isGM())
 				{
-					sendPacket(SystemMessageId.THAT_WEAPON_CANT_ATTACK);
+					if (wpn.getItemType() == WeaponType.FISHINGROD)
+					{
+						sendPacket(SystemMessageId.CANNOT_ATTACK_WITH_FISHING_POLE);
+					}
+					else
+					{
+						sendPacket(SystemMessageId.THAT_WEAPON_CANT_ATTACK);
+					}
+					sendPacket(ActionFailed.STATIC_PACKET);
+					return;
 				}
-				sendPacket(ActionFailed.STATIC_PACKET);
-				return;
-			}
-		}
-		
-		if (getActingPlayer() != null)
-		{
-			if (getActingPlayer().inObserverMode())
-			{
-				sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
-				sendPacket(ActionFailed.STATIC_PACKET);
-				return;
 			}
 			
-			else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
+			if (getActingPlayer() != null)
 			{
-				if (TerritoryWarManager.getInstance().isTWInProgress())
+				if (getActingPlayer().inObserverMode())
 				{
-					sendPacket(SystemMessageId.YOU_CANNOT_ATTACK_A_MEMBER_OF_THE_SAME_TERRITORY);
+					sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
+					sendPacket(ActionFailed.STATIC_PACKET);
+					return;
 				}
-				else
+				
+				else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
 				{
-					sendPacket(SystemMessageId.FORCED_ATTACK_IS_IMPOSSIBLE_AGAINST_SIEGE_SIDE_TEMPORARY_ALLIED_MEMBERS);
+					if (TerritoryWarManager.getInstance().isTWInProgress())
+					{
+						sendPacket(SystemMessageId.YOU_CANNOT_ATTACK_A_MEMBER_OF_THE_SAME_TERRITORY);
+					}
+					else
+					{
+						sendPacket(SystemMessageId.FORCED_ATTACK_IS_IMPOSSIBLE_AGAINST_SIEGE_SIDE_TEMPORARY_ALLIED_MEMBERS);
+					}
+					sendPacket(ActionFailed.STATIC_PACKET);
+					return;
 				}
+				
+				// Checking if target has moved to peace zone
+				else if (target.isInsidePeaceZone(getActingPlayer()))
+				{
+					getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
+					sendPacket(ActionFailed.STATIC_PACKET);
+					return;
+				}
+			}
+			else if (isInsidePeaceZone(this, target))
+			{
+				getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
 				sendPacket(ActionFailed.STATIC_PACKET);
 				return;
 			}
 			
-			// Checking if target has moved to peace zone
-			else if (target.isInsidePeaceZone(getActingPlayer()))
+			stopEffectsOnAction();
+			
+			// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
+			L2Weapon weaponItem = getActiveWeaponItem();
+			
+			// GeoData Los Check here (or dz > 1000)
+			if (!GeoData.getInstance().canSeeTarget(this, target))
 			{
+				sendPacket(SystemMessageId.CANT_SEE_TARGET);
 				getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
 				sendPacket(ActionFailed.STATIC_PACKET);
 				return;
 			}
-		}
-		else if (isInsidePeaceZone(this, target))
-		{
-			getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
-			sendPacket(ActionFailed.STATIC_PACKET);
-			return;
-		}
-		
-		stopEffectsOnAction();
-		
-		// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
-		L2Weapon weaponItem = getActiveWeaponItem();
-		
-		// GeoData Los Check here (or dz > 1000)
-		if (!GeoData.getInstance().canSeeTarget(this, target))
-		{
-			sendPacket(SystemMessageId.CANT_SEE_TARGET);
-			getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
-			sendPacket(ActionFailed.STATIC_PACKET);
-			return;
-		}
-		
-		// BOW and CROSSBOW checks
-		if ((weaponItem != null) && !isTransformed())
-		{
-			if (weaponItem.getItemType() == WeaponType.BOW)
+			
+			// BOW and CROSSBOW checks
+			if ((weaponItem != null) && !isTransformed())
 			{
-				// Check for arrows and MP
-				if (isPlayer())
+				if (weaponItem.getItemType() == WeaponType.BOW)
 				{
-					// Checking if target has moved to peace zone - only for player-bow attacks at the moment
-					// Other melee is checked in movement code and for offensive spells a check is done every time
-					if (target.isInsidePeaceZone(getActingPlayer()))
-					{
-						getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
-						sendPacket(ActionFailed.STATIC_PACKET);
-						return;
-					}
-					
-					// Equip arrows needed in left hand and send a Server->Client packet ItemList to the L2PcInstance then return True
-					if (!checkAndEquipArrows())
-					{
-						// Cancel the action because the L2PcInstance have no arrow
-						getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
-						sendPacket(ActionFailed.STATIC_PACKET);
-						sendPacket(SystemMessageId.NOT_ENOUGH_ARROWS);
-						return;
-					}
-					
-					// Verify if the bow can be use
-					if (_disableBowAttackEndTime <= GameTimeController.getInstance().getGameTicks())
+					// Check for arrows and MP
+					if (isPlayer())
 					{
-						// Verify if L2PcInstance owns enough MP
-						int mpConsume = weaponItem.getMpConsume();
-						if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
+						// Checking if target has moved to peace zone - only for player-bow attacks at the moment
+						// Other melee is checked in movement code and for offensive spells a check is done every time
+						if (target.isInsidePeaceZone(getActingPlayer()))
 						{
-							mpConsume = weaponItem.getReducedMpConsume();
+							getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
+							sendPacket(ActionFailed.STATIC_PACKET);
+							return;
 						}
-						mpConsume = (int) calcStat(Stats.BOW_MP_CONSUME_RATE, mpConsume, null, null);
 						
-						if (getCurrentMp() < mpConsume)
+						// Equip arrows needed in left hand and send a Server->Client packet ItemList to the L2PcInstance then return True
+						if (!checkAndEquipArrows())
 						{
-							// If L2PcInstance doesn't have enough MP, stop the attack
-							ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
-							sendPacket(SystemMessageId.NOT_ENOUGH_MP);
+							// Cancel the action because the L2PcInstance have no arrow
+							getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
 							sendPacket(ActionFailed.STATIC_PACKET);
+							sendPacket(SystemMessageId.NOT_ENOUGH_ARROWS);
 							return;
 						}
 						
-						// If L2PcInstance have enough MP, the bow consumes it
-						if (mpConsume > 0)
+						// Verify if the bow can be use
+						if (_disableBowAttackEndTime <= GameTimeController.getInstance().getGameTicks())
 						{
-							getStatus().reduceMp(mpConsume);
+							// Verify if L2PcInstance owns enough MP
+							int mpConsume = weaponItem.getMpConsume();
+							if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
+							{
+								mpConsume = weaponItem.getReducedMpConsume();
+							}
+							mpConsume = (int) calcStat(Stats.BOW_MP_CONSUME_RATE, mpConsume, null, null);
+							
+							if (getCurrentMp() < mpConsume)
+							{
+								// If L2PcInstance doesn't have enough MP, stop the attack
+								ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
+								sendPacket(SystemMessageId.NOT_ENOUGH_MP);
+								sendPacket(ActionFailed.STATIC_PACKET);
+								return;
+							}
+							
+							// If L2PcInstance have enough MP, the bow consumes it
+							if (mpConsume > 0)
+							{
+								getStatus().reduceMp(mpConsume);
+							}
+							
+							// Set the period of bow no re-use
+							_disableBowAttackEndTime = (5 * GameTimeController.TICKS_PER_SECOND) + GameTimeController.getInstance().getGameTicks();
+						}
+						else
+						{
+							// Cancel the action because the bow can't be re-use at this moment
+							ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
+							sendPacket(ActionFailed.STATIC_PACKET);
+							return;
 						}
-						
-						// Set the period of bow no re-use
-						_disableBowAttackEndTime = (5 * GameTimeController.TICKS_PER_SECOND) + GameTimeController.getInstance().getGameTicks();
-					}
-					else
-					{
-						// Cancel the action because the bow can't be re-use at this moment
-						ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
-						sendPacket(ActionFailed.STATIC_PACKET);
-						return;
 					}
 				}
-			}
-			if (weaponItem.getItemType() == WeaponType.CROSSBOW)
-			{
-				// Check for bolts
-				if (isPlayer())
+				if (weaponItem.getItemType() == WeaponType.CROSSBOW)
 				{
-					// Checking if target has moved to peace zone - only for player-crossbow attacks at the moment
-					// Other melee is checked in movement code and for offensive spells a check is done every time
-					if (target.isInsidePeaceZone(getActingPlayer()))
-					{
-						getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
-						sendPacket(ActionFailed.STATIC_PACKET);
-						return;
-					}
-					
-					// Equip bolts needed in left hand and send a Server->Client packet ItemList to the L2PcINstance then return True
-					if (!checkAndEquipBolts())
-					{
-						// Cancel the action because the L2PcInstance have no arrow
-						getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
-						sendPacket(ActionFailed.STATIC_PACKET);
-						sendPacket(SystemMessageId.NOT_ENOUGH_BOLTS);
-						return;
-					}
-					
-					// Verify if the crossbow can be use
-					if (_disableCrossBowAttackEndTime <= GameTimeController.getInstance().getGameTicks())
+					// Check for bolts
+					if (isPlayer())
 					{
-						// Verify if L2PcInstance owns enough MP
-						int mpConsume = weaponItem.getMpConsume();
-						if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
+						// Checking if target has moved to peace zone - only for player-crossbow attacks at the moment
+						// Other melee is checked in movement code and for offensive spells a check is done every time
+						if (target.isInsidePeaceZone(getActingPlayer()))
 						{
-							mpConsume = weaponItem.getReducedMpConsume();
+							getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
+							sendPacket(ActionFailed.STATIC_PACKET);
+							return;
 						}
-						mpConsume = (int) calcStat(Stats.BOW_MP_CONSUME_RATE, mpConsume, null, null);
 						
-						if (getCurrentMp() < mpConsume)
+						// Equip bolts needed in left hand and send a Server->Client packet ItemList to the L2PcINstance then return True
+						if (!checkAndEquipBolts())
 						{
-							// If L2PcInstance doesn't have enough MP, stop the attack
-							ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
-							sendPacket(SystemMessageId.NOT_ENOUGH_MP);
+							// Cancel the action because the L2PcInstance have no arrow
+							getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
 							sendPacket(ActionFailed.STATIC_PACKET);
+							sendPacket(SystemMessageId.NOT_ENOUGH_BOLTS);
 							return;
 						}
 						
-						// If L2PcInstance have enough MP, the bow consumes it
-						if (mpConsume > 0)
+						// Verify if the crossbow can be use
+						if (_disableCrossBowAttackEndTime <= GameTimeController.getInstance().getGameTicks())
 						{
-							getStatus().reduceMp(mpConsume);
+							// Verify if L2PcInstance owns enough MP
+							int mpConsume = weaponItem.getMpConsume();
+							if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
+							{
+								mpConsume = weaponItem.getReducedMpConsume();
+							}
+							mpConsume = (int) calcStat(Stats.BOW_MP_CONSUME_RATE, mpConsume, null, null);
+							
+							if (getCurrentMp() < mpConsume)
+							{
+								// If L2PcInstance doesn't have enough MP, stop the attack
+								ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
+								sendPacket(SystemMessageId.NOT_ENOUGH_MP);
+								sendPacket(ActionFailed.STATIC_PACKET);
+								return;
+							}
+							
+							// If L2PcInstance have enough MP, the bow consumes it
+							if (mpConsume > 0)
+							{
+								getStatus().reduceMp(mpConsume);
+							}
+							
+							// Set the period of crossbow no re-use
+							_disableCrossBowAttackEndTime = (5 * GameTimeController.TICKS_PER_SECOND) + GameTimeController.getInstance().getGameTicks();
+						}
+						else
+						{
+							// Cancel the action because the crossbow can't be re-use at this moment
+							ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
+							sendPacket(ActionFailed.STATIC_PACKET);
+							return;
 						}
-						
-						// Set the period of crossbow no re-use
-						_disableCrossBowAttackEndTime = (5 * GameTimeController.TICKS_PER_SECOND) + GameTimeController.getInstance().getGameTicks();
-					}
-					else
-					{
-						// Cancel the action because the crossbow can't be re-use at this moment
-						ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
-						sendPacket(ActionFailed.STATIC_PACKET);
-						return;
 					}
-				}
-				else if (isNpc())
-				{
-					if (_disableCrossBowAttackEndTime > GameTimeController.getInstance().getGameTicks())
+					else if (isNpc())
 					{
-						return;
+						if (_disableCrossBowAttackEndTime > GameTimeController.getInstance().getGameTicks())
+						{
+							return;
+						}
 					}
 				}
 			}
-		}
-		
-		// Add the L2PcInstance to _knownObjects and _knownPlayer of the target
-		target.getKnownList().addKnownObject(this);
-		
-		// Reduce the current CP if TIREDNESS configuration is activated
-		if (Config.ALT_GAME_TIREDNESS)
-		{
-			setCurrentCp(getCurrentCp() - 10);
-		}
-		
-		// Verify if soulshots are charged.
-		final boolean wasSSCharged = isChargedShot(ShotType.SOULSHOTS);
-		// Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)
-		final int timeAtk = calculateTimeBetweenAttacks(target, weaponItem);
-		// the hit is calculated to happen halfway to the animation - might need further tuning e.g. in bow case
-		final int timeToHit = timeAtk / 2;
-		_attackEndTime = System.currentTimeMillis() + timeAtk;
-		final int ssGrade = (weaponItem != null) ? weaponItem.getItemGradeSPlus().getId() : 0;
-		// Create a Server->Client packet Attack
-		Attack attack = new Attack(this, target, wasSSCharged, ssGrade);
-		
-		// Make sure that char is facing selected target
-		// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
-		setHeading(Util.calculateHeadingFrom(this, target));
-		
-		// Get the Attack Reuse Delay of the L2Weapon
-		int reuse = calculateReuseTime(target, weaponItem);
-		
-		boolean hitted = false;
-		switch (getAttackType())
-		{
-			case BOW:
-			{
-				hitted = doAttackHitByBow(attack, target, timeAtk, reuse);
-				break;
-			}
-			case CROSSBOW:
-			{
-				hitted = doAttackHitByCrossBow(attack, target, timeAtk, reuse);
-				break;
-			}
-			case POLE:
+			
+			// Add the L2PcInstance to _knownObjects and _knownPlayer of the target
+			target.getKnownList().addKnownObject(this);
+			
+			// Reduce the current CP if TIREDNESS configuration is activated
+			if (Config.ALT_GAME_TIREDNESS)
 			{
-				hitted = doAttackHitByPole(attack, target, timeToHit);
-				break;
+				setCurrentCp(getCurrentCp() - 10);
 			}
-			case FIST:
+			
+			// Verify if soulshots are charged.
+			final boolean wasSSCharged = isChargedShot(ShotType.SOULSHOTS);
+			// Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)
+			final int timeAtk = calculateTimeBetweenAttacks(target, weaponItem);
+			// the hit is calculated to happen halfway to the animation - might need further tuning e.g. in bow case
+			final int timeToHit = timeAtk / 2;
+			_attackEndTime = System.currentTimeMillis() + timeAtk;
+			final int ssGrade = (weaponItem != null) ? weaponItem.getItemGradeSPlus().getId() : 0;
+			// Create a Server->Client packet Attack
+			Attack attack = new Attack(this, target, wasSSCharged, ssGrade);
+			
+			// Make sure that char is facing selected target
+			// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
+			setHeading(Util.calculateHeadingFrom(this, target));
+			
+			// Get the Attack Reuse Delay of the L2Weapon
+			int reuse = calculateReuseTime(target, weaponItem);
+			
+			boolean hitted = false;
+			switch (getAttackType())
 			{
-				if (!isPlayer())
+				case BOW:
+				{
+					hitted = doAttackHitByBow(attack, target, timeAtk, reuse);
+					break;
+				}
+				case CROSSBOW:
+				{
+					hitted = doAttackHitByCrossBow(attack, target, timeAtk, reuse);
+					break;
+				}
+				case POLE:
+				{
+					hitted = doAttackHitByPole(attack, target, timeToHit);
+					break;
+				}
+				case FIST:
+				{
+					if (!isPlayer())
+					{
+						hitted = doAttackHitSimple(attack, target, timeToHit);
+						break;
+					}
+				}
+				case DUAL:
+				case DUALFIST:
+				case DUALDAGGER:
+				{
+					hitted = doAttackHitByDual(attack, target, timeToHit);
+					break;
+				}
+				default:
 				{
 					hitted = doAttackHitSimple(attack, target, timeToHit);
 					break;
 				}
 			}
-			case DUAL:
-			case DUALFIST:
-			case DUALDAGGER:
-			{
-				hitted = doAttackHitByDual(attack, target, timeToHit);
-				break;
-			}
-			default:
+			
+			// Flag the attacker if it's a L2PcInstance outside a PvP area
+			final L2PcInstance player = getActingPlayer();
+			if (player != null)
 			{
-				hitted = doAttackHitSimple(attack, target, timeToHit);
-				break;
+				AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
+				if (player.getSummon() != target)
+				{
+					player.updatePvPStatus(target);
+				}
 			}
-		}
-		
-		// Flag the attacker if it's a L2PcInstance outside a PvP area
-		final L2PcInstance player = getActingPlayer();
-		if (player != null)
-		{
-			AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
-			if (player.getSummon() != target)
+			// Check if hit isn't missed
+			if (!hitted)
 			{
-				player.updatePvPStatus(target);
+				abortAttack(); // Abort the attack of the L2Character and send Server->Client ActionFailed packet
 			}
-		}
-		// Check if hit isn't missed
-		if (!hitted)
-		{
-			abortAttack(); // Abort the attack of the L2Character and send Server->Client ActionFailed packet
-		}
-		else
-		{
-			// If we didn't miss the hit, discharge the shoulshots, if any
-			setChargedShot(ShotType.SOULSHOTS, false);
-			
-			if (player != null)
+			else
 			{
-				if (player.isCursedWeaponEquipped())
+				// If we didn't miss the hit, discharge the shoulshots, if any
+				setChargedShot(ShotType.SOULSHOTS, false);
+				
+				if (player != null)
 				{
-					// If hit by a cursed weapon, CP is reduced to 0
-					if (!target.isInvul())
+					if (player.isCursedWeaponEquipped())
 					{
-						target.setCurrentCp(0);
+						// If hit by a cursed weapon, CP is reduced to 0
+						if (!target.isInvul())
+						{
+							target.setCurrentCp(0);
+						}
 					}
-				}
-				else if (player.isHero())
-				{
-					// If a cursed weapon is hit by a Hero, CP is reduced to 0
-					if (target.isPlayer() && target.getActingPlayer().isCursedWeaponEquipped())
+					else if (player.isHero())
 					{
-						target.setCurrentCp(0);
+						// If a cursed weapon is hit by a Hero, CP is reduced to 0
+						if (target.isPlayer() && target.getActingPlayer().isCursedWeaponEquipped())
+						{
+							target.setCurrentCp(0);
+						}
 					}
 				}
 			}
+			
+			// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
+			// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
+			if (attack.hasHits())
+			{
+				broadcastPacket(attack);
+			}
+			
+			// Notify AI with EVT_READY_TO_ACT
+			ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), timeAtk + reuse);
 		}
-		
-		// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
-		// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
-		if (attack.hasHits())
+		finally
 		{
-			broadcastPacket(attack);
+			_attackLock.unlock();
 		}
-		
-		// Notify AI with EVT_READY_TO_ACT
-		ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), timeAtk + reuse);
 	}
 	
 	/**