-
Notifications
You must be signed in to change notification settings - Fork 8
Working with Kelp Schedulers
This wiki page will teach you how to use Schedulers in Kelp.
There are two different types of schedulers:
-
RepeatingScheduler
: Execute a specific task repeatedly with a given delay and an optional initial delay. -
DelayedScheduler
: Perform a one-shot action with a specific delay.
Both scheduler types support executions on the server's main thread as well as an async thread.
To create a new instance of a scheduler, SchedulerFactory
is needed, which you can simply inject via Guice. There you have methods such as newDelayedScheduler()
to create a new scheduler instance:
public class SchedulerTest {
private SchedulerFactory schedulerFactory;
@Inject
public SchedulerTest(SchedulerFactory schedulerFactory) {
this.schedulerFactory = schedulerFactory;
}
public void test() {
DelayedScheduler delayedScheduler = schedulerFactory.newDelayedScheduler();
}
}
By default, each scheduler is executed on the main thread. You can change that by using the async()
method. An overview of all configuration methods can be found here:
Method | Description | Example |
---|---|---|
async() |
The code inside the scheduler is executed on a new asynchronous thread. | |
sync() |
The code inside the scheduler is executed on the server's main thread (default value) | |
timeUnit(TimeUnit) |
Sets the TimeUnit of the scheduler's delay value (either the initial delay of DelayedScheduler or the interval of RepeatingScheduler ) |
timeUnit(TimeUnit.MINUTES) |
milliseconds() |
Sets the TimeUnit to MILLISECONDS
|
|
seconds() |
Sets the TimeUnit to SECONDS
|
|
minutes() |
Sets the TimeUnit to MINUTES
|
|
hours() |
Sets the TimeUnit to HOURS
|
|
run(KelpSchedulerRunnable) |
Actually starts the scheduler and provides the code to be executed in each iteration | run(taskId -> System.out.println("Test")) |
Method | Description | Example |
---|---|---|
withDelayOf(int) |
Sets the delay before the scheduler is fired and the runnable is executed. The unit for the given int is the TimeUnit described in the table above. |
withDelayOf(10) |
Method | Description | Example |
---|---|---|
every(int) |
Sets the delay between each execution. The unit for the given int is the TimeUnit described in the table above. |
every(1) |
withInitialDelay(int) |
Sets the initial delay for the first execution. When the run method is called, the given amount of time has to pass before the code inside the runnable is called and the repeating delay can start. | withInitialDelay(5) |
waitForTaskCompletion(bool) |
If set to true , the repeating delay is counted after the execution of the runnable has finished. So if the repeating delay is 5 seconds and the runnable takes 1 second to finish, it would take 6 seconds between iteration. If set to false this interval will be regular and the runnable might get called if another task is still running (default) |
waitForTaskCompletion(true) |
Waits 10 seconds, until it sends BOOOM!
to the console on an async thread
schedulerFactory.newDelayedScheduler()
.async()
.withDelayOf(10)
.seconds()
.run(taskId -> {
System.out.println("BOOOM!");
});
Starts a scheduler counting down from 30 seconds. If the time expired, the scheduler is stopped.
public class SchedulerTest {
private SchedulerFactory schedulerFactory;
private KelpSchedulerRepository schedulerRepository;
@Inject
public SchedulerTest(SchedulerFactory schedulerFactory, KelpSchedulerRepository kelpSchedulerRepository) {
this.schedulerFactory = schedulerFactory;
this.schedulerRepository = kelpSchedulerRepository;
}
public void test() {
AtomicInteger countDown = new AtomicInteger(30);
schedulerFactory.newRepeatingScheduler()
.sync()
.every(1)
.seconds()
.run(taskId -> {
System.out.println("Only " + countDown.get() + " seconds until the game starts!");
if (countDown.get() == 0) {
System.out.println("The game starts now, all players are teleported...");
schedulerRepository.interruptScheduler(taskId);
return;
}
countDown.getAndDecrement();
});
}
}
Sending packets, teleporting players, opening sidebars, etc. is not thread-safe in bukkit and may not be executed from async threads, which is why many developers have to choose a sync scheduler, although the rest of the code is thread-safe and performance would increase if it is executed asynchronously.
This is why the ServerMainThread
class of Kelp exists. It allows you to jump back easily to the main thread while having an async scheduler.
In this example, a the async thread is not blocked, but a new action is queued in the main thread as it is independent from the following actions in the async thread.
ServerMainThread.RunParallel.run(() -> {
player.teleport(spawnLocation);
player.sendBossBar("Welcome in the Lobby!");
});
This example blocks the async thread and lets it wait for the result processed on the server's main thread. After the result has been retrieved, the async thread can continue.
World spawnWorld = (World) ServerMainThread.WaitForCompletion.result(() -> {
// do some complex and not thread-safe operations
return Bukkit.getWorld(worldName);
});
Of course you can also return null
to return no result, but still block the async thread until the operations have finished.
ServerMainThread.WaitForCompletion.result(() -> {
// do some complex and not thread-safe operations
return null;
});
(c) 2019-2021 pxav.
Kelp is an open-source project maintained by multiple developers. If you have additions/questions/problems to report about the wiki, feel free to create an issue here on GitHub or join the Discord
- SQL Module coming soon
- Documentation in progress