Annotation Interface RunAsync
@CmdMapping method body as eligible to run off the main thread -- see below
for when that is, and is not, actually safe.
An @RunAsync body must not touch world, entity, block or chunk state
directly.
Asynchrony here is reserved for pure-CPU or I/O work. The one Bukkit call an async body
may make is scheduling its state-touching work back onto the main thread through
BukkitScheduler.runTask(org.bukkit.plugin.Plugin, Runnable);
this annotation never grants safe access to those APIs by itself. A handler whose body
consists only of such state access has no reason to carry the annotation at all.
Without this annotation, a synchronous command body is not run inline on the calling thread --
the framework already defers it by one tick via runTask() in
BaseCommandExecutor. Removing
@RunAsync from a handler that never needed it is therefore not "making the command
block the server"; it restores the same one-tick-deferred synchronous dispatch every other
command already uses.
The observable failure when this rule is broken is not a slow command -- it is a crash.
Bukkit's async-scheduler dispatch (see runTaskAsynchronously() in
BaseCommandExecutor) runs the body off the main thread unconditionally, and the first
call into world, entity, block or chunk state trips Paper's asynchronous-operation check and
kills the command mid-handler.
For work that genuinely belongs off-thread -- and needs a processing message, a timeout, or
configurable behavior around that boundary -- use AsyncCommand instead of this
annotation.
- See Also: