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 = Collections.synchronizedList(ArrayList()) 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() } }