Runnables is a lightweight Paper/Folia-compatible plugin API that provides a drop-in replacement for BukkitRunnable with full support for:
- Folia async, region, and entity scheduling
- Paper synchronous and asynchronous tasks
- Task IDs and task cancellation
- Cross-platform compatibility (Paper/Bukkit/Folia)
This API allows you to write scheduled tasks in a BukkitRunnable-style API while automatically supporting Folia's threaded regions without rewriting your code.
Add the following dependency and repository to your Maven project:
Repository:
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>Dependency:
<dependency>
<groupId>com.github.Hihelloy-main</groupId>
<artifactId>Runnables</artifactId>
<version>v1.0</version>
<scope>provided</scope>
</dependency>Plugin.yml example:
Remember that the runnables API is actually a plugin so you need to add it as a dependency in your plugin.yml!
name: ExamplePlugin
version: '1.0'
main: com.hihelloy.examplePlugin.ExamplePlugin
api-version: '1.20'
authors: [ Hihelloy ]
depend: [Runnables]- Paper 1.20.6+
- Folia (threaded regions enabled)
- Bukkit/Spigot (async tasks supported via fallback)
Extend PaperRunnable just like BukkitRunnable:
PaperRunnable task = new PaperRunnable() {
@Override
public void run() {
// Your code here
System.out.println("Task executed!");
}
};// Run immediately
task.runTask(plugin);
// Run after delay (ticks)
task.runTaskLater(plugin, 20L);
// Run repeating task (delay, period in ticks)
task.runTaskTimer(plugin, 10L, 20L);// Run immediately asynchronously
task.runAsync(plugin);
// Run async after delay
task.runAsyncLater(plugin, 20L);
// Run async repeating
task.runAsyncTimer(plugin, 10L, 20L);// Run at a specific location
task.runAtLocation(plugin, location);
// Delayed location task
task.runAtLocationLater(plugin, location, 20L);
// Repeating location task
task.runAtLocationTimer(plugin, location, 10L, 20L);// Run at an entity
task.runAtEntity(plugin, entity);
// Run at entity with delayed start
task.runAtEntityLater(plugin, entity, 20L);
// Run repeating at entity
task.runAtEntityTimer(plugin, entity, 10L, 20L);
// With a retired callback when the task ends
task.runAtEntity(plugin, entity, () -> System.out.println("Task retired"));- Get task ID
int id = task.getTaskId();
int foliaId = task.getFoliaTaskId();
int bukkitId = task.getBukkitTaskId();- Check if running
boolean running = task.isRunning();- Cancel a task
task.cancel(); // cancels this task
PaperRunnable.cancelTask(id); // cancels a task by ID- Full BukkitRunnable replacement with Folia compatibility
- Async scheduling works on both Folia and vanilla Paper
- Region scheduling using Folia region scheduler
- Entity scheduling with optional retired callback
- Task IDs for both Folia and Bukkit tasks
- Automatic detection of Folia environment (
isFolia())
public class ExamplePlugin extends JavaPlugin {
@Override
public void onEnable() {
PaperRunnable runnable = new PaperRunnable() {
@Override
public void run() {
getLogger().info("Hello from PaperRunnable!");
}
};
// Run async after 20 ticks
runnable.runAsyncLater(this, 20L);
}
}MIT License © Hihelloy
- Folia region and entity scheduling only works on Folia. Attempting to use these on vanilla Paper/Bukkit will throw an
IllegalStateException. - Async tasks fallback on Bukkit if Folia isn’t detected.
- 1 tick = 50ms. Async timers automatically convert ticks to milliseconds for Folia’s
AsyncScheduler.
-
PaperRunnablerun()runTask(plugin)runTaskLater(plugin, delayTicks)runTaskTimer(plugin, delayTicks, periodTicks)runAsync(plugin)runAsyncLater(plugin, delayTicks)runAsyncTimer(plugin, delayTicks, periodTicks)runAtLocation(plugin, location)runAtLocationLater(plugin, location, delayTicks)runAtLocationTimer(plugin, location, delayTicks, periodTicks)runAtEntity(plugin, entity)runAtEntity(plugin, entity, Runnable retired)runAtEntityLater(plugin, entity, delayTicks)runAtEntityLater(plugin, entity, delayTicks, Runnable retired)runAtEntityTimer(plugin, entity, delayTicks, periodTicks)runAtEntityTimer(plugin, entity, delayTicks, periodTicks, Runnable retired)cancel()cancelTask(int taskId)getTaskId()getBukkitTaskId()getFoliaTaskId()isRunning()isFolia()isCancelled()