Addition of AI related task managers.

This commit is contained in:
MobiusDevelopment
2020-03-30 16:49:57 +00:00
parent d094486cc3
commit c9601fac2a
201 changed files with 5014 additions and 2037 deletions

View File

@@ -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.<br>
@@ -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;
}

View File

@@ -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())

View File

@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
}
@Override
protected void onEvtThink()
public void onEvtThink()
{
if (_isThinking)
{

View File

@@ -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)

View File

@@ -82,7 +82,7 @@ public class DoorAI extends CreatureAI
}
@Override
protected void onEvtThink()
public void onEvtThink()
{
}

View File

@@ -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);

View File

@@ -332,7 +332,7 @@ public class PlayerAI extends PlayableAI
}
@Override
protected void onEvtThink()
public void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{

View File

@@ -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);

View File

@@ -154,7 +154,7 @@ public class SummonAI extends PlayableAI implements Runnable
}
@Override
protected void onEvtThink()
public void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{

View File

@@ -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)
{
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<Attackable> 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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<Creature, Integer> NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
private static final Map<Creature, Integer> 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<Creature, Integer> entry : NORMAL_FOLLOW_CREATURES.entrySet())
{
follow(entry.getKey(), entry.getValue());
}
_workingNormal = false;
}, 1000, 1000);
ThreadPool.scheduleAtFixedRate(() ->
{
if (_workingAttack)
{
return;
}
_workingAttack = true;
for (Entry<Creature, Integer> 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();
}
}