Scheduling
Minecraft ticks
Section titled “Minecraft ticks”Minecraft’s sandbox engine is entirely tick based. This means that the game loop executes at specific intervals rather than every single frame like other games. When the game is running normally*, there are 20 ticks per second or 50 milliseconds per tick.
* The set tick rate can be changed using commands or internals.
The Schedulers
Section titled “The Schedulers”Canvas’ API is a superset of Paper’s API which means we include the four schedulers provided by them. Please keep in mind that although these objects share the same name, they are not the same as Folia’s configurable schedulers.
The Global Region Scheduler
Section titled “The Global Region Scheduler”The global region is a single internal scheduled task that is not controlled by a specific region. This is strictly for things that are not tied to a specific location, and are server-wide or world-wide. Things like time, weather, tick rate changing, raids, world border, console sender, etc, should all be scheduled here. Anything that is tied to a location or entity should never be scheduled here.
Plugin plugin = ...;// Obtaining the global region schedulerGlobalRegionScheduler globalScheduler = Bukkit.getGlobalRegionScheduler();
// Scheduling a taskglobalScheduler.run(plugin, task -> { Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "say Hello, world!")});The Region Scheduler
Section titled “The Region Scheduler”The region scheduler executes tasks on specific regions.
This is for scheduling things specifically tied to a Location in a world. Things like dropping items at a specified location and
such should be scheduled here. This should never be used for global operations. This should not be used for entity-specific or global operations.
Plugin plugin = ...;Location locationToChange = ...;// Obtaining the region schedulerRegionScheduler regionScheduler = Bukkit.getRegionScheduler();
// Scheduling a taskregionScheduler.execute(plugin, locationToChange, () -> { locationToChange.getBlock().setType(Material.DIAMOND_BLOCK);});The Entity Scheduler
Section titled “The Entity Scheduler”This is for scheduling things specifically tied to an entity. Not locations, not global operations, entities. The entity state can change for any number of reasons, and as such, Paper/Folia provides the entity scheduler so that you can schedule things to the entity specifically, and it will handle states and edge cases with those states like teleporting, being removed, etc. Anything location-specific should not be scheduled here, as the entity position can change after scheduling for any reason. Things like changing the entity health or setting it on fire should be scheduled here.
- Just because something doesn’t throw an exception when working with the schedulers doesn’t mean it’s perfectly scheduled. Always extensively debug for any edge cases and ensure you’re using the proper scheduler!
Plugin plugin = ...;Entity entity = ...;// Obtaining an entity's schedulerEntityScheduler entityScheduler = entity.getScheduler();
// Scheduling a taskentityScheduler.execute(plugin, (task) -> { entity.setHealth(0);}, null);The Async Scheduler
Section titled “The Async Scheduler”The async scheduler should be used for scheduling tasks that do not interact with the Bukkit API or for other intensive tasks that can be done off the main thread of a region or the global region scheduler.
Plugin plugin = ...;// Obtaining the async schedulerAsyncScheduler asyncScheduler = Bukkit.getAsyncScheduler();
// Scheduling a taskasyncScheduler.runNow(plugin, (task) -> { // Writing to a file or something that can be done asynchronously.})Tick Thread Check API
Section titled “Tick Thread Check API”Paper (Moonrise) provides an API for Folia that introduces tick thread checking to see if an object or location is owned by the current ticking region, or if the current thread is ticking the global tick thread. This can assist with scheduling things and ensuring that all operations are executed on the proper scheduling context. The available tick thread checks are:
isOwnedByCurrentRegion(World, Position)— Returns if the current tick handle owns the position in the provided worldisOwnedByCurrentRegion(World, Position, radius in chunks)— Returns if the current tick handle owns the position in the provided world and the surrounding chunks in a provided radiusisOwnedByCurrentRegion(Location)— Returns if the current tick handle owns the location providedisOwnedByCurrentRegion(Location, radius in chunks)— Returns if the current tick handle owns the location provided and the surrounding chunks in a provided radiusisOwnedByCurrentRegion(Block)— Returns if the current tick handle owns the position of the block providedisOwnedByCurrentRegion(World, chunk x, chunk z)— Returns if the current tick handle owns the chunk coords in the provided worldisOwnedByCurrentRegion(World, chunk x, chunk z, radius in chunks)— Returns if the current tick handle owns the chunk coords in the provided world and the surrounding chunks in a provided radiusisOwnedByCurrentRegion(World, from chunk x, from chunk z, to chunk x, to chunk z)— Returns if the current tick handle owns the area of chunks specified in the provided worldisOwnedByCurrentRegion(Entity)— Returns if the current tick handle owns the entity providedisOwnedByCurrentRegion(World, BoundingBox)— Canvas exclusive — Returns if the current tick handle owns the specified bounding box in the world providedisOwnedByCurrentRegion(Location, Vector, chunk buffer)— Canvas exclusive — Returns if the current tick handle owns the specfied location, adjusted by the provided velocity with a provided chunk radius bufferisGlobalTickThread— Returns if the current tick handle is the global tick
Dispatching Commands
Section titled “Dispatching Commands”It’s important to always dispatch commands on the right thread when working in Folia environments such as Canvas.
There is no main thread so you need to ensure you’re on the proper thread when calling dispatchCommand.
Dispatching a command from the wrong thread will throw an UnsupportedOperationException.
For the Console Sender
Section titled “For the Console Sender”The console sender and remote console sender must always be called on the global region scheduler. Access the global region scheduler as shown above and dispatch your commands from that thread.
For Entities
Section titled “For Entities”Entities dispatching commands should be run on the entity’s scheduler. Access the entity scheduler as shown above and dispatch your commands from that thread.