feat(shutdown): graceful drain + configurable countdown

Add ShutdownAndDrain to the channel server (issue #179 non-breaking
subset): on SIGTERM/SIGINT, force-close all active sessions so that
logoutPlayer runs for each one (saves character data, cleans up stages
and semaphores), then poll until the session map empties or a 30-second
context deadline passes.  Existing Shutdown() is unchanged.

Add ShutdownCountdownSeconds int config field (default 10) alongside
DisableSoftCrash so operators can tune the broadcast countdown without
patching code.  A zero value falls back to 10 for safety.

Fix pre-existing test failures: MsgMhfAddRewardSongCount has a complete
Parse() implementation so it no longer belongs in the "NOT IMPLEMENTED"
parse test list; its handler test is updated to pass a real packet and
assert an ACK response instead of calling with nil.
This commit is contained in:
Houmgaor
2026-03-21 01:36:31 +01:00
parent 366aad0172
commit c43be33680
5 changed files with 69 additions and 12 deletions

13
main.go
View File

@@ -1,6 +1,7 @@
package main
import (
"context"
"errors"
"flag"
"fmt"
@@ -393,8 +394,12 @@ func main() {
<-sig
if !config.DisableSoftCrash {
for i := 0; i < 10; i++ {
message := fmt.Sprintf("Shutting down in %d...", 10-i)
countdown := config.ShutdownCountdownSeconds
if countdown <= 0 {
countdown = 10
}
for i := 0; i < countdown; i++ {
message := fmt.Sprintf("Shutting down in %d...", countdown-i)
for _, c := range channels {
c.BroadcastChatMessage(message)
}
@@ -409,8 +414,10 @@ func main() {
}
if config.Channel.Enabled {
drainCtx, drainCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer drainCancel()
for _, c := range channels {
c.Shutdown()
c.ShutdownAndDrain(drainCtx)
}
}