Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * 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 com.l2jserver.gameserver.ai.ext.tasks;
- import com.l2jserver.gameserver.ai.L2CharacterAI;
- import com.l2jserver.gameserver.ai.ext.AI_TaskQueue;
- import com.l2jserver.gameserver.ai.ext.struct.task.AITaskSimpleVar;
- import com.l2jserver.gameserver.idfactory.IdFactory;
- import com.l2jserver.gameserver.model.actor.L2Character;
- import com.l2jserver.gameserver.util.DynamicExtension;
- import com.l2jserver.util.Rnd;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.InvocationTargetException;
- import java.util.concurrent.locks.ReentrantLock;
- import java.util.logging.Logger;
- /**
- * ==============================================<br>
- * AbstractAITask - Abstract constuct of AI Task.<br>
- *
- * AI Tasks, are small routines, that AI can execute when request is made and conditions are
- * met. There are three types of tasks: random (chance of execution depends on task's actual weight score,
- * comparing to other queued random tasks), generic fifo/lifo (always executed one task / AI main thread cycle, and
- * after successfull execution, task is removed from queue) and cyclic random / generic tasks (cyclic tasks are
- * not removed from task queue, until "removable()" condition is true)
- * ==============================================<br>
- * @author Deedlit(deedlit@protonmail.com)
- */
- public abstract class AbstractAITask
- {
- protected static final Logger _log = Logger.getLogger(AbstractAITask.class.getName());
- public static final int MAX_RND_TASK_WEIGHT = 100000;
- public enum AI_TaskMaxInstancesExceededAction
- {
- NONE,
- OVERWRITE_LAST,
- OVERWRITE_FIRST,
- OVERWRITE_LAST_EXECUTED(true),
- OVERWRITE_FIRST_EXECUTED(true),
- WAIT,
- WAIT_EXECUTED(true);
- private boolean executed;
- AI_TaskMaxInstancesExceededAction()
- {
- this(false);
- }
- AI_TaskMaxInstancesExceededAction(boolean executed)
- {
- this.executed = executed;
- }
- public boolean executed()
- {
- return executed;
- }
- }
- /* ====================== FIELDS ================================ */
- private final L2Character _actor;
- private L2Character _Eactor = null;
- private Runnable _task;
- private boolean _result = false;
- private boolean _executed = false;
- private boolean _cyclic = false;
- private boolean _random = false;
- private int _taskId = 0;
- private long _execTs = 0;
- private long _createdTs = 0;
- private volatile long _weightUpdateTs = 0;
- private int _initialWeight = 1;
- private volatile int _currentWeight = 1;
- private int _execCount = 0;
- private long _lastExecTs = 0;
- private volatile boolean _released = true;
- protected int addedOnTaskRunCycle = -1;
- private AI_TaskQueue _queue = null;
- private ReentrantLock _lock = new ReentrantLock(true);
- protected String _name;
- /* ====================== FIELDS ================================ */
- /* ====================== CONSTRUCTORS ========================== */
- /**
- * Initializes random task, that can be set to run in cycles, with specified initial weight.
- * @param queue specified task queue, to which this task will be assigned, managed and handled by.
- * @param initialWeight
- * @param cyclic
- * @param actor
- */
- public AbstractAITask(AI_TaskQueue queue, int initialWeight, boolean cyclic, L2Character actor)
- {
- this(queue, 0, true, cyclic, initialWeight, actor);
- }
- /**
- * Initializes random task, that can be set to run in cycles, with specified initial weight.
- * @param initialWeight
- * @param cyclic
- * @param actor
- */
- public AbstractAITask(int initialWeight, boolean cyclic, L2Character actor)
- {
- this(actor.getTaskQueue(), 0, true, cyclic, initialWeight, actor);
- }
- /**
- * Initializes single run, random task with specified initial weight.
- * @param queue specified task queue, to which this task will be assigned, managed and handled by.
- * @param initialWeight
- * @param actor
- */
- public AbstractAITask(AI_TaskQueue queue, int initialWeight, L2Character actor)
- {
- this(queue, 0, true, false, initialWeight, actor);
- }
- /**
- * Initializes single run, random task with specified initial weight.
- * @param initialWeight
- * @param actor
- */
- public AbstractAITask(int initialWeight, L2Character actor)
- {
- this(actor.getTaskQueue(), 0, true, false, initialWeight, actor);
- }
- /**
- * Initializes general type task, single or cyclic execution mode.
- * @param queue specified task queue, to which this task will be assigned, managed and handled by.
- * @param cyclic
- * @param actor
- */
- public AbstractAITask(AI_TaskQueue queue, boolean cyclic, L2Character actor)
- {
- this(queue, 0, false, cyclic, 0, actor);
- }
- /**
- * Initializes general type task, single or cyclic execution mode.
- * @param cyclic
- * @param actor
- */
- public AbstractAITask(boolean cyclic, L2Character actor)
- {
- this(actor.getTaskQueue(), 0, false, cyclic, 0, actor);
- }
- /**
- * Initializes general type task. Single execution mode.
- * @param queue specified task queue, to which this task will be assigned, managed and handled by.
- * @param actor
- */
- public AbstractAITask(AI_TaskQueue queue, L2Character actor)
- {
- this(queue, 0, false, false, 0, actor);
- }
- /**
- * Initializes general type task. Single execution mode.
- * @param actor
- */
- public AbstractAITask(L2Character actor)
- {
- this(actor.getTaskQueue(), 0, false, false, 0, actor);
- }
- /**
- * Create AI task.
- * @param queue specified task queue, to which this task will be assigned, managed and handled by.
- * @param taskUniqueId <0 to assign automatically from IdFactory pool. NOTE: TaskQueue for tasks, that do not have assigned ID, will automatically assign one, on addition to TaskQueue. To
- * reduce CPU utilization, it's preferred, that you create tasks without assigned id, to perform basic validation.
- * @param random true if this task is "random" task type (AI TaskQueue manager, will randomly execute one random task; chance
- * depends on task's current "weight"; max. weight = AbstractAI.MAX_RND_TASK_WEIGHT)
- * @param cyclic true if this is an cyclic task (will be executed on every cycle); false if this task should be executed only once and
- * then removed from pool (or reinitialized)
- * @param initialWeight initial task "weight". Used by "random" task type only, to determine initial random chance for task to be
- * randomly executed. Min. is 1, max is AbstractAI.MAX_RND_TASK_WEIGHT.
- * @param actor character
- */
- public AbstractAITask(AI_TaskQueue queue, int taskUniqueId, boolean random, boolean cyclic, int initialWeight, L2Character actor)
- {
- _queue = queue;
- _taskId = taskUniqueId;
- if (_taskId < 0)
- getNewId();
- _actor = actor;
- _Eactor = actor;
- _createdTs = System.currentTimeMillis();
- _cyclic = cyclic;
- _random = random;
- if (_random)
- {
- setWeight(initialWeight, true);
- setWeight(initialWeight, false);
- }
- initTaskTypeName();
- }
- /* ====================== CONSTRUCTORS ========================== */
- //----------------------------------- MAIN CLASS ----------------------------------------------//
- protected void initTaskTypeName()
- {
- _name = getClass().getSimpleName();
- }
- /**
- * Returns task type name (by default task class name).
- * @return
- */
- public String getTaskTypeName()
- {
- return _name;
- }
- /**
- * Access task queue object.
- * @return
- */
- public final AI_TaskQueue getTaskQueue()
- {
- return _queue;
- }
- /**
- * Set task's Task Queue that will handle execution.
- * @param queue
- */
- public final void setTaskQueue(AI_TaskQueue queue)
- {
- _queue = queue;
- }
- /**
- * Check if owner has now active AI.
- * @return
- */
- public boolean hasAI()
- {
- return getActor().hasAI();
- }
- /**
- * Access owner's AI.
- * @return
- */
- public L2CharacterAI getAI()
- {
- if (hasAI())
- return getActor().getAI();
- return null;
- }
- /**
- * Access actor (owner) of task AI.
- * @return
- */
- public L2Character getActor()
- {
- if (_actor == null)
- return _Eactor;
- return _actor;
- }
- /**
- * Executed by AI task manager.
- * @param useThreadPoolManager
- */
- public final boolean runThread()
- {
- if (_lock.tryLock())
- {
- try
- {
- _executed = true;
- _execTs = System.currentTimeMillis();
- _execCount++;
- getTaskQueue().registerTaskTypeExecCountAndTimestamp(hashCode());
- _result = run();
- return true;
- }
- catch (Exception e)
- {
- }
- finally
- {
- _lock.unlock();
- }
- }
- return false;
- }
- /**
- * Returns timestamp of task creation.
- * @return
- */
- public final long getTaskCreateTimestamp()
- {
- return _createdTs;
- }
- /**
- * Returns timestamp of last execution of task.
- * @param onlyThisTask true - will return counter of exacly this task; false - will return AI TaskQueue global counter, for all tasks of this type.
- * @return
- */
- public final long getTaskLastExecTimestamp(boolean onlyThisTask)
- {
- if (onlyThisTask)
- return _execTs > 0 ? _execTs : _lastExecTs;
- return getTaskQueue().getTaskTypeLastExecTimestamp(hashCode());
- }
- /**
- * Returns timestamp of last succesfull weight_update();
- * @return
- */
- public long getLastWeightUpdateTimestamp()
- {
- return _weightUpdateTs;
- }
- /**
- * Returns time (millis), since last execution of task (successfull or not), or -1 if task not yet executed.
- * @param onlyThisTask true - will return counter of exacly this task; false - will return AI TaskQueue global counter, for all tasks of this type.
- * @return
- */
- public final long calcTimeSinceTaskLastExec(boolean onlyThisTask)
- {
- if (getTaskLastExecTimestamp(onlyThisTask) > 0)
- return System.currentTimeMillis() - getTaskLastExecTimestamp(onlyThisTask);
- return -1;
- }
- /**
- * Returns time (millis), since task has been created (constructed)
- * @return
- */
- public final long calcTimeSinceTaskCreate()
- {
- if (getTaskCreateTimestamp() > 0)
- return System.currentTimeMillis() - getTaskCreateTimestamp();
- return -1;
- }
- /**
- * Holds counter for task executions.
- * @param onlyThisTask true - will return counter of exacly this task; false - will return AI TaskQueue global counter, for all tasks of this type.
- * @return
- */
- public final int getTaskExecCounter(boolean onlyThisTask)
- {
- if (onlyThisTask)
- return _execCount;
- return getTaskQueue().getTaskTypeExecCount(hashCode());
- }
- /**
- * Determine if task execution was successfull.
- * @return
- */
- public final boolean execSuccess()
- {
- return _result;
- }
- /**
- * Determine if task was executed.
- * @return
- */
- public final boolean taskExecuted()
- {
- return _executed;
- }
- /**
- * Returns task unique id, or 0 if no id assigned (task id released)
- * @return
- */
- public final int getTaskId()
- {
- return _taskId;
- }
- /**
- * Determines if task is cyclic, or removed after one execution.
- * @return
- */
- public boolean cyclic()
- {
- return _cyclic;
- }
- /**
- * Determines if task is randomized.
- * @return
- */
- public boolean random()
- {
- return _random;
- }
- /**
- * Set random task weight.
- * @param weight
- * @param initial
- */
- public void setWeight(int weight, boolean initial)
- {
- if (initial)
- _initialWeight = getWeightProperLimits(weight);
- else
- {
- _weightUpdateTs = System.currentTimeMillis();
- _currentWeight = getWeightProperLimits(weight);
- }
- }
- /**
- * Return current random task weight.
- * @param initial
- * @return
- */
- public int weight(boolean initial)
- {
- if (initial)
- return _initialWeight;
- return _currentWeight;
- }
- /**
- * Reset task "executed" and "result" states, to initial. Store latest execution time and reset it..
- */
- public void reset()
- {
- _lastExecTs = _execTs;
- _execTs = 0;
- _executed = false;
- _result = false;
- }
- /**
- * Add task to queue.
- * @param topOfQueue
- * @return
- */
- public final boolean addToQueue(boolean topOfQueue)
- {
- return getTaskQueue().addTaskToQueue(this, topOfQueue);
- }
- /**
- * Removes task from queue.
- * @return
- */
- public final boolean removeFromQueue(boolean releaseId)
- {
- return getTaskQueue().removeTaskFromQueue(this, releaseId);
- }
- /**
- * Check if this task is already queued in AI.
- * @return
- */
- public final boolean isTaskQueued()
- {
- return getTaskQueue().isTaskQueued(this);
- }
- /**
- * Release task id and return it to idfactory pool.
- */
- public final void free()
- {
- if (_released)
- return;
- _released = true;
- IdFactory.getInstance().releaseId(_taskId);
- _taskId = 0;
- }
- /**
- * Check if task is has been released.
- * @return
- */
- public final boolean released()
- {
- return _released;
- }
- /**
- * After releasing task id (free()), use getNewId() to assign new unique id for task, from idfactory.
- * @return
- */
- public final int getNewId()
- {
- if (!_released && _taskId != 0)
- return _taskId;
- _released = false;
- _taskId = IdFactory.getInstance().getNextId();
- return _taskId;
- }
- /* ========================================================================== */
- /**
- * Get task class hashCode, used to identify task classes.
- *
- * @return
- */
- @Override
- public int hashCode()
- {
- return _name.hashCode();
- }
- @Override
- public boolean equals(Object obj)
- {
- return isSameType(obj) && (!((AbstractAITask)obj).released() && !released() && ((AbstractAITask)obj).getTaskId() == getTaskId());
- }
- public boolean isSameType(Object obj)
- {
- return (obj instanceof AbstractAITask && ((AbstractAITask)obj).hashCode() == hashCode());
- }
- @Override
- public String toString()
- {
- return toString(false);
- }
- public String toString(boolean noActorInfo)
- {
- return getTaskTypeName()+":["+(!released() ? getTaskId() : "id_released")+"] type=("+(cyclic() ? "cyclic" : "single")+","+(random() ? "random{weight:"
- + weight(false)+"}" : "generic")+") valid="+valid()+" waiting="+waiting(getTaskQueue().getTaskRunCycle())+"(taskcycle="+getTaskQueue().getTaskRunCycle()+") ready="+ready()
- +" executed="+taskExecuted()+(taskExecuted() ? "{result:"+execSuccess()+"}" : "")+" exec_count="+getTaskExecCounter(true)+"/"+getTaskExecCounter(false)
- +" removable="+removeable()+" valid_run_delay="+ valid_run_delay()+" max_instances="+max_task_instances()+" hashcode="+hashCode()+(!noActorInfo ? "; actor:"+getActor() : "");
- }
- /* ========================================================================== */
- /**
- * If you wish to put task type int AI debug exception list, override this method and return true, for all task types you wish not to be logged by AI loggers.
- * @return
- */
- public boolean quiet_exec()
- {
- return false;
- }
- public void setAddedOnTaskRunCycle(int cycle)
- {
- this.addedOnTaskRunCycle = cycle;
- }
- /**
- * Check if task is waiting.
- * @return
- */
- public boolean waiting(int taskRunCycle)
- {
- if (taskRunCycle < 0 || addedOnTaskRunCycle < 0)
- return false;
- return taskRunCycle <= addedOnTaskRunCycle;
- }
- /* ====================== ABSTRACT METHODS ====================== */
- /**
- * Check if task state is valid. Validity is checked before adding task to queue, and before every task run. This method should be able to determine with as low computation, as possible, if
- * AI conditions are right, for task to be added to queue..
- * @return
- */
- public abstract boolean valid();
- /**
- * Check if task is ready to execution.
- * @return
- */
- public abstract boolean ready();
- /**
- * Abstract task procedute.
- * @return
- */
- public abstract boolean run();
- /**
- * Determine if after running sucessfully this task, onEvtThink() should be skipped in this AI thread cycle.
- * @return
- */
- public abstract boolean block_think_after_run();
- /**
- * Executed by AI, to check if weight should be updated.
- * @return
- */
- public abstract int update_weight();
- /**
- * Check if "random" type task weight can be updated at this time.
- * @return
- */
- public abstract boolean valid_update_weight_delay();
- /**
- * Determine if task should be removed from queue, after, even unsucessfull execution.
- * @return true - task will be auto removed from queue after exec (even unsuccessfull)
- */
- public abstract boolean removeable();
- /**
- * Determine if enough time has passed, for task to be run.
- * @return true if yes
- */
- public abstract boolean valid_run_delay();
- /**
- * Determine how many instances of task, can be in AI TaskQueue. Some tasks require max. 1 active instance in queue, some do not need to be limited.
- * @return number of max. instances of this task in AI TaskQueue, 0 if no instance allowed atm, or -1, if no limits.
- */
- public abstract int max_task_instances();
- // public abstract AI_TaskMaxInstancesExceededAction max_task_instances_exceeded_action();
- /**
- * Triggered by Task Queue, when task registers to listen for certain events of queue variables.
- *
- * @param event event that occured
- * @param variable variable that triggered event.
- */
- public abstract void on_variable_listener(AITaskSimpleVar.LISTENER_EVENTS event, AITaskSimpleVar variable);
- /**
- * Triggered by Task Queue, after sucessfully adding task to queue.
- *
- * @param queue
- */
- public abstract void on_added_to_queue(AI_TaskQueue queue);
- /**
- * Triggered by Task Queue, before removing task from queue, allowing performing cleanup (eg. unregistering variable listeners).
- *
- * @param queue
- */
- public abstract void on_remove_from_queue(AI_TaskQueue queue);
- /* ====================== ABSTRACT METHODS ====================== */
- public synchronized void setEActor(L2Character actor)
- {
- _Eactor = actor;
- }
- public boolean isEActor()
- {
- return _Eactor != null;
- }
- /* ====================== STATIC HELPER METHODS ================= */
- /**
- * Calculates weight score for task, using specified percentage value of max. allowed weight.
- * @param weightPercentageOfMaxWeightAllowed weight percentage (0.001 to 100.0%)
- * @return weight score for task, corresponding to provided weight (of max allowed) percentage.
- */
- public static int calculateWeightForPercentage(double weightPercentageOfMaxWeightAllowed)
- {
- if (weightPercentageOfMaxWeightAllowed <= 0)
- weightPercentageOfMaxWeightAllowed = 0.001;
- if (weightPercentageOfMaxWeightAllowed > 100)
- weightPercentageOfMaxWeightAllowed = 100.;
- return (int) Math.round((weightPercentageOfMaxWeightAllowed * (MAX_RND_TASK_WEIGHT / 100.)));
- }
- /**
- * Returns proper weight value, for provided weight.
- * @param weight task weight.
- * @return proper weight value (1 ... AbstractAI.MAX_RND_TASK_WEIGHT inclusive)
- */
- public static int getWeightProperLimits(int weight)
- {
- if (weight < 1)
- return 1;
- if (weight > MAX_RND_TASK_WEIGHT)
- return MAX_RND_TASK_WEIGHT;
- return weight;
- }
- /**
- * Generates random weight value for task, in range between provided min and max (inclusive) value. Returned weight is always
- * limited to proper weight range (1 ... AbstractAI.MAX_RND_TASK_WEIGHT), if min ... max out of range.
- * @param min
- * @param max
- * @return
- */
- public static int getRandomWeightRange(int min, int max)
- {
- return getWeightProperLimits(Rnd.qget(min, max));
- }
- /**
- * Returns chance percentage for specified <u>weight</u>.
- *
- * @param weight task weight value (0... to {@link #MAX_RND_TASK_WEIGHT})
- * @return how much of MAX_RND_TASK_WEIGHT specified weight is, in percentage (0 - 100.0)
- */
- public static double getPercentageForWeight(int weight)
- {
- return (double)weight * 100. / (double)MAX_RND_TASK_WEIGHT;
- }
- /* ====================== STATIC HELPER METHODS ================= */
- /* ===================== STATIC TASK CLASS INITIALIZERS ============== */
- /**
- * Creates new instance of AI Task.
- *
- * @param className class name of AI task (loaded with {@link DynamicExtension#getExtensionClass(String)} or available in {@link com.l2jserver.gameserver.ai.ext.tasks.*} package.
- * @param constructor_params constructor definition
- * @param params constructor parameters
- * @return instance of AbstractAITask child, or null when failed.
- *
- * @throws ClassNotFoundException
- * @throws NoSuchMethodException
- * @throws IllegalAccessException
- * @throws java.lang.reflect.InvocationTargetException
- * @throws InstantiationException
- */
- public static AbstractAITask newTaskInstance(AI_TaskQueue queue, String className, final Class<?>[] constructor_params, final Object[] params)
- throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException
- {
- if (className == null)
- throw new ClassNotFoundException((queue != null ? queue.getTaskQueueName() : "newTaskInstance")+": AI Task for class name not registered ["+className+"].");
- Class<?> n = DynamicExtension.getInstance().getExtensionClass(className);
- Constructor<?> _constructor = null;
- if (n == null) // Not found. Try within server's core.
- _constructor = Class.forName("com.l2jserver.gameserver.ai.ext.tasks." + className).getConstructor(constructor_params);
- else
- _constructor = Class.forName(n.getCanonicalName(), true, DynamicExtension.getInstance().getClassLoader()).getConstructor(constructor_params);
- if (_constructor == null)
- throw new ClassNotFoundException((queue != null ? queue.getTaskQueueName() : "newTaskInstance")+": AI Task for class name not registered ["+className+"].");
- Object tmp = _constructor.newInstance(params);
- if (tmp instanceof AbstractAITask)
- return ((AbstractAITask)tmp);
- else
- throw new UnsupportedClassVersionError((queue != null ? queue.getTaskQueueName() : "newTaskInstance")+": Can't create new AI Task instance, for class name. \""+tmp.getClass().getCanonicalName()+"\" is not an valid instance of [AbstractAITask].");
- }
- /**
- * Create new task (see {@link #newTaskInstance(com.l2jserver.gameserver.ai.ext.AI_TaskQueue, String, Class[], Object[])} ).
- *
- * @param className class name of AI task (loaded with {@link DynamicExtension#getExtensionClass(String)} or available in {@link com.l2jserver.gameserver.ai.ext.tasks.*} package.
- * @param randomTask
- * @param cyclicTask
- * @param initialWeight
- * @param cha
- * @return
- * @throws ClassNotFoundException
- * @throws InvocationTargetException
- * @throws NoSuchMethodException
- * @throws InstantiationException
- * @throws IllegalAccessException
- */
- public static AbstractAITask newTaskInstance(AI_TaskQueue queue, String className, boolean randomTask, boolean cyclicTask, int initialWeight, L2Character cha)
- throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException
- {
- return newTaskInstance(queue, className, new Class<?>[]{Class.forName("com.l2jserver.gameserver.ai.ext.AI_TaskQueue"), int.class, boolean.class, boolean.class, int.class, Class.forName("com.l2jserver.gameserver.model.actor.L2Character")},
- new Object[] {queue, 0, randomTask, cyclicTask, initialWeight, cha});
- }
- /* ===================== STATIC TASK CLASS INITIALIZERS ============== */
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement