How to schedule timed tasks in an Extension

Often times in the server-side game logic it is necessary to use timers for recurring events that are sent to the clients (e.g. the end of a turn time, npc actions, etc).

A quick solution to this problem is using the ScheduledThreadPoolExecutor class, provided in the JDK, which offers a convenient task executor backed by a pool of threads. SFS2X already runs its own instance of this Executor (wrapped in a class called TaskScheduler).

The following snippet of Java code shows how to run a looping task using the SmartFoxServer’s own TaskScheduler.

public class SchedulerTestExtension extends SFSExtension
{
    private class TaskRunner implements Runnable
    {
        private int runningCycles = 0;

        public void run()
        {
			try
			{
            	runningCycles++;
	            trace("Inside the running task. Cycle:  " + runningCycles);

	            if (runningCycles >= 10)
	            {
	                trace("Time to stop the task!");
	                taskHandle.cancel();
	            }
			}
			catch (Exception e)
			{
				// Handle exceptions here
			}
        }
    }

    // Keeps a reference to the task execution
    ScheduledFuture<?> taskHandle;

    @Override
    public void init()
    {
        SmartFoxServer sfs = SmartFoxServer.getInstance();

        // Schedule the task to run every second, with no initial delay
        taskHandle = sfs.getTaskScheduler().scheduleAtFixedRate(new TaskRunner(), 0, 1, TimeUnit.SECONDS);
    }

    @Override
    public void destroy()
    {
        if (taskHandle != null)
           taskHandle.cancel();
    }

}

The scheduleAtFixedRate method takes four arguments:

  1. a Runnable object that will execute the Task’s code;
  2. the initial delay before the execution starts;
  3. the interval at which the task will be executed;
  4. the time unit used to express the time values.

The Scheduler also exposes a schedule method that executes a Runnable task once after the specified amount of time. Finally the Scheduler’s thread pool can be resized on-the-fly at runtime via the resizeThreadPool() method.

IMPORTANT: runtime exceptions occurring inside the run() method will stop the Task from running. If don’t want your Task to be interrupted by an expected error make sure to catch any Exception that might occur.

Finally notice how we keep track of the started task via the taskHandle variable and make sure to stop it when the Extension is removed, via the destroy() method. This is important to avoid leaking the task in memory.

NOTE:
The initial size of the system TaskScheduler’s thread pool can be adjusted via the Server Configurator module in the AdminTool.