CloudNetService / CloudNet

A modern application that can dynamically and easily deliver Minecraft oriented software
https://cloudnetservice.eu
Apache License 2.0
371 stars 115 forks source link

Fix: Minestom does not properly shut down #1337

Closed GiantTreeLP closed 3 months ago

GiantTreeLP commented 9 months ago

Motivation

Minestom is a very lightweight framework for implementing specialized Minecraft servers.
Especially when the full implementation or behavior of vanilla Minecraft is not needed, Minestom offers a way to run services with very little resources.

One issue that arised in conjunction with CloudNet is that services implemented using Minestom would not properly stop. Minestom would shut down and the service would get unregistered from CloudNet but the processes would be kept running.

Modification

This pull request first exposes and then properly uses the built-in methods for closing resources that prevent the Wrapper process from exiting.
Mainly, this is facilitated by shutting down components that hold thread pools which in turn hold non-daemon threads that specifically prevent the JVM from shutting down.

Result

Services using the Minestom framework now properly shut down and allow CloudNet to advance to the next lifecycle steps.
The changes from this PR can also be used to improve the quality of other implementations.

Other context

Fixes #1304

GiantTreeLP commented 9 months ago

If you want to test these changes, I have developed a simple minimal Minestom implementation:

package com.github.gianttree.miniminestom

import net.minestom.server.MinecraftServer
import net.minestom.server.timer.TaskSchedule
import java.util.logging.ConsoleHandler
import java.util.logging.Level
import java.util.logging.Logger

fun main() {

    val logger = Logger.getLogger("MiniMinestom")
    logger.level = Level.ALL
    logger.useParentHandlers = false
    logger.addHandler(ConsoleHandler().apply {
        level = Level.ALL
    })

    logger.info("Starting MiniMinestom...")

    val hostAddress = System.getProperty("service.bind.host")
    logger.fine("Host address: $hostAddress")
    if (hostAddress == null) {
        logger.severe("Host address not set!")
        return
    }

    val hostPort = Integer.getInteger("service.bind.port")
    logger.fine("Host port: $hostPort")
    if (hostPort == null) {
        logger.severe("Host port not set!")
        return
    }

    val server = MinecraftServer.init()
    logger.fine("Server initialized")

    val scheduler = MinecraftServer.getSchedulerManager()
    scheduler.scheduleTask(
        {
            logger.info("Stopping server")
            MinecraftServer.stopCleanly()
        },
        TaskSchedule.seconds(2),
        TaskSchedule.stop()
    )

    logger.info("Starting server")
    server.start(hostAddress, hostPort)
    logger.info("Server started")
}
0utplay commented 3 months ago

Closed in favor of #1338