83 lines
2.2 KiB
Kotlin
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()
|
|
}
|
|
}
|