-
Notifications
You must be signed in to change notification settings - Fork 0
Scheduler
com.github.frosxt.prisoncore.scheduler.api.TaskOrchestrator is the only sanctioned way for a module to schedule work. Don't call Bukkit.getScheduler() directly. The orchestrator is what lets the platform centralize thread management.
public interface TaskOrchestrator {
TaskHandle mainThread(TaskSpec spec);
TaskHandle io(TaskSpec spec);
TaskHandle cpu(TaskSpec spec);
CompletableFuture<Void> switchToMainThread();
}Resolved through the service container:
final TaskOrchestrator orchestrator = context.services().resolve(TaskOrchestrator.class);mainThread(TaskSpec) runs the task on the Bukkit main thread. Required for anything that touches entity state, world state, inventories, or fires Bukkit events. The implementation routes through Bukkit's scheduler under the hood.
io(TaskSpec) runs on a shared scheduled executor sized for I/O work. Use this for disk reads, database queries, network calls, and the periodic flush of any write-behind cache you maintain. The pool is sized by available CPU count and is daemon-threaded.
cpu(TaskSpec) runs on a separate scheduled executor sized for CPU-bound work. Use this when you need to do significant computation that shouldn't interfere with I/O latency. In practice most modules don't need to distinguish; defaulting to io is fine for almost everything.
switchToMainThread() returns a CompletableFuture<Void> that completes on the main thread. This is the right way to hop a continuation back from an async callback into main-thread code:
orchestrator.io(TaskSpec.builder(() -> {
final var data = loadFromDatabase();
orchestrator.switchToMainThread().thenRun(() -> player.sendMessage(data));
}).build());com.github.frosxt.prisoncore.scheduler.api.TaskSpec
The immutable description of a task. Build one with TaskSpec.builder(Runnable).
TaskSpec spec = TaskSpec.builder(this::doWork)
.delay(Duration.ofMillis(500))
.period(Duration.ofSeconds(30))
.build();Builder methods:
-
delay(Duration)— initial delay before the first execution. Defaults to zero. -
period(Duration)— repeat interval. Omit this for a one-shot task.
A non-null period marks the spec as repeating. The same TaskSpec can be submitted to multiple pools, but typically you build it once where you submit it.
com.github.frosxt.prisoncore.scheduler.api.TaskHandle
Returned by every scheduling call.
public interface TaskHandle {
int id();
void cancel();
boolean isCancelled();
}Hold onto the handle for any repeating task. Cancel it from your onDisable so the task stops running before the kernel shuts your services down.
public final class MyModule extends AbstractPlatformModule {
private TaskHandle pollTask;
@Override
protected void onEnable(final ModuleContext context) {
final TaskOrchestrator orchestrator = context.services().resolve(TaskOrchestrator.class);
pollTask = orchestrator.io(TaskSpec.builder(this::poll)
.delay(Duration.ofSeconds(5))
.period(Duration.ofSeconds(60))
.build());
}
@Override
protected void onDisable(final ModuleContext context) {
if (pollTask != null) {
pollTask.cancel();
}
}
private void poll() {
// runs on an io thread every minute
}
}For one-shot tasks you can usually ignore the returned handle. For repeating tasks you almost always want it.
The scheduler is the seam between platform-managed threads and Bukkit. Three rules to keep in mind:
- Anything that calls into Bukkit (entities, worlds, players, inventories, events) must run on the main thread. If you're not sure, route through
mainThreadorswitchToMainThread. - Anything that hits disk, a database, or the network must not run on the main thread. Use
io. - Continuations from
CompletableFuturecomplete on whichever thread completed the future. If you wired up aniotask that ends with.thenAccept(...), that callback runs on an io thread. Hop back to the main thread before touching Bukkit state.
If you find yourself wanting to call Thread.sleep(...) to delay something, you want a delay(...) on a TaskSpec instead.
This wiki documents the public API surface module authors are expected to touch. Kernel internals are intentionally omitted. Spot something wrong? Open an issue.
Start here
Architecture
Subsystems
- Commands
- Menus
- Messages and Placeholders
- Scheduler
- Storage
- Events and Listeners
- Configuration
- Player Profiles
Utilities