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(); + } +}