From c9601fac2af021259a9bc62f613ba432c2aa658d Mon Sep 17 00:00:00 2001
From: MobiusDevelopment <8391001+MobiusDevelopment@users.noreply.github.com>
Date: Mon, 30 Mar 2020 16:49:57 +0000
Subject: [PATCH] Addition of AI related task managers.
---
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 92 ++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 43 ++---
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/FortSiegeGuardAI.java | 2 +-
.../l2jmobius/gameserver/ai/NpcWalkerAI.java | 6 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../l2jmobius/gameserver/ai/SiegeGuardAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 138 ++++------------
.../l2jmobius/gameserver/ai/AttackableAI.java | 20 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 8 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/FortSiegeGuardAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../l2jmobius/gameserver/ai/SiegeGuardAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 138 ++++------------
.../l2jmobius/gameserver/ai/AttackableAI.java | 20 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 8 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/FortSiegeGuardAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../l2jmobius/gameserver/ai/SiegeGuardAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
.../l2jmobius/gameserver/ai/AbstractAI.java | 112 ++++---------
.../l2jmobius/gameserver/ai/AttackableAI.java | 22 +--
.../gameserver/ai/ControllableMobAI.java | 2 +-
.../l2jmobius/gameserver/ai/CreatureAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/DoorAI.java | 2 +-
.../gameserver/ai/DoppelgangerAI.java | 4 +-
.../org/l2jmobius/gameserver/ai/PlayerAI.java | 2 +-
.../org/l2jmobius/gameserver/ai/SummonAI.java | 2 +-
.../l2jmobius/gameserver/ai/VehicleAI.java | 2 +-
.../AttackableThinkTaskManager.java | 91 +++++++++++
.../CreatureFollowTaskManager.java | 147 ++++++++++++++++++
201 files changed, 5014 insertions(+), 2037 deletions(-)
create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index a5b0347ce9..bc89e3ee87 100644
--- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index a5b0347ce9..bc89e3ee87 100644
--- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index a5b0347ce9..bc89e3ee87 100644
--- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index a5b0347ce9..bc89e3ee87 100644
--- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index a5b0347ce9..bc89e3ee87 100644
--- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index a5b0347ce9..bc89e3ee87 100644
--- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index a5b0347ce9..bc89e3ee87 100644
--- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index a5b0347ce9..bc89e3ee87 100644
--- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index 6441b23078..4d1df91e4a 100644
--- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,10 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.Skill;
@@ -44,6 +40,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.network.serverpackets.StopRotation;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -53,56 +50,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
abstract class AbstractAI implements Ctrl
{
- protected static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- class FollowTask implements Runnable
- {
- protected int _range = 60;
- protected boolean newtask = true;
-
- public FollowTask()
- {
- // null
- }
-
- public FollowTask(int range)
- {
- _range = range;
- }
-
- @Override
- public void run()
- {
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final Creature follow = getFollowTarget();
- if (follow == null)
- {
- stopFollow();
- return;
- }
- if (!_actor.isInsideRadius(follow, _range, true, false))
- {
- moveToPawn(follow, _range);
- }
- else if (newtask)
- {
- newtask = false;
- _actor.broadcastPacket(new MoveToPawn(_actor, follow, _range));
- }
- }
- catch (Throwable t)
- {
- LOGGER.warning(t.getMessage());
- }
- }
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -132,10 +79,6 @@ abstract class AbstractAI implements Ctrl
/** Diferent internal state flags */
private int _moveToPawnTimeout;
- protected Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
-
/**
* Constructor of AbstractAI.
* @param accessor The AI accessor of the Creature
@@ -770,15 +713,7 @@ abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
- // Create and Launch an AI Follow Task to execute every 1s
- _followTarget = target;
- _followTask = ThreadPool.scheduleAtFixedRate(new FollowTask(), 5, FOLLOW_INTERVAL);
+ startFollow(target, -1);
}
/**
@@ -788,14 +723,16 @@ abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
_followTarget = target;
- _followTask = ThreadPool.scheduleAtFixedRate(new FollowTask(range), 5, ATTACK_FOLLOW_INTERVAL);
+ if (range == -1)
+ {
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -803,12 +740,7 @@ abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
_followTarget = null;
}
diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index ae769a8bdb..bd0cfd5d2e 100644
--- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -20,10 +20,7 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.datatables.sql.TerritoryTable;
@@ -56,6 +53,7 @@ import org.l2jmobius.gameserver.model.items.type.WeaponType;
import org.l2jmobius.gameserver.model.quest.EventType;
import org.l2jmobius.gameserver.model.quest.Quest;
import org.l2jmobius.gameserver.model.spawn.Spawn;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
/**
* This class manages AI of Attackable.
@@ -65,12 +63,8 @@ public class AttackableAI extends CreatureAI
// protected static final Logger LOGGER = Logger.getLogger(AttackableAI.class);
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
- /** The Attackable AI task executed every 1s (call onEvtThink method) */
- private Future> _aiTask;
-
/** The delay after wich the attacked is stopped */
private int _attackTimeout;
@@ -129,7 +123,7 @@ public class AttackableAI extends CreatureAI
return false;
}
- final Attackable me = (Attackable) _actor;
+ final Attackable me = getActiveChar();
// Check if the target isn't invulnerable
if (target.isInvul())
@@ -293,20 +287,12 @@ public class AttackableAI extends CreatureAI
public synchronized void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
public synchronized void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
}
@Override
@@ -331,7 +317,7 @@ public class AttackableAI extends CreatureAI
// Check if actor is not dead
if (!_actor.isAlikeDead())
{
- final Attackable npc = (Attackable) _actor;
+ final Attackable npc = getActiveChar();
// If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
if (npc.getKnownList().getKnownPlayers().size() > 0)
@@ -387,7 +373,7 @@ public class AttackableAI extends CreatureAI
*/
private void thinkActive()
{
- final Attackable npc = (Attackable) _actor;
+ final Attackable npc = getActiveChar();
// Update every 1s the _globalAggro counter to come close to 0
if (_globalAggro != 0)
@@ -687,7 +673,7 @@ public class AttackableAI extends CreatureAI
// Stop hating this target after the attack timeout or if target is dead
if (originalAttackTarget != null)
{
- ((Attackable) _actor).stopHating(originalAttackTarget);
+ (getActiveChar()).stopHating(originalAttackTarget);
}
// Set the AI Intention to AI_INTENTION_ACTIVE
@@ -860,7 +846,7 @@ public class AttackableAI extends CreatureAI
}
else
{
- hated = ((Attackable) _actor).getMostHated();
+ hated = (getActiveChar()).getMostHated();
}
if (hated == null)
@@ -1010,7 +996,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || _actor.isAllSkillsDisabled())
@@ -1062,7 +1048,7 @@ public class AttackableAI extends CreatureAI
}
// Add the attacker to the _aggroList of the actor
- ((Attackable) _actor).addDamageHate(attacker, 0, 1);
+ (getActiveChar()).addDamageHate(attacker, 0, 1);
// Set the Creature movement type to run and send Server->Client packet ChangeMoveType to all others PlayerInstance
if (!_actor.isRunning())
@@ -1077,7 +1063,7 @@ public class AttackableAI extends CreatureAI
{
setIntention(AI_INTENTION_ATTACK, attacker);
}
- else if (((Attackable) _actor).getMostHated() != getAttackTarget())
+ else if ((getActiveChar()).getMostHated() != getAttackTarget())
{
setIntention(AI_INTENTION_ATTACK, attacker);
}
@@ -1098,7 +1084,7 @@ public class AttackableAI extends CreatureAI
@Override
protected void onEvtAggression(Creature target, int aggro)
{
- final Attackable me = (Attackable) _actor;
+ final Attackable me = getActiveChar();
// To avoid lag issue
if (me.isDead())
@@ -1140,4 +1126,9 @@ public class AttackableAI extends CreatureAI
{
_globalAggro = value;
}
+
+ public Attackable getActiveChar()
+ {
+ return (Attackable) _actor;
+ }
}
diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index bc68407fa3..3d0160a074 100644
--- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index 6f9ef58e1c..493e7dcbaa 100644
--- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -537,7 +537,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
@@ -934,7 +934,7 @@ public class CreatureAI extends AbstractAI
// Get the distance between the current position of the Creature and the target (x,y)
if (target == null)
{
- LOGGER.warning("maybeMoveToPawn: target == NULL!");
+ // LOGGER.warning("maybeMoveToPawn: target == NULL!");
return false;
}
diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/DoorAI.java
index 22574e6537..3a1ab99767 100644
--- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -91,7 +91,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// null;
}
diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/FortSiegeGuardAI.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/FortSiegeGuardAI.java
index 572987957f..c0bf68475f 100644
--- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/FortSiegeGuardAI.java
+++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/FortSiegeGuardAI.java
@@ -819,7 +819,7 @@ public class FortSiegeGuardAI extends CreatureAI implements Runnable
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
// setIntention(AI_INTENTION_IDLE);
diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/NpcWalkerAI.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/NpcWalkerAI.java
index ccc3cb50bc..90c076a9b1 100644
--- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/NpcWalkerAI.java
+++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/NpcWalkerAI.java
@@ -78,7 +78,7 @@ public class NpcWalkerAI extends CreatureAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (!Config.ALLOW_NPC_WALKERS)
{
@@ -104,7 +104,7 @@ public class NpcWalkerAI extends CreatureAI implements Runnable
@Override
protected void onEvtArrivedBlocked(Location location)
{
- LOGGER.warning("NpcWalker ID: " + getActor().getNpcId() + ": Blocked at rote position [" + _currentPos + "], coords: " + location.getX() + ", " + location.getY() + ", " + location.getZ() + ". Teleporting to next point");
+ // LOGGER.warning("NpcWalker ID: " + getActor().getNpcId() + ": Blocked at rote position [" + _currentPos + "], coords: " + location.getX() + ", " + location.getY() + ", " + location.getZ() + ". Teleporting to next point");
if (_route.size() <= _currentPos)
{
@@ -141,7 +141,7 @@ public class NpcWalkerAI extends CreatureAI implements Runnable
}
catch (ArrayIndexOutOfBoundsException e)
{
- LOGGER.info("NpcWalkerInstance: Error, " + e);
+ // LOGGER.info("NpcWalkerInstance: Error, " + e);
}
}
diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index c19d53cb82..674af0bd6c 100644
--- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -292,7 +292,7 @@ public class PlayerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/SiegeGuardAI.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/SiegeGuardAI.java
index ec744e2e81..26c4b8468c 100644
--- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/SiegeGuardAI.java
+++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/SiegeGuardAI.java
@@ -629,7 +629,7 @@ public class SiegeGuardAI extends CreatureAI implements Runnable
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || _actor.isAllSkillsDisabled())
diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/SummonAI.java
index cd9c9b9fa7..c87444d1df 100644
--- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -151,7 +151,7 @@ public class SummonAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..d7cf7d2c6f
--- /dev/null
+++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getFollowTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius(followTarget, followRange, true, false))
+ {
+ if (!creature.isInsideRadius(followTarget, 3000, true, false))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c4f8bdd8e7..5ea2d3dcf3 100644
--- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,10 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -41,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.network.serverpackets.StopRotation;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -49,84 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- protected final Logger LOGGER = Logger.getLogger(getClass().getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
- private class FollowTask implements Runnable
- {
- protected int _range = 70;
-
- public FollowTask()
- {
- }
-
- public FollowTask(int range)
- {
- _range = range;
- }
-
- @Override
- public void run()
- {
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final Creature followTarget = _followTarget; // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, _range))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, _range);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning(getClass().getSimpleName() + ": Error: " + e.getMessage());
- }
- }
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -156,9 +75,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
private int _moveToPawnTimeout;
- protected Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -554,7 +487,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled())
@@ -829,15 +762,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
- // Create and Launch an AI Follow Task to execute every 1s
- _followTarget = target;
- _followTask = ThreadPool.scheduleAtFixedRate(new FollowTask(), 5, FOLLOW_INTERVAL);
+ startFollow(target, -1);
}
/**
@@ -847,14 +772,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
_followTarget = target;
- _followTask = ThreadPool.scheduleAtFixedRate(new FollowTask(range), 5, ATTACK_FOLLOW_INTERVAL);
+ if (range == -1)
+ {
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -862,12 +789,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
_followTarget = null;
}
diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 92374af181..cbf0a1b623 100644
--- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -63,6 +63,7 @@ import org.l2jmobius.gameserver.model.skills.AbnormalVisualEffect;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.targets.TargetType;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -71,8 +72,6 @@ import org.l2jmobius.gameserver.util.Util;
*/
public class AttackableAI extends CreatureAI
{
- // private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
-
/**
* Fear task.
* @author Zoey76
@@ -105,10 +104,7 @@ public class AttackableAI extends CreatureAI
protected static final int FEAR_TICKS = 5;
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /** The Attackable AI task executed every 1s (call onEvtThink method). */
- private Future> _aiTask;
/** The delay after which the attacked is stopped. */
private int _attackTimeout;
/** The Attackable aggro counter. */
@@ -316,21 +312,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -2235,7 +2223,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index 0c751502b2..f75bf06069 100644
--- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index 23a8475bdd..d44b9d7eb8 100644
--- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -428,7 +428,7 @@ public class CreatureAI extends AbstractAI
setTarget(object);
if ((object.getX() == 0) && (object.getY() == 0))
{
- LOGGER.warning("Object in coords 0,0 - using a temporary fix");
+ // LOGGER.warning("Object in coords 0,0 - using a temporary fix");
object.setXYZ(getActor().getX(), getActor().getY(), getActor().getZ() + 5);
}
@@ -479,7 +479,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
@@ -932,7 +932,7 @@ public class CreatureAI extends AbstractAI
{
if (worldPosition == null)
{
- LOGGER.warning("maybeMoveToPosition: worldPosition == NULL!");
+ // LOGGER.warning("maybeMoveToPosition: worldPosition == NULL!");
return false;
}
@@ -1002,7 +1002,7 @@ public class CreatureAI extends AbstractAI
// Get the distance between the current position of the Creature and the target (x,y)
if (target == null)
{
- LOGGER.warning("maybeMoveToPawn: target == NULL!");
+ // LOGGER.warning("maybeMoveToPawn: target == NULL!");
return false;
}
if (offset < 0)
diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/DoorAI.java
index 05a2b9a037..0483bd56e9 100644
--- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -82,7 +82,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/FortSiegeGuardAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/FortSiegeGuardAI.java
index e0a2a2ca40..01975ffd18 100644
--- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/FortSiegeGuardAI.java
+++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/FortSiegeGuardAI.java
@@ -630,7 +630,7 @@ public class FortSiegeGuardAI extends CreatureAI implements Runnable
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
// setIntention(AI_INTENTION_IDLE);
diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 9c04c1a935..51fda8cc38 100644
--- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -332,7 +332,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SiegeGuardAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SiegeGuardAI.java
index 6ccc374eb9..4fffd55c7c 100644
--- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SiegeGuardAI.java
+++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SiegeGuardAI.java
@@ -633,7 +633,7 @@ public class SiegeGuardAI extends CreatureAI implements Runnable
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
// setIntention(AI_INTENTION_IDLE);
diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 062f2bce3a..217d288f9d 100644
--- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -154,7 +154,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 4accd15729..f44f8fc783 100644
--- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -116,7 +116,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..947939ce1e
--- /dev/null
+++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getFollowTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c4f8bdd8e7..5ea2d3dcf3 100644
--- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,10 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -41,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.network.serverpackets.StopRotation;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -49,84 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- protected final Logger LOGGER = Logger.getLogger(getClass().getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
- private class FollowTask implements Runnable
- {
- protected int _range = 70;
-
- public FollowTask()
- {
- }
-
- public FollowTask(int range)
- {
- _range = range;
- }
-
- @Override
- public void run()
- {
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final Creature followTarget = _followTarget; // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, _range))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, _range);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning(getClass().getSimpleName() + ": Error: " + e.getMessage());
- }
- }
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -156,9 +75,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
private int _moveToPawnTimeout;
- protected Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -554,7 +487,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled())
@@ -829,15 +762,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
- // Create and Launch an AI Follow Task to execute every 1s
- _followTarget = target;
- _followTask = ThreadPool.scheduleAtFixedRate(new FollowTask(), 5, FOLLOW_INTERVAL);
+ startFollow(target, -1);
}
/**
@@ -847,14 +772,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
_followTarget = target;
- _followTask = ThreadPool.scheduleAtFixedRate(new FollowTask(range), 5, ATTACK_FOLLOW_INTERVAL);
+ if (range == -1)
+ {
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -862,12 +789,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
_followTarget = null;
}
diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 92374af181..cbf0a1b623 100644
--- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -63,6 +63,7 @@ import org.l2jmobius.gameserver.model.skills.AbnormalVisualEffect;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.targets.TargetType;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -71,8 +72,6 @@ import org.l2jmobius.gameserver.util.Util;
*/
public class AttackableAI extends CreatureAI
{
- // private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
-
/**
* Fear task.
* @author Zoey76
@@ -105,10 +104,7 @@ public class AttackableAI extends CreatureAI
protected static final int FEAR_TICKS = 5;
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /** The Attackable AI task executed every 1s (call onEvtThink method). */
- private Future> _aiTask;
/** The delay after which the attacked is stopped. */
private int _attackTimeout;
/** The Attackable aggro counter. */
@@ -316,21 +312,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -2235,7 +2223,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index 0c751502b2..f75bf06069 100644
--- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index 23a8475bdd..d44b9d7eb8 100644
--- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -428,7 +428,7 @@ public class CreatureAI extends AbstractAI
setTarget(object);
if ((object.getX() == 0) && (object.getY() == 0))
{
- LOGGER.warning("Object in coords 0,0 - using a temporary fix");
+ // LOGGER.warning("Object in coords 0,0 - using a temporary fix");
object.setXYZ(getActor().getX(), getActor().getY(), getActor().getZ() + 5);
}
@@ -479,7 +479,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
@@ -932,7 +932,7 @@ public class CreatureAI extends AbstractAI
{
if (worldPosition == null)
{
- LOGGER.warning("maybeMoveToPosition: worldPosition == NULL!");
+ // LOGGER.warning("maybeMoveToPosition: worldPosition == NULL!");
return false;
}
@@ -1002,7 +1002,7 @@ public class CreatureAI extends AbstractAI
// Get the distance between the current position of the Creature and the target (x,y)
if (target == null)
{
- LOGGER.warning("maybeMoveToPawn: target == NULL!");
+ // LOGGER.warning("maybeMoveToPawn: target == NULL!");
return false;
}
if (offset < 0)
diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/DoorAI.java
index 05a2b9a037..0483bd56e9 100644
--- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -82,7 +82,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/FortSiegeGuardAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/FortSiegeGuardAI.java
index e0a2a2ca40..01975ffd18 100644
--- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/FortSiegeGuardAI.java
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/FortSiegeGuardAI.java
@@ -630,7 +630,7 @@ public class FortSiegeGuardAI extends CreatureAI implements Runnable
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
// setIntention(AI_INTENTION_IDLE);
diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 9c04c1a935..51fda8cc38 100644
--- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -332,7 +332,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SiegeGuardAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SiegeGuardAI.java
index 6ccc374eb9..4fffd55c7c 100644
--- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SiegeGuardAI.java
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SiegeGuardAI.java
@@ -633,7 +633,7 @@ public class SiegeGuardAI extends CreatureAI implements Runnable
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
// setIntention(AI_INTENTION_IDLE);
diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 062f2bce3a..217d288f9d 100644
--- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -154,7 +154,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 4accd15729..f44f8fc783 100644
--- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -116,7 +116,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..947939ce1e
--- /dev/null
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getFollowTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index 3b29875b4a..30cb3186ba 100644
--- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index 3b29875b4a..30cb3186ba 100644
--- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index 3b29875b4a..30cb3186ba 100644
--- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index 3b29875b4a..30cb3186ba 100644
--- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index 3b29875b4a..30cb3186ba 100644
--- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 6075092cb6..a7e0b7c617 100644
--- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index 3b29875b4a..30cb3186ba 100644
--- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/AbstractAI.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/AbstractAI.java
index c544548976..3620a8814e 100644
--- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/AbstractAI.java
+++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/AbstractAI.java
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
-import org.l2jmobius.commons.concurrent.ThreadPool;
-import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.WorldObject;
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
+import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
/**
* Mother class of all objects AI in the world.
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
*/
public abstract class AbstractAI implements Ctrl
{
- private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
-
- private NextAction _nextAction;
-
- /**
- * @return the _nextAction
- */
- public NextAction getNextAction()
- {
- return _nextAction;
- }
-
- /**
- * @param nextAction the next action to set.
- */
- public void setNextAction(NextAction nextAction)
- {
- _nextAction = nextAction;
- }
-
/** The creature that this AI manages */
protected final Creature _actor;
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
/** Different internal state flags */
protected int _moveToPawnTimeout;
- private Future> _followTask = null;
- private static final int FOLLOW_INTERVAL = 1000;
- private static final int ATTACK_FOLLOW_INTERVAL = 500;
+ private NextAction _nextAction;
+
+ /**
+ * @return the _nextAction
+ */
+ public NextAction getNextAction()
+ {
+ return _nextAction;
+ }
+
+ /**
+ * @param nextAction the next action to set.
+ */
+ public void setNextAction(NextAction nextAction)
+ {
+ _nextAction = nextAction;
+ }
protected AbstractAI(Creature creature)
{
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
* @param pawn
* @param offset
*/
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void startFollow(Creature target, int range)
{
- if (_followTask != null)
- {
- _followTask.cancel(false);
- _followTask = null;
- }
-
+ stopFollow();
setTarget(target);
-
- final int followRange = range == -1 ? Rnd.get(50, 100) : range;
- _followTask = ThreadPool.scheduleAtFixedRate(() ->
+ if (range == -1)
{
- try
- {
- if (_followTask == null)
- {
- return;
- }
-
- final WorldObject followTarget = getTarget(); // copy to prevent NPE
- if (followTarget == null)
- {
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- if (!_actor.isInsideRadius3D(followTarget, followRange))
- {
- if (!_actor.isInsideRadius3D(followTarget, 3000))
- {
- // if the target is too far (maybe also teleported)
- if (_actor.isSummon())
- {
- ((Summon) _actor).setFollowStatus(false);
- }
-
- setIntention(AI_INTENTION_IDLE);
- return;
- }
-
- moveToPawn(followTarget, followRange);
- }
- }
- catch (Exception e)
- {
- LOGGER.warning("Error: " + e.getMessage());
- }
- }, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
+ CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
+ }
+ else
+ {
+ CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
+ }
}
/**
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
*/
public synchronized void stopFollow()
{
- if (_followTask != null)
- {
- // Stop the Follow Task
- _followTask.cancel(false);
- _followTask = null;
- }
+ CreatureFollowTaskManager.getInstance().remove(_actor);
}
public void setTarget(WorldObject target)
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/AttackableAI.java
index 86871742ad..4104e382a0 100644
--- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/AttackableAI.java
+++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/AttackableAI.java
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.l2jmobius.Config;
-import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.GameTimeController;
import org.l2jmobius.gameserver.enums.AISkillScope;
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.skills.SkillCaster;
import org.l2jmobius.gameserver.model.zone.ZoneId;
+import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
import org.l2jmobius.gameserver.util.Util;
/**
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
private static final int RANDOM_WALK_RATE = 30; // confirmed
- // private static final int MAX_DRIFT_RANGE = 300;
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
- /**
- * The Attackable AI task executed every 1s (call onEvtThink method).
- */
- private Future> _aiTask;
/**
* The delay after which the attacked is stopped.
*/
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
public void startAITask()
{
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
- }
+ AttackableThinkTaskManager.getInstance().add(getActiveChar());
}
@Override
public void stopAITask()
{
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
+ AttackableThinkTaskManager.getInstance().remove(getActiveChar());
super.stopAITask();
}
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
* Manage AI thinking actions of a Attackable.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || getActiveChar().isAllSkillsDisabled())
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
index f8a9a42516..4a0f9f822f 100644
--- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
+++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/ControllableMobAI.java
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_isThinking)
{
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/CreatureAI.java
index 3b29875b4a..30cb3186ba 100644
--- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/CreatureAI.java
+++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/CreatureAI.java
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
* Do nothing.
*/
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
// do nothing
}
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/DoorAI.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/DoorAI.java
index dca1a5de56..f21c7e4d7d 100644
--- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/DoorAI.java
+++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/DoorAI.java
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
}
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
index 3b3931ce9b..6cab2aa265 100644
--- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
+++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/DoppelgangerAI.java
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/PlayerAI.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/PlayerAI.java
index 72cd985005..93b7c8cccf 100644
--- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/PlayerAI.java
+++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/PlayerAI.java
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/SummonAI.java
index 78040622d9..541ea37f21 100644
--- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/SummonAI.java
+++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/SummonAI.java
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
- protected void onEvtThink()
+ public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/VehicleAI.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/VehicleAI.java
index 303731ae71..40a5af5d63 100644
--- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/VehicleAI.java
+++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/VehicleAI.java
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
}
@Override
- protected void moveToPawn(WorldObject pawn, int offset)
+ public void moveToPawn(WorldObject pawn, int offset)
{
}
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
new file mode 100644
index 0000000000..25e2a371f8
--- /dev/null
+++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/taskmanager/AttackableThinkTaskManager.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.actor.Attackable;
+
+/**
+ * @author Mobius
+ */
+public class AttackableThinkTaskManager
+{
+ private static final Set ATTACKABLES = ConcurrentHashMap.newKeySet();
+ private static boolean _working = false;
+
+ public AttackableThinkTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_working)
+ {
+ return;
+ }
+ _working = true;
+
+ CreatureAI ai;
+ for (Attackable attackable : ATTACKABLES)
+ {
+ if (attackable.hasAI())
+ {
+ ai = attackable.getAI();
+ if (ai != null)
+ {
+ ai.onEvtThink();
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+ else
+ {
+ remove(attackable);
+ }
+ }
+
+ _working = false;
+ }, 1000, 1000);
+ }
+
+ public void add(Attackable attackable)
+ {
+ if (!ATTACKABLES.contains(attackable))
+ {
+ ATTACKABLES.add(attackable);
+ }
+ }
+
+ public void remove(Attackable attackable)
+ {
+ ATTACKABLES.remove(attackable);
+ }
+
+ public static AttackableThinkTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
+ }
+}
diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
new file mode 100644
index 0000000000..a70fa101ce
--- /dev/null
+++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/taskmanager/CreatureFollowTaskManager.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.l2jmobius.gameserver.taskmanager;
+
+import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.l2jmobius.commons.concurrent.ThreadPool;
+import org.l2jmobius.commons.util.Rnd;
+import org.l2jmobius.gameserver.ai.CreatureAI;
+import org.l2jmobius.gameserver.model.WorldObject;
+import org.l2jmobius.gameserver.model.actor.Creature;
+import org.l2jmobius.gameserver.model.actor.Summon;
+
+/**
+ * @author Mobius
+ */
+public class CreatureFollowTaskManager
+{
+ private static final Map NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static final Map ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
+ private static boolean _workingNormal = false;
+ private static boolean _workingAttack = false;
+
+ public CreatureFollowTaskManager()
+ {
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingNormal)
+ {
+ return;
+ }
+ _workingNormal = true;
+
+ for (Entry entry : NORMAL_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingNormal = false;
+ }, 1000, 1000);
+
+ ThreadPool.scheduleAtFixedRate(() ->
+ {
+ if (_workingAttack)
+ {
+ return;
+ }
+ _workingAttack = true;
+
+ for (Entry entry : ATTACK_FOLLOW_CREATURES.entrySet())
+ {
+ follow(entry.getKey(), entry.getValue());
+ }
+
+ _workingAttack = false;
+ }, 500, 500);
+ }
+
+ private void follow(Creature creature, int range)
+ {
+ if (creature.hasAI())
+ {
+ final CreatureAI ai = creature.getAI();
+ if (ai != null)
+ {
+ final WorldObject followTarget = ai.getTarget();
+ if (followTarget == null)
+ {
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+
+ final int followRange = range == -1 ? Rnd.get(50, 100) : range;
+ if (!creature.isInsideRadius3D(followTarget, followRange))
+ {
+ if (!creature.isInsideRadius3D(followTarget, 3000))
+ {
+ // If the target is too far (maybe also teleported).
+ if (creature.isSummon())
+ {
+ ((Summon) creature).setFollowStatus(false);
+ }
+ ai.setIntention(AI_INTENTION_IDLE);
+ return;
+ }
+ ai.moveToPawn(followTarget, followRange);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+ else
+ {
+ remove(creature);
+ }
+ }
+
+ public void addNormalFollow(Creature creature, int range)
+ {
+ NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void addAttackFollow(Creature creature, int range)
+ {
+ ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
+ }
+
+ public void remove(Creature creature)
+ {
+ NORMAL_FOLLOW_CREATURES.remove(creature);
+ ATTACK_FOLLOW_CREATURES.remove(creature);
+ }
+
+ public static CreatureFollowTaskManager getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
+ }
+}