Skip to content
This repository was archived by the owner on Dec 10, 2025. It is now read-only.

Commit 0af4c5c

Browse files
authored
Merge pull request #46 from SLNE-Development/43-improve-handling-of-standalone-crash-in-server-client-architecture
Improve crash handling in standalone server-client architecture with graceful shutdown and restart logic
2 parents a3f4094 + 59396ad commit 0af4c5c

File tree

72 files changed

+984
-875
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+984
-875
lines changed

surf-cloud-api/surf-cloud-api-common/src/main/kotlin/dev/slne/surf/cloud/api/common/exceptions/FatalSurfError.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.slne.surf.cloud.api.common.exceptions
22

3+
import dev.slne.surf.cloud.api.common.util.annotation.InternalApi
34
import org.apache.commons.lang3.StringUtils
45
import org.apache.commons.text.WordUtils
56
import org.springframework.boot.ExitCodeGenerator
@@ -21,6 +22,7 @@ private const val LINE_WIDTH = 80
2122
* @property possibleSolutions Suggestions for resolving the error.
2223
* @property exitCode The exit code to return when terminating the application.
2324
*/
25+
@InternalApi
2426
class FatalSurfError private constructor(
2527
private val simpleErrorMessage: String?,
2628
private val detailedErrorMessage: String?,

surf-cloud-api/surf-cloud-api-common/src/main/kotlin/dev/slne/surf/cloud/api/common/util/spring-util.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import kotlinx.coroutines.runBlocking
66
import kotlinx.coroutines.withContext
77
import org.springframework.aop.framework.AopProxyUtils
88
import org.springframework.beans.factory.ObjectFactory
9+
import org.springframework.beans.factory.ObjectProvider
910
import org.springframework.core.MethodIntrospector
1011
import org.springframework.core.annotation.AnnotatedElementUtils
1112
import org.springframework.core.annotation.AnnotationUtils
1213
import org.springframework.core.type.AnnotationMetadata
14+
import org.springframework.stereotype.Component
1315
import org.springframework.transaction.TransactionStatus
1416
import org.springframework.transaction.support.TransactionOperations
1517
import org.springframework.util.StopWatch
@@ -117,3 +119,8 @@ suspend inline fun <T> TransactionOperations.executeAndAwait(
117119
} as T
118120
}
119121
}
122+
123+
124+
inline fun <T> ObjectProvider<T>.forEachOrdered(action: (T) -> Unit) {
125+
orderedStream().iterator().forEach(action)
126+
}

surf-cloud-api/surf-cloud-api-common/src/main/kotlin/dev/slne/surf/cloud/api/common/util/util.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,6 @@ fun Method.isSuspending() = kotlinFunction?.isSuspend == true
150150
suspend inline fun <T, R> Iterable<T>.mapAsync(crossinline transform: suspend (T) -> R): ObjectList<Deferred<R>> =
151151
coroutineScope {
152152
mapTo(mutableObjectListOf()) { async { transform(it) } }
153-
}
153+
}
154+
155+
fun <T : Any> T?.asOptional(): Optional<T> = Optional.ofNullable(this)

surf-cloud-bukkit/src/main/kotlin/dev/slne/surf/cloud/bukkit/BukkitBootstrap.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.slne.surf.cloud.bukkit
22

33
import dev.slne.surf.cloud.core.common.CloudCoreInstance.BootstrapData
4+
import dev.slne.surf.cloud.core.common.coreCloudInstance
45
import dev.slne.surf.cloud.core.common.handleEventuallyFatalError
56
import io.papermc.paper.plugin.bootstrap.BootstrapContext
67
import io.papermc.paper.plugin.bootstrap.PluginBootstrap
@@ -14,13 +15,15 @@ class BukkitBootstrap : PluginBootstrap {
1415

1516
override fun bootstrap(context: BootstrapContext): Unit = runBlocking {
1617
try {
17-
bukkitCloudInstance.bootstrap(
18+
coreCloudInstance.bootstrap(
1819
BootstrapData(
1920
dataFolder = context.dataDirectory
2021
)
2122
)
2223
} catch (e: Throwable) {
23-
e.handleEventuallyFatalError { exitProcess(it.exitCode) }
24+
e.handleEventuallyFatalError {
25+
exitProcess(it)
26+
}
2427
}
2528
}
2629

surf-cloud-bukkit/src/main/kotlin/dev/slne/surf/cloud/bukkit/BukkitMain.kt

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package dev.slne.surf.cloud.bukkit
22

33
import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin
4-
import com.github.shynixn.mccoroutine.folia.globalRegionDispatcher
54
import com.github.shynixn.mccoroutine.folia.launch
65
import dev.jorel.commandapi.CommandAPIBukkit
76
import dev.jorel.commandapi.kotlindsl.*
@@ -11,11 +10,11 @@ import dev.slne.surf.cloud.api.common.player.teleport.TeleportCause
1110
import dev.slne.surf.cloud.api.common.player.teleport.fineLocation
1211
import dev.slne.surf.cloud.api.common.player.toCloudPlayer
1312
import dev.slne.surf.cloud.api.common.server.CloudServerManager
13+
import dev.slne.surf.cloud.core.common.coreCloudInstance
1414
import dev.slne.surf.cloud.core.common.handleEventuallyFatalError
1515
import dev.slne.surf.surfapi.bukkit.api.event.listen
1616
import net.kyori.adventure.text.Component
1717
import net.kyori.adventure.text.format.NamedTextColor
18-
import org.bukkit.Bukkit
1918
import org.bukkit.Location
2019
import org.bukkit.entity.Player
2120
import org.bukkit.event.server.ServerLoadEvent
@@ -25,43 +24,33 @@ import kotlin.contracts.contract
2524
class BukkitMain : SuspendingJavaPlugin() {
2625
override suspend fun onLoadAsync() {
2726
try {
28-
bukkitCloudInstance.onLoad()
27+
coreCloudInstance.onLoad()
2928
} catch (t: Throwable) {
30-
t.handleEventuallyFatalError { Bukkit.shutdown() }
29+
t.handleEventuallyFatalError { server.restart() }
3130
}
3231
}
3332

3433
override suspend fun onEnableAsync() {
3534
try {
36-
bukkitCloudInstance.onEnable()
35+
coreCloudInstance.onEnable()
3736
} catch (t: Throwable) {
38-
t.handleEventuallyFatalError { Bukkit.shutdown() }
37+
t.handleEventuallyFatalError { server.restart() }
3938
}
4039

4140
var serverLoaded = false
4241
listen<ServerLoadEvent> {
4342
if (serverLoaded) return@listen
4443
serverLoaded = true
4544

46-
launch(globalRegionDispatcher) {
45+
launch {
4746
try {
48-
bukkitCloudInstance.afterStart()
47+
coreCloudInstance.afterStart()
4948
} catch (t: Throwable) {
50-
t.handleEventuallyFatalError { Bukkit.shutdown() }
49+
t.handleEventuallyFatalError { server.restart() }
5150
}
5251
}
5352
}
5453

55-
// TODO: does this actually delay until the server is fully loaded?
56-
// launch(globalRegionDispatcher) {
57-
// delay(1.ticks)
58-
// try {
59-
// bukkitCloudInstance.afterStart()
60-
// } catch (t: Throwable) {
61-
// t.handleEventuallyFatalError({ Bukkit.shutdown() })
62-
// }
63-
// }
64-
6554
commandAPICommand("deport") {
6655
entitySelectorArgumentOnePlayer("target")
6756
locationArgument("location")
@@ -133,7 +122,7 @@ class BukkitMain : SuspendingJavaPlugin() {
133122

134123
override suspend fun onDisableAsync() {
135124
try {
136-
bukkitCloudInstance.onDisable()
125+
coreCloudInstance.onDisable()
137126
} catch (t: Throwable) {
138127
t.handleEventuallyFatalError {}
139128
}

surf-cloud-bukkit/src/main/kotlin/dev/slne/surf/cloud/bukkit/CloudBukkitInstance.kt

Lines changed: 0 additions & 40 deletions
This file was deleted.

surf-cloud-bukkit/src/main/kotlin/dev/slne/surf/cloud/bukkit/command/PaperCommandManager.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.slne.surf.cloud.bukkit.command
22

3+
import dev.slne.surf.cloud.api.common.util.TimeLogger
34
import dev.slne.surf.cloud.bukkit.command.broadcast.broadcastCommand
45
import dev.slne.surf.cloud.bukkit.command.connection.disconnectPlayerCommand
56
import dev.slne.surf.cloud.bukkit.command.connection.timeoutPlayerCommand
@@ -9,8 +10,20 @@ import dev.slne.surf.cloud.bukkit.command.network.glistCommand
910
import dev.slne.surf.cloud.bukkit.command.network.sendCommand
1011
import dev.slne.surf.cloud.bukkit.command.network.serverCommand
1112
import dev.slne.surf.cloud.bukkit.command.playtime.playtimeCommand
13+
import dev.slne.surf.cloud.core.common.spring.CloudLifecycleAware
14+
import org.springframework.core.annotation.Order
15+
import org.springframework.stereotype.Component
16+
17+
@Component
18+
@Order(CloudLifecycleAware.MISC_PRIORITY)
19+
class PaperCommandManager : CloudLifecycleAware {
20+
21+
override suspend fun onEnable(timeLogger: TimeLogger) {
22+
timeLogger.measureStep("Register Cloud commands") {
23+
registerCommands()
24+
}
25+
}
1226

13-
object PaperCommandManager {
1427
fun registerCommands() {
1528
findCommand()
1629
serverCommand()

surf-cloud-bukkit/src/main/kotlin/dev/slne/surf/cloud/bukkit/listener/ListenerManager.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
11
package dev.slne.surf.cloud.bukkit.listener
22

3+
import dev.slne.surf.cloud.api.common.util.TimeLogger
34
import dev.slne.surf.cloud.bukkit.listener.exception.SurfFatalErrorExceptionListener
45
import dev.slne.surf.cloud.bukkit.listener.player.ConnectionListener
56
import dev.slne.surf.cloud.bukkit.listener.player.SilentDisconnectListener
67
import dev.slne.surf.cloud.bukkit.plugin
8+
import dev.slne.surf.cloud.core.common.spring.CloudLifecycleAware
79
import dev.slne.surf.surfapi.bukkit.api.event.register
810
import dev.slne.surf.surfapi.bukkit.api.nms.NmsUseWithCaution
911
import dev.slne.surf.surfapi.bukkit.api.nms.nmsBridge
1012
import org.bukkit.event.HandlerList
13+
import org.springframework.core.annotation.Order
14+
import org.springframework.stereotype.Component
1115

1216
@OptIn(NmsUseWithCaution::class)
13-
object ListenerManager {
17+
@Component
18+
@Order(CloudLifecycleAware.MISC_PRIORITY)
19+
class ListenerManager: CloudLifecycleAware {
20+
21+
override suspend fun onEnable(timeLogger: TimeLogger) {
22+
timeLogger.measureStep("Registering listeners") {
23+
registerListeners()
24+
}
25+
}
26+
27+
override suspend fun onDisable(timeLogger: TimeLogger) {
28+
timeLogger.measureStep("Unregistering listeners") {
29+
unregisterListeners()
30+
}
31+
}
1432

1533
fun registerListeners() {
1634
ConnectionListener.register()

surf-cloud-bukkit/src/main/kotlin/dev/slne/surf/cloud/bukkit/listener/exception/SurfFatalErrorExceptionListener.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import org.bukkit.event.EventHandler
88
import org.bukkit.event.Listener
99

1010
object SurfFatalErrorExceptionListener : Listener {
11-
private val handler = { _: FatalSurfError -> Bukkit.shutdown() }
11+
// private val handler = { _: FatalSurfError -> Bukkit.shutdown() }
1212

13-
@EventHandler
14-
fun onServerException(event: ServerExceptionEvent) {
15-
val e = event.exception
16-
if (e.handle()) return
17-
if (e.cause?.handle() == true) return
18-
}
19-
20-
private fun Throwable.handle() = handleEventuallyFatalError(false, false, handler)
13+
// @EventHandler
14+
// fun onServerException(event: ServerExceptionEvent) {
15+
// val e = event.exception
16+
// if (e.handle()) return
17+
// if (e.cause?.handle() == true) return
18+
// }
19+
//
20+
// private fun Throwable.handle() = handleEventuallyFatalError(false, false, handler)
2121

2222
}

surf-cloud-bukkit/src/main/kotlin/dev/slne/surf/cloud/bukkit/netty/BukkitCloudClientNettyManagerImpl.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dev.slne.surf.cloud.bukkit.netty
33
import com.google.auto.service.AutoService
44
import dev.slne.surf.cloud.api.client.netty.CloudClientNettyManager
55
import dev.slne.surf.cloud.core.client.netty.CommonCloudClientNettyManagerImpl
6+
import dev.slne.surf.cloud.core.common.util.bean
67
import dev.slne.surf.cloud.core.common.util.checkInstantiationByServiceLoader
78

89
@AutoService(CloudClientNettyManager::class)
@@ -11,5 +12,5 @@ class BukkitCloudClientNettyManagerImpl : CommonCloudClientNettyManagerImpl() {
1112
checkInstantiationByServiceLoader()
1213
}
1314

14-
override val client get() = BukkitNettyManager.nettyClient
15+
override val client get() = bean<BukkitNettyManager>().nettyClient
1516
}

0 commit comments

Comments
 (0)