MCCoroutine

repository·master·Indexed 42 days ago

https://github.com/shynixn/mccoroutine

A library that brings Kotlin Coroutine support to Minecraft server and proxy environments. It enables asynchronous programming for events, commands, and schedulers across platforms including Paper, Spigot, Fabric, Folia, Minestom, Sponge, BungeeCord, and Velocity. Key features include suspendable event listeners, the SuspendingCommandExecutor interface, and a CoroutineLifeCycle scope for plugin reloading. Supports Minecraft versions 1.7 to the latest and requires Java 8+.

Tokens
23K
Snippets
57
Records
70
Agent score
54%

What's inside MCCoroutine

  1. Overview of MCCoroutine

    master

    MCCoroutine is a library that provides extensive support for Kotlin Coroutines within Minecraft server environments. It allows developers to perform asynchronous operations (like database access) more easily by extending existing Minecraft APIs with suspendable commands, events, and schedules.

    Key Features:

    • Full implementation of Kotlin Coroutines (async/await).
    • Extension functions for established Minecraft APIs.
    • Integration with events, commands, and schedulers.
    • Coroutine LifeCycle scope for plugins (designed to support plugin reloading).
    • No NMS (Net Minecraft Server) dependency.
    • Supports Minecraft 1.7 through the latest versions.
    • Requires Java 8+.
  2. Key Features of MCCoroutine

    master

    MCCoroutine offers several core capabilities for Minecraft developers:

    • Coroutine Integration: Full implementation of Kotlin Coroutines for Minecraft Servers and Proxies.
    • API Extensions: Provides extension functions for established Minecraft functions.
    • Event & Command Integration: Connects coroutines directly to events, commands, and schedulers.
    • Lifecycle Management: Includes a CoroutineLifeCycle scope for plugins, which correctly handles plugin reloading.
    • Compatibility:
      • No NMS (Net Minecraft Server) dependency, making it more stable across versions.
      • Supports Minecraft versions from 1.7 to the latest.
      • Supports Java 8 to the latest.
  3. Supported Minecraft Server and Proxy Platforms

    master

    MCCoroutine supports a wide range of Minecraft server implementations and proxies:

    Game Servers:

    • CraftBukkit
    • Fabric
    • Folia
    • Minestom
    • Paper
    • Spigot
    • SpongeVanilla v7.x.x
    • SpongeForge v7.x.x

    Proxies:

    • BungeeCord
    • Waterfall
    • Velocity
  4. Understanding cancellation in suspendable events

    master

    When using @EventHandler with suspend functions, cancellation behavior depends on when the first suspension point occurs:

    1. Before the first suspension: You can still modify the event (e.g., event.isCancelled = true) before any suspend function (like delay or withContext) is called. Cancellation is possible at this stage.
    2. After the first suspension: Once a function has suspended (e.g., waiting for I/O via withContext(Dispatchers.IO)), the event has effectively already been processed by the engine. You cannot cancel the event or change its outcome after the first suspension point has been reached.
    // Cancellation is possible here
    @EventHandler
    suspend fun onPlayerInteractEvent(event: PlayerInteractEvent) {
        event.isCancelled = true
        
        withContext(Dispatchers.IO){
            delay(50)
        }
    }
    
    // Cancellation is NOT possible here
    @EventHandler
    suspend fun onPlayerInteractEvent(event: PlayerInteractEvent) {
        withContext(Dispatchers.IO){
            delay(50)
        }
        // The event outcome is already decided
        event.isCancelled = true
    }
  5. Use suspending functions in Minecraft listeners

    master
    MCCoroutine allows you to use Kotlin Coroutines within Minecraft plugin listeners by using the suspend keyword. This enables you to write asynchronous logic (like delays, network calls, or complex computations) directly inside event handlers without blocking the main server thread.
  6. Understand default exception behavior in MCCoroutine

    master

    MCCoroutine follows standard Kotlin Coroutine exception handling. If an exception is thrown within a suspendable command executor or listener and is not caught, it propagates upwards to MCCoroutine.

    By default, MCCoroutine logs every exception to the server logger at Level.SEVERE, except for CoroutineCancellation (which is thrown when a job is cancelled).

  7. Understand plugin.launch execution order

    master

    When using plugin.launch, the execution order follows standard coroutine suspension rules. If the coroutine is launched from the main thread, it enters the scope immediately. When a delay or other suspending function is called, the coroutine suspends, allowing the original code block to continue execution before the coroutine resumes.

    // Bukkit example
    class Foo(private val plugin: Plugin) {
        fun bar() {
            println("I am first") // Main Thread
    
            val job = plugin.launch {
                println("I am second") // Entered immediately if already on main thread
                delay(1000)
                println("I am fourth") // Resumes after 1000ms
                bob()
            }
    
            println("I am third") // Executes immediately after launch/suspension
        }
    
        private suspend fun bob() {
            println("I am fifth")
        }
    }
    // Output: 
    // I am first
    // I am second
    // I am third
    // I am fourth
    // I am fifth
  8. How to use coroutines in Folia

    master

    Folia does not have a single server main thread. To use MCCoroutine in Folia, you should adopt a pattern where each plugin manages its own data on a personal "main thread" and switches contexts to interact with Minecraft entities or regions.

    1. Plugin Main Thread: Use plugin.launch {} or plugin.launch(plugin.mainDispatcher) {} to handle your plugin's internal data (e.g., repositories, lists) to avoid concurrency issues.
    2. Minecraft Resources: Use plugin.entityDispatcher(entity) or plugin.regionDispatcher(location) to switch to the correct thread for a specific entity or location.
    3. Fallback: MCCoroutine automatically falls back to Bukkit dispatchers if running on a standard Bukkit server.

    Warning: Simply changing imports from Bukkit to Folia is not enough; you must restructure your code to handle the lack of a global main thread.

    import com.github.shynixn.mccoroutine.folia.launch
    import org.bukkit.plugin.Plugin
    
    fun foo(plugin: Plugin, entity: Entity) {
        plugin.launch {
            // Your plugin's personal main thread for data access
            val storedEntityDataInDatabase = database.get()
            
            // Switch to the correct thread for the entity in Folia
            withContext(plugin.entityDispatcher(entity)) {
                  // In Folia, this is the correct thread for the entity.
                  // In Bukkit, this is the main thread.
            }
        }
    }
  9. Implementation and performance of MCCoroutine

    master

    MCCoroutine is a wrapper around existing Minecraft framework schedulers (such as the Bukkit scheduler).

    • Implementation: When using withContext, MCCoroutine dispatches tasks to the appropriate Bukkit schedulers. All performance and behavior characteristics of the underlying Bukkit schedulers apply to MCCoroutine.
    • Memory: MCCoroutine does not create its own threads or thread pools, so it adds no direct overhead. However, standard Kotlin Coroutines may introduce slight memory increases due to their own internal thread pools.
    • Speed: Suspendable Listeners and Command Executors perform at the same speed as ordinary listeners/executors once the event is being processed. There is a minor overhead during the initial registration phase due to the use of reflection.
  10. Identify if MCCoroutine is right for your project

    master

    MCCoroutine is intended for developers who meet the following criteria:

    • You are creating server software for platforms like Bukkit, Spigot, or Sponge.
    • You are already using Kotlin in your implementation.
  11. Implement Suspending Plugins for Sponge

    master

    In Sponge, inject a SuspendingPluginContainer into your plugin class. You can then use @Listener on suspend functions to handle server events on the Minecraft Main Thread.

    import com.github.shynixn.mccoroutine.sponge.SuspendingPluginContainer
    
    @Plugin(id = "mccoroutinesample", name = "MCCoroutineSample")
    class MCCoroutineSamplePlugin {
        private val database = Database()
    
        @Inject
        private lateinit var suspendingPluginContainer: SuspendingPluginContainer
    
        @Listener
        suspend fun onEnable(event: GameStartedServerEvent) {
            // Minecraft Main Thread
            database.createDbIfNotExist()
        }
    
        @Listener
        suspend fun onDisable(event: GameStoppingServerEvent) {
            // Minecraft Main Thread
        }
    }
  12. Implement Suspending Plugins for Folia

    master

    For Folia, extend SuspendingJavaPlugin. Note that onEnableAsync runs on the Global Region Thread.

    import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin
    
    class MCCoroutineSamplePlugin : SuspendingJavaPlugin() {
        private val database = Database()
    
        override suspend fun onEnableAsync() {
            // Global Region Thread
            database.createDbIfNotExist()
        }
    
        override suspend fun onDisableAsync() {
        }
    }