Scheduler

Schedule delayed tasks, repeat actions on a timer, run work off the main thread, and build sequential chains — all with a clean, fluent API.

Package: fr.eri.eriapi.scheduler Minecraft 1.12.2 Java 8

Introduction

The Scheduler module gives you full control over when your code runs. Whether you need to trigger something after a short delay, repeat an action every few seconds, perform expensive work off the main game thread, or sequence a series of timed steps — the Scheduler covers all of these scenarios without requiring you to manage threads or tick counters manually.

Core concepts

  • Delay — run code once after N ticks (20 ticks = 1 second)
  • Repeat — run code every N ticks, optionally for a fixed number of times
  • Async — run a Callable off the main thread, then handle the result back on the main thread
  • Chain — execute a sequence of tasks, each after a specified delay from the previous
  • Cancel / Pause / Resume — manage any running task via its ScheduledTask handle
Tick-based timing

Minecraft runs at 20 ticks per second (TPS). All durations in the Scheduler are expressed in ticks. Use 20 for one second, 200 for ten seconds, and so on.

EriScheduler — Static API

EriScheduler is the main entry point. All methods are static, so you never need to instantiate anything. Every scheduling call returns a ScheduledTask that you can use to pause, resume, or cancel the task later.

Delayed execution

Run a block of code once after a given number of ticks:

Java — delay
// Execute once after 20 ticks (1 second)
ScheduledTask task = EriScheduler.delay(20, () -> {
    player.sendMessage(new TextComponentString("1 second later!"));
});

Fixed-count repetition

Run a block every N ticks for a maximum number of executions:

Java — repeat (limited)
// Run every 20 ticks, up to 5 times
ScheduledTask task = EriScheduler.repeat(20, 5, () -> {
    System.out.println("Tick!");
});

Infinite repetition

Run a block every N ticks indefinitely until cancelled:

Java — repeat (infinite)
// Run every 100 ticks (5 seconds) forever
ScheduledTask task = EriScheduler.repeat(100, () -> {
    System.out.println("Every 5 seconds");
});

Cancel all tasks

Java — cancelAll
// Stop every running task immediately
EriScheduler.cancelAll();

Method reference

Method Parameters Returns Description
delay int ticks, Runnable task ScheduledTask Execute task once after ticks ticks.
repeat int ticks, int maxCount, Runnable task ScheduledTask Execute task every ticks ticks, at most maxCount times.
repeat int ticks, Runnable task ScheduledTask Execute task every ticks ticks indefinitely.
async Callable<T> callable AsyncExecutor<T> Run callable off the main thread and return an async handle.
chain TaskChain Create a new sequential task chain.
cancelAll void Cancel every active task managed by the scheduler.
activeCount int Return the number of currently active (non-cancelled) tasks.

ScheduledTask — Task reference

Every call to EriScheduler.delay() or EriScheduler.repeat() returns a ScheduledTask object. Hold onto this reference if you need to control the task after scheduling it.

Java — ScheduledTask control
ScheduledTask task = EriScheduler.repeat(20, () -> doStuff());

// Pause without losing the execution count
task.pause();

// Resume from where it was paused
task.resume();

// Stop permanently
task.cancel();

// Query the task's current state
boolean running   = task.isRunning();
boolean cancelled = task.isCancelled();
boolean paused    = task.isPaused();

// How many times has the runnable been executed so far?
int count = task.getExecutionCount();

Method reference

Method Returns Description
pause() ScheduledTask Temporarily suspend the task. The tick counter is frozen until resume() is called.
resume() ScheduledTask Resume a paused task. Has no effect if the task is not paused.
cancel() void Permanently stop the task. It cannot be restarted after cancellation.
isRunning() boolean Returns true if the task is active and not paused or cancelled.
isCancelled() boolean Returns true if cancel() was called on this task.
isPaused() boolean Returns true if the task is currently paused.
getExecutionCount() int Returns the total number of times the runnable has been executed.
Fluent pausing

pause() and resume() return this, so you can chain them: task.pause().resume() (though that would be a no-op). More practically, store the reference and call them in response to user actions.

TaskChain — Sequential chain

A TaskChain lets you queue a series of tasks where each one fires after a specified delay following the previous step. Call EriScheduler.chain() to create one, add steps with .then(delay, runnable), and start the sequence with .run().

This is ideal for cutscene-style sequences, tutorial steps, timed announcements, or any workflow where things need to happen in a strict order with controlled pauses between them.

Java — TaskChain
EriScheduler.chain()
    .then(0,  () -> System.out.println("Now"))
    .then(20, () -> System.out.println("1s later"))
    .then(40, () -> System.out.println("2s later"))
    .run();
Delay is relative to the previous step

The delay value in .then(delay, runnable) is relative to the previous step, not to when .run() was called. In the example above: step 1 fires at tick 0, step 2 at tick 20, step 3 at tick 60 (20 + 40).

Method reference

Method Parameters Returns Description
then int delay, Runnable task TaskChain Append a step that runs task after delay ticks from the preceding step.
run void Start executing the chain from the first step.

AsyncExecutor — Async tasks

EriScheduler.async() runs a Callable on a background thread, keeping the main game thread responsive. The returned AsyncExecutor handle lets you attach callbacks that run back on the main thread once the async work is done, or handle errors gracefully.

Use this for anything that might block — HTTP requests, file I/O, heavy computation — to avoid causing lag spikes in the game.

Java — AsyncExecutor
EriScheduler.async(() -> {
    // Runs off the main thread
    return fetchDataFromWeb();
}).onError(e -> {
    System.err.println("Error: " + e.getMessage());
}).thenSync(result -> {
    // Runs back on the main thread once the async work completes
    player.sendMessage(new TextComponentString("Data: " + result));
});

Method reference

Method Parameters Returns Description
onError Consumer<Throwable> handler AsyncExecutor<T> Register a callback invoked on the main thread if the async callable throws an exception.
thenSync Consumer<T> callback AsyncExecutor<T> Register a callback invoked on the main thread with the result value once the async work completes successfully.
Never access Minecraft objects from the async callable

The async callable runs on a background thread. Accessing Minecraft, World, EntityPlayer, or any Minecraft game object from inside the callable will cause thread-safety issues and potentially crash the game. Only do I/O or pure computation there. Use thenSync() to interact with Minecraft objects.

Complete example — Countdown

This example shows a practical countdown timer that sends messages to the player every second, then announces "Go!" when it reaches zero. It combines repeat with a fixed execution count and an inline state variable.

Java — Countdown timer
final int[] count = {5};

ScheduledTask countdown = EriScheduler.repeat(20, 6, () -> {
    if (count[0] > 0) {
        player.sendMessage(new TextComponentString(count[0] + "..."));
        count[0]--;
    } else {
        player.sendMessage(new TextComponentString("Go!"));
    }
});

The task runs 6 times total (once per second for 5 seconds, then the "Go!" message on the 6th tick). After 6 executions the scheduler automatically stops it. The final int[] trick is necessary because Java 8 lambdas can only capture effectively final variables — wrapping the counter in a single-element array works around this limitation.

Cancelling early

If you need to abort the countdown before it finishes (e.g., the player disconnects), just call countdown.cancel() at any point. The remaining executions will never fire.

Countdown with a chain variant

You can also express the same countdown as a TaskChain for explicit control over each step:

Java — Countdown via TaskChain
EriScheduler.chain()
    .then(0,   () -> player.sendMessage(new TextComponentString("5...")))
    .then(20,  () -> player.sendMessage(new TextComponentString("4...")))
    .then(20,  () -> player.sendMessage(new TextComponentString("3...")))
    .then(20,  () -> player.sendMessage(new TextComponentString("2...")))
    .then(20,  () -> player.sendMessage(new TextComponentString("1...")))
    .then(20,  () -> player.sendMessage(new TextComponentString("Go!")))
    .run();