diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/IntegratedStarboundServer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/IntegratedStarboundServer.kt index 5c8ba08a..2f484002 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/IntegratedStarboundServer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/IntegratedStarboundServer.kt @@ -12,11 +12,7 @@ class IntegratedStarboundServer(val client: StarboundClient, root: File) : Starb override fun tick0(delta: Double) { if (client.isShutdown) { - shutdown() + close() } } - - override fun close0() { - - } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt index 713b626b..be02c430 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt @@ -49,9 +49,12 @@ import ru.dbotthepony.kstarbound.world.SystemWorld import ru.dbotthepony.kstarbound.world.SystemWorldLocation import ru.dbotthepony.kstarbound.world.UniversePos import java.util.UUID +import java.util.concurrent.CompletableFuture import java.util.concurrent.Future import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.locks.ReentrantLock import java.util.stream.Collectors import kotlin.collections.HashMap import kotlin.math.min @@ -523,6 +526,15 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn // set to true so failed connection attempts don't appear in chat private var announcedDisconnect = true private val isDisconnecting = AtomicBoolean() + private val disconnectSignal = CompletableFuture() + + fun waitDisconnect(howLong: Long, timeUnit: TimeUnit): Boolean { + try { + return disconnectSignal.get(howLong, timeUnit) + } catch (err: Throwable) { + return false + } + } private suspend fun disconnect0(reason: String) { // make server forget shipworld right away @@ -601,8 +613,11 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn channel.flush() channel.close() } + + disconnectSignal.complete(true) } catch (err: Throwable) { LOGGER.error("Exception while disconnecting $this", err) + disconnectSignal.complete(false) } finally { // don't leave residue entities in worlds if we encountered an exception tracker?.remove("Disconnect: $reason") @@ -614,7 +629,9 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn override fun disconnect(reason: String) { if (isDisconnecting.compareAndSet(false, true)) { - server.scope.launch { disconnect0(reason) } + // launch with global scope because server will pause its own thread + // and wait for us to disconnect (if server is shutting down) + Starbound.GLOBAL_SCOPE.launch { disconnect0(reason) } } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt index 61418538..97109712 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt @@ -467,8 +467,10 @@ sealed class StarboundServer(val root: File) : BlockableEventLoop("Server thread } fun notifyWorldUnloaded(worldID: WorldID) { - execute { - worlds.remove(worldID) + if (!isShutdown) { + execute { + worlds.remove(worldID) + } } } @@ -516,7 +518,11 @@ sealed class StarboundServer(val root: File) : BlockableEventLoop("Server thread return channels.connections.firstOrNull { it.uuid == uuid } } - protected abstract fun close0() + protected open fun close() { + channels.close() + shutdown() + } + protected abstract fun tick0(delta: Double) private fun tick(delta: Double) { @@ -560,7 +566,6 @@ sealed class StarboundServer(val root: File) : BlockableEventLoop("Server thread database.commit() databaseCleanable.clean() universe.close() - close0() } companion object { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt index a460f582..628acf91 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt @@ -207,8 +207,10 @@ class ServerWorld private constructor( clients.forEach { LOGGER.info("Kicking ${it.client.alias()} off dying world") - it.remove("World shutting down", setReturnWarp = false) - it.client.enqueueWarp(WarpAlias.Return) + it.remove("World shutting down", setReturnWarp = !uncleanShutdown) + + if (!server.isShutdown) + it.client.enqueueWarp(WarpAlias.Return) } if (!uncleanShutdown) {