@@ -15,6 +15,7 @@ import org.bukkit.material.Hopper as LegacyHopper
1515import org.bukkit.plugin.Plugin
1616import java.lang.reflect.Method
1717import java.util.Locale
18+ import java.util.concurrent.CompletableFuture
1819import java.util.concurrent.ConcurrentHashMap
1920import java.util.function.Consumer
2021
@@ -52,6 +53,10 @@ object ServerCompat {
5253 val implementationClass = server.javaClass.name
5354 val serverName = runCatching { server.name }.getOrElse { Bukkit .getName() }
5455 val versionText = Bukkit .getVersion()
56+ val hasFoliaSchedulers =
57+ serverHasMethod(server, " getGlobalRegionScheduler" ) &&
58+ serverHasMethod(server, " getRegionScheduler" ) &&
59+ serverHasMethod(server, " getAsyncScheduler" )
5560 val composite = listOf (serverName, versionText, implementationClass)
5661 .joinToString(" " )
5762 .lowercase(Locale .ROOT )
@@ -61,7 +66,9 @@ object ServerCompat {
6166 }
6267
6368 when {
64- classExists(" io.papermc.paper.threadedregions.RegionizedServer" ) || composite.contains(" folia" ) -> {
69+ classExists(" io.papermc.paper.threadedregions.RegionizedServer" ) ||
70+ composite.contains(" folia" ) ||
71+ hasFoliaSchedulers -> {
6572 ServerRuntime (ServerFamily .FOLIA , " Folia" , serverName, versionText, implementationClass)
6673 }
6774 classExists(" io.papermc.paper.configuration.Configuration" ) || composite.contains(" paper" ) ||
@@ -120,6 +127,12 @@ object ServerCompat {
120127 Class .forName(className)
121128 }.isSuccess
122129 }
130+
131+ private fun serverHasMethod (server : Any , name : String ): Boolean {
132+ return runCatching {
133+ server.javaClass.getMethod(name)
134+ }.isSuccess
135+ }
123136}
124137
125138object BukkitCompat {
@@ -441,11 +454,11 @@ object BukkitCompat {
441454object SchedulerBridge {
442455
443456 fun runLater (plugin : Plugin , delayTicks : Long , task : Runnable ) {
444- Bukkit .getScheduler().runTaskLater( plugin, task, delayTicks )
457+ FoliaUtil .runLater( plugin, delayTicks, task )
445458 }
446459
447460 fun runAsync (plugin : Plugin , task : Runnable ) {
448- Bukkit .getScheduler().runTaskAsynchronously (plugin, task)
461+ FoliaUtil .runAsync (plugin, task)
449462 }
450463
451464 fun runPlayerTaskLater (plugin : Plugin , player : Player , delayTicks : Long , task : Runnable ) {
@@ -512,6 +525,22 @@ object FoliaUtil {
512525 }
513526 }
514527
528+ private val asyncScheduler by lazy {
529+ if (! isFolia) {
530+ null
531+ } else {
532+ runCatching {
533+ Bukkit .getServer().javaClass.getMethod(" getAsyncScheduler" ).invoke(Bukkit .getServer())
534+ }.getOrNull()
535+ }
536+ }
537+
538+ private val asyncRunNowMethod by lazy {
539+ asyncScheduler?.javaClass?.methods?.firstOrNull {
540+ it.name == " runNow" && it.parameterCount == 2
541+ }
542+ }
543+
515544 private val regionScheduler by lazy {
516545 if (! isFolia) {
517546 null
@@ -589,6 +618,32 @@ object FoliaUtil {
589618 return TaskHandle { task.cancel() }
590619 }
591620
621+ fun runAsync (plugin : Plugin , action : Runnable ): TaskHandle {
622+ if (! isFolia) {
623+ val task = Bukkit .getScheduler().runTaskAsynchronously(plugin, action)
624+ return TaskHandle { task.cancel() }
625+ }
626+
627+ var scheduledTask: Any? = null
628+ val consumer = Consumer <Any ?> { task ->
629+ if (scheduledTask == null && task != null ) {
630+ scheduledTask = task
631+ }
632+ action.run ()
633+ }
634+
635+ val returnedTask = invokeAsync(asyncRunNowMethod, plugin, consumer)
636+ if (returnedTask != null ) {
637+ scheduledTask = returnedTask
638+ }
639+ if (scheduledTask != null ) {
640+ return TaskHandle { cancelScheduledTask(scheduledTask) }
641+ }
642+
643+ val future = CompletableFuture .runAsync(action)
644+ return TaskHandle { future.cancel(true ) }
645+ }
646+
592647 fun runAtLocation (plugin : Plugin , location : Location , action : Runnable ) {
593648 if (! isFolia) {
594649 action.run ()
@@ -653,6 +708,14 @@ object FoliaUtil {
653708 }.getOrNull()
654709 }
655710
711+ private fun invokeAsync (method : Method ? , vararg args : Any? ): Any? {
712+ val scheduler = asyncScheduler ? : return null
713+ val targetMethod = method ? : return null
714+ return runCatching {
715+ targetMethod.invoke(scheduler, * args)
716+ }.getOrNull()
717+ }
718+
656719 private fun cancelScheduledTask (task : Any? ) {
657720 val scheduledTask = task ? : return
658721 val cancelMethod = scheduledTaskCancelMethods.computeIfAbsent(scheduledTask.javaClass) { taskClass ->
0 commit comments