Skip to content

Commit

Permalink
Initialize Folia support
Browse files Browse the repository at this point in the history
  • Loading branch information
vjh0107 committed Jan 25, 2024
1 parent 99e9e50 commit d7a6a4b
Show file tree
Hide file tree
Showing 18 changed files with 264 additions and 42 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
kotlin.code.style=official

project.group=kr.summitsystems
project.version=0.0.8-beta4
project.version=0.0.8-beta5
project.name=spring-bukkit
project.url=https://github.com/summit-systems/spring-bukkit
project.url.scm=https://github.com/summit-systems/spring-bukkit.git
Expand Down
Binary file modified project-dependency-graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencyResolutionManagement {

library("spigot", "org.spigotmc:spigot-api:${extra["spigot.version"]}")
library("paper", "io.papermc.paper:paper-api:${extra["spigot.version"]}")
library("folia", "dev.folia:folia-api:${extra["spigot.version"]}")
library("mockbukkit", "com.github.seeseemelk:MockBukkit-v1.20:${extra["mock.bukkit.version"]}")
library("protocollib", "com.comphenix.protocol:ProtocolLib:${extra["protocollib.version"]}")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package kr.summitsystems.springbukkit.core.scheduler

import kr.summitsystems.springbukkit.core.Disposable
import org.bukkit.plugin.Plugin

interface BukkitScheduledTask : Disposable {
fun isCurrentlyRunning(): Boolean

fun getPlugin(): Plugin

fun isDisposed(): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package kr.summitsystems.springbukkit.core.scheduler

import org.bukkit.plugin.Plugin
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class BukkitSchedulerConfiguration {
@ConditionalOnMissingBean(BukkitTaskScheduler::class)
@Bean
fun defaultBukkitTaskScheduler(
plugin: Plugin
): BukkitTaskScheduler {
return DefaultBukkitTaskScheduler(plugin)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package kr.summitsystems.springbukkit.core.scheduler

interface BukkitTaskScheduler {
fun schedule(task: () -> Unit): BukkitScheduledTask

fun scheduleAtFixedRate(periodTicks: Long, initialDelayTicks: Long = 0, task: () -> Unit): BukkitScheduledTask

fun scheduleWithFixedDelay(delayedTicks: Long, task: () -> Unit): BukkitScheduledTask
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package kr.summitsystems.springbukkit.core.scheduler

import org.bukkit.plugin.Plugin
import org.bukkit.scheduler.BukkitScheduler
import org.bukkit.scheduler.BukkitTask

class DefaultBukkitScheduledTask(
private val bukkitScheduler: BukkitScheduler,
private val delegate: BukkitTask
) : BukkitScheduledTask {
override fun isCurrentlyRunning(): Boolean {
return bukkitScheduler.isCurrentlyRunning(delegate.taskId)
}

override fun getPlugin(): Plugin {
return delegate.owner
}

override fun isDisposed(): Boolean {
return delegate.isCancelled
}

override fun dispose() {
return delegate.cancel()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package kr.summitsystems.springbukkit.core.scheduler

import org.bukkit.Bukkit
import org.bukkit.plugin.Plugin

class DefaultBukkitTaskScheduler(
private val plugin: Plugin
) : BukkitTaskScheduler {
private val bukkitScheduler = Bukkit.getScheduler()

override fun schedule(task: () -> Unit): BukkitScheduledTask {
val bukkitTask = bukkitScheduler.runTask(plugin, task)
return DefaultBukkitScheduledTask(bukkitScheduler, bukkitTask)
}

override fun scheduleAtFixedRate(
periodTicks: Long,
initialDelayTicks: Long,
task: () -> Unit
): BukkitScheduledTask {
val bukkitTask = bukkitScheduler.runTaskTimer(plugin, task, initialDelayTicks, periodTicks)
return DefaultBukkitScheduledTask(bukkitScheduler, bukkitTask)
}

override fun scheduleWithFixedDelay(delayedTicks: Long, task: () -> Unit): BukkitScheduledTask {
val bukkitTask = bukkitScheduler.runTaskLater(plugin, task, delayedTicks)
return DefaultBukkitScheduledTask(bukkitScheduler, bukkitTask)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import org.bukkit.plugin.Plugin
import org.bukkit.plugin.PluginDescriptionFile
import org.bukkit.plugin.PluginManager
import org.bukkit.plugin.ServicesManager
import org.bukkit.scheduler.BukkitScheduler
import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
Expand Down Expand Up @@ -36,12 +35,6 @@ class BukkitConfiguration {
return server.servicesManager
}

@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Bean
fun bukkitScheduler(server: Server): BukkitScheduler {
return server.scheduler
}

@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Bean
fun pluginDescriptionFile(plugin: Plugin): PluginDescriptionFile {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package kr.summitsystems.springbukkit.kotlinx.coroutines.dispatcher

import kotlinx.coroutines.*
import kr.summitsystems.springbukkit.core.scheduler.BukkitScheduledTask
import kr.summitsystems.springbukkit.core.scheduler.DefaultBukkitScheduledTask
import kr.summitsystems.springbukkit.kotlinx.coroutines.PluginCoroutineContextElement
import org.bukkit.Bukkit
import org.bukkit.Server
import org.bukkit.plugin.IllegalPluginAccessException
import org.bukkit.plugin.Plugin
import org.bukkit.scheduler.BukkitRunnable
import org.bukkit.scheduler.BukkitScheduler
import kotlin.coroutines.CoroutineContext

Expand All @@ -15,48 +16,46 @@ open class BukkitMainDispatcher(
private val server: Server = Bukkit.getServer(),
private val scheduler: BukkitScheduler = Bukkit.getScheduler()
) : MainCoroutineDispatcher(), Delay {

override val immediate: MainCoroutineDispatcher
get() = BukkitMainDispatcherImmediate(server, scheduler)
get() = object : BukkitMainDispatcher(server, scheduler) {
override val immediate: MainCoroutineDispatcher
get() = this

override fun isDispatchNeeded(context: CoroutineContext): Boolean {
return !server.isPrimaryThread
}
}

open fun runTask(plugin: Plugin, task: () -> Unit): BukkitScheduledTask {
return DefaultBukkitScheduledTask(scheduler, scheduler.runTask(plugin, task))
}

open fun runTaskWithFixedDelay(plugin: Plugin, delay: Long, task: () -> Unit): BukkitScheduledTask {
return DefaultBukkitScheduledTask(scheduler, scheduler.runTaskLater(plugin, task, delay))
}

override fun dispatch(context: CoroutineContext, block: Runnable) {
final override fun dispatch(context: CoroutineContext, block: Runnable) {
try {
scheduler.runTask(getPluginByCoroutineContext(context), block)
runTask(getPluginByCoroutineContext(context)) { block.run() }
} catch (_: IllegalPluginAccessException) {
}
}

override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
val runnable = object : BukkitRunnable() {
override fun run() {
final override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
val task = try {
runTaskWithFixedDelay(getPluginByCoroutineContext(continuation.context), timeMillis / 50) {
with(continuation) {
resumeUndispatched(Unit)
}
}
}

val task = try {
runnable.runTaskLater(getPluginByCoroutineContext(continuation.context), timeMillis / 50)
} catch (_: IllegalPluginAccessException) {
null
}
continuation.invokeOnCancellation { task?.cancel() }
continuation.invokeOnCancellation { task?.dispose() }
}

private fun getPluginByCoroutineContext(coroutineContext: CoroutineContext): Plugin {
return coroutineContext[PluginCoroutineContextElement]?.plugin
?: throw IllegalStateException("PluginCoroutineContextElement is missing.")
}

private class BukkitMainDispatcherImmediate(
private val server: Server,
scheduler: BukkitScheduler
) : BukkitMainDispatcher(server, scheduler), Delay {
override val immediate: MainCoroutineDispatcher
get() = this

override fun isDispatchNeeded(context: CoroutineContext): Boolean {
return !server.isPrimaryThread
}
}
}
3 changes: 2 additions & 1 deletion spring-bukkit-support/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
dependencies {
compileOnly(libs.spigot)
compileOnly(libs.paper)
compileOnly(libs.folia)
implementation(libs.protocollib)

api(projects.core)
implementation(projects.command)
implementation(projects.view)
implementation(projects.kotlinxCoroutines)

testImplementation(libs.spigot)
testImplementation(libs.spring.test)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package kr.summitsystems.springbukkit.support.folia

import io.papermc.paper.threadedregions.scheduler.ScheduledTask
import kr.summitsystems.springbukkit.core.scheduler.BukkitScheduledTask
import org.bukkit.plugin.Plugin

class FoliaBukkitScheduledTask(private val delegate: ScheduledTask) : BukkitScheduledTask {
override fun isCurrentlyRunning(): Boolean {
return delegate.executionState == ScheduledTask.ExecutionState.RUNNING
}

override fun getPlugin(): Plugin {
return delegate.owningPlugin
}

override fun isDisposed(): Boolean {
return delegate.isCancelled
}

override fun dispose() {
delegate.cancel()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package kr.summitsystems.springbukkit.support.folia

import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler
import kr.summitsystems.springbukkit.core.scheduler.BukkitScheduledTask
import kr.summitsystems.springbukkit.core.scheduler.BukkitTaskScheduler
import org.bukkit.Bukkit
import org.bukkit.plugin.Plugin

class FoliaBukkitTaskScheduler(
private val plugin: Plugin
) : BukkitTaskScheduler {
private val globalRegionScheduler: GlobalRegionScheduler = Bukkit.getGlobalRegionScheduler()

override fun schedule(task: () -> Unit): BukkitScheduledTask {
val scheduledTask = globalRegionScheduler.run(plugin) { task.invoke() }
return FoliaBukkitScheduledTask(scheduledTask)
}

override fun scheduleAtFixedRate(
periodTicks: Long,
initialDelayTicks: Long,
task: () -> Unit
): BukkitScheduledTask {
val scheduledTask = globalRegionScheduler.runAtFixedRate(plugin, { task.invoke() }, initialDelayTicks, periodTicks)
return FoliaBukkitScheduledTask(scheduledTask)
}

override fun scheduleWithFixedDelay(delayedTicks: Long, task: () -> Unit): BukkitScheduledTask {
val scheduledTask = globalRegionScheduler.runDelayed(plugin, { task.invoke() }, delayedTicks)
return FoliaBukkitScheduledTask(scheduledTask)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package kr.summitsystems.springbukkit.support.folia

import kr.summitsystems.springbukkit.core.scheduler.BukkitTaskScheduler
import org.bukkit.plugin.Plugin
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class FoliaConfiguration {
@ConditionalOnClass(name = ["io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler"])
@Bean
fun foliaBukkitTaskScheduler(plugin: Plugin): BukkitTaskScheduler {
return FoliaBukkitTaskScheduler(plugin)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package kr.summitsystems.springbukkit.support.folia.coroutines

import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler
import kotlinx.coroutines.Delay
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.MainCoroutineDispatcher
import kr.summitsystems.springbukkit.core.scheduler.BukkitScheduledTask
import kr.summitsystems.springbukkit.kotlinx.coroutines.dispatcher.BukkitMainDispatcher
import kr.summitsystems.springbukkit.support.folia.FoliaBukkitScheduledTask
import org.bukkit.Bukkit
import org.bukkit.plugin.Plugin
import kotlin.coroutines.CoroutineContext

@InternalCoroutinesApi
open class FoliaMainDispatcher(
private val globalRegionScheduler: GlobalRegionScheduler = Bukkit.getGlobalRegionScheduler()
) : BukkitMainDispatcher(), Delay {
override val immediate: MainCoroutineDispatcher
get() = object : FoliaMainDispatcher(globalRegionScheduler) {
override val immediate: MainCoroutineDispatcher
get() = this

override fun isDispatchNeeded(context: CoroutineContext): Boolean {
return true
}
}

override fun runTask(plugin: Plugin, task: () -> Unit): BukkitScheduledTask {
return FoliaBukkitScheduledTask(globalRegionScheduler.run(plugin) { task.invoke() })
}

override fun runTaskWithFixedDelay(plugin: Plugin, delay: Long, task: () -> Unit): BukkitScheduledTask {
return FoliaBukkitScheduledTask(globalRegionScheduler.runDelayed(plugin, { task.invoke() }, delay))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package kr.summitsystems.springbukkit.support.folia.coroutines

import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.MainCoroutineDispatcher
import kotlinx.coroutines.internal.MainDispatcherFactory

@OptIn(InternalCoroutinesApi::class)
class FoliaMainDispatcherFactory : MainDispatcherFactory {
override val loadPriority: Int
get() = -1

override fun createDispatcher(allFactories: List<MainDispatcherFactory>): MainCoroutineDispatcher {
return if (classExists("io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler")) {
FoliaMainDispatcher()
} else {
allFactories
.first { it.loadPriority != this.loadPriority }
.createDispatcher(allFactories)
}
}

private fun classExists(className: String): Boolean {
return try {
Class.forName(className)
true
} catch (ex: ClassNotFoundException) {
false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kr.summitsystems.springbukkit.support.folia.coroutines.FoliaMainDispatcherFactory
Loading

0 comments on commit d7a6a4b

Please sign in to comment.