KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt
2024-02-12 17:27:04 +07:00

83 lines
2.2 KiB
Kotlin

package ru.dbotthepony.kstarbound.server
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.util.MailboxExecutorService
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.server.world.ServerWorld
import ru.dbotthepony.kstarbound.util.ExecutionSpinner
import java.io.Closeable
import java.io.File
import java.util.Collections
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.LockSupport
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
sealed class StarboundServer(val root: File) : Closeable {
init {
if (!root.exists()) {
check(root.mkdirs()) { "Unable to create ${root.absolutePath}" }
} else if (!root.isDirectory) {
throw IllegalArgumentException("${root.absolutePath} is not a directory")
}
}
val worlds: MutableList<ServerWorld> = Collections.synchronizedList(ArrayList<ServerWorld>())
val serverID = threadCounter.getAndIncrement()
val mailbox = MailboxExecutorService()
val spinner = ExecutionSpinner(mailbox, ::spin, Starbound.TICK_TIME_ADVANCE_NANOS)
val thread = Thread(spinner, "Starbound Server $serverID")
val settings = ServerSettings()
val channels = ServerChannels(this)
val lock = ReentrantLock()
var isClosed = false
private set
init {
thread.uncaughtExceptionHandler = Thread.UncaughtExceptionHandler { t, e ->
LOGGER.fatal("Unexpected exception in server execution loop, shutting down", e)
actuallyClose()
}
thread.isDaemon = this is IntegratedStarboundServer
thread.start()
}
fun playerInGame(player: ServerConnection) {
val world = worlds.first()
world.acceptPlayer(player)
}
protected abstract fun close0()
private fun spin(): Boolean {
if (isClosed) return false
channels.connectionsView.forEach { if (it.isConnected) it.flush() }
return !isClosed
}
private fun actuallyClose() {
if (isClosed) return
isClosed = true
channels.close()
worlds.forEach { it.close() }
close0()
}
final override fun close() {
if (Thread.currentThread() == thread) {
actuallyClose()
} else {
mailbox.execute { actuallyClose() }
}
}
companion object {
private val threadCounter = AtomicInteger()
private val LOGGER = LogManager.getLogger()
}
}