Skip to content

Commit

Permalink
Added graceful shutdown of game server
Browse files Browse the repository at this point in the history
  • Loading branch information
Martomate committed Dec 26, 2024
1 parent cf53770 commit 4cf4b8c
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 3 deletions.
14 changes: 14 additions & 0 deletions client/src/main/scala/hexacraft/client/GameClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ class GameClient(
}

private def logout(): Unit = {
if isOnline then {
println("Logging out...")
}
isLoggingOut = true
Future(socket.sendPacket(NetworkPacket.Logout)) // use Future to make sure it does not block
eventHandler.send(GameClient.Event.GameQuit)
Expand Down Expand Up @@ -521,6 +524,8 @@ class GameClient(
})
if currentTickFut.isEmpty then return // the first tick has no server data to act on

var serverIsShuttingDown = false

try {
val Seq(playerNbt, worldEventsNbtPacket, worldLoadingEventsNbt) =
Await.result(currentTickFut.get, Duration(1, TimeUnit.SECONDS))
Expand All @@ -540,6 +545,11 @@ class GameClient(
player.flying = syncedPlayer.flying

val worldEventsNbt = worldEventsNbtPacket.asMap.get
if worldEventsNbt.getBoolean("server_shutting_down", false) then {
println("The server is shutting down")
serverIsShuttingDown = true
}

val blockUpdatesNbtList = worldEventsNbt.getList("block_updates").getOrElse(Seq()).map(_.asMap.get)
val blockUpdates =
for u <- blockUpdatesNbtList
Expand Down Expand Up @@ -715,6 +725,10 @@ class GameClient(
logout()
case e => throw e
}

if serverIsShuttingDown then {
logout()
}
}

private def updateSoundListener(): Unit = {
Expand Down
23 changes: 20 additions & 3 deletions server/src/main/scala/hexacraft/server/GameServer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class GameServer(
world: ServerWorld
)(using CylinderSize) {

private var isShuttingDown: Boolean = false

private val players: mutable.LongMap[PlayerData] = mutable.LongMap.empty

private val collisionDetector: CollisionDetector = new CollisionDetector(world)
Expand Down Expand Up @@ -266,7 +268,16 @@ class GameServer(
notifyPlayersAboutBlockUpdate(coords, BlockState.Air)
}

def shutdown(): Unit = {
isShuttingDown = true

if players.nonEmpty then {
Thread.sleep(1000) // give clients a chance to logout
}
}

def unload(): Unit = {
shutdown()
stop()

savePlayers()
Expand Down Expand Up @@ -322,7 +333,14 @@ class GameServer(

packet match {
case Login(id, name) =>
if !players.contains(clientId) then {
if isShuttingDown then {
return Some(
Nbt.makeMap(
"success" -> Nbt.ByteTag(false),
"error" -> Nbt.StringTag("server is shutting down")
)
)
} else if !players.contains(clientId) then {
if !isOnline && players.nonEmpty then {
return Some(
Nbt.makeMap(
Expand Down Expand Up @@ -366,11 +384,9 @@ class GameServer(
val (playerId, otherData) = otherPlayer
if playerId != clientId then {
otherData.entityEventsWaitingToBeSent.synchronized {
println(s"Sending Login message about new player: $clientId")
otherData.entityEventsWaitingToBeSent += entity.id -> EntityEvent.Spawned(entity.toNBT)
}
playerData.entityEventsWaitingToBeSent.synchronized {
println(s"Sending Login message about existing player: $playerId")
playerData.entityEventsWaitingToBeSent += otherData.entity.id -> EntityEvent.Spawned(
otherData.entity.toNBT
)
Expand Down Expand Up @@ -477,6 +493,7 @@ class GameServer(
val response = Nbt.emptyMap
.withField("block_updates", Nbt.ListTag(updatesNbt))
.withField("entity_events", Nbt.makeMap("ids" -> Nbt.ListTag(ids), "events" -> Nbt.ListTag(events)))
.withField("server_shutting_down", Nbt.ByteTag(isShuttingDown)) // TODO: make proper shutdown feature

Some(response)
case GetWorldLoadingEvents(maxChunksToLoad) =>
Expand Down

0 comments on commit 4cf4b8c

Please sign in to comment.