mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-05-06 22:35:11 +02:00
feat(shutdown): passive drain and rename DisableSoftCrash
Shutdown now proceeds in three phases: close listeners immediately on signal, broadcast the in-game countdown, wait up to ShutdownDrainSeconds (default 30) for sessions to disconnect naturally via DrainPassive, then force-close any stragglers. This prevents players from entering new quests after the countdown starts, and lets mid-quest sessions finish saving without being killed mid-write. A second SIGINT during passive drain cancels it so the force-close phase runs immediately. Renamed DisableSoftCrash -> DisableShutdownCountdown since the flag controls the countdown, not crash behaviour. Existing config.json files keep working via a Viper alias on the legacy key. Closes #179.
This commit is contained in:
@@ -64,30 +64,31 @@ func (m Mode) String() string {
|
||||
|
||||
// Config holds the global server-wide config.
|
||||
type Config struct {
|
||||
Host string `mapstructure:"Host"`
|
||||
BinPath string `mapstructure:"BinPath"`
|
||||
Language string
|
||||
DisableSoftCrash bool // Disables the 'Press Return to exit' dialog allowing scripts to reboot the server automatically
|
||||
ShutdownCountdownSeconds int // Seconds to count down before shutting down (default 10; ignored when DisableSoftCrash is true)
|
||||
HideLoginNotice bool // Hide the Erupe notice on login
|
||||
LoginNotices []string // MHFML string of the login notices displayed
|
||||
PatchServerManifest string // Manifest patch server override
|
||||
PatchServerFile string // File patch server override
|
||||
DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion
|
||||
DisableSaveIntegrityCheck bool // Skip SHA-256 hash verification on load (needed for cross-server save transfers)
|
||||
ClientMode string
|
||||
RealClientMode Mode
|
||||
QuestCacheExpiry int // Number of seconds to keep quest data cached
|
||||
CommandPrefix string // The prefix for commands
|
||||
AutoCreateAccount bool // Automatically create accounts if they don't exist
|
||||
LoopDelay int // Delay in milliseconds between each loop iteration
|
||||
DefaultCourses []uint16
|
||||
EarthStatus int32
|
||||
EarthID int32
|
||||
EarthMonsters []int32
|
||||
SaveDumps SaveDumpOptions
|
||||
Screenshots ScreenshotsOptions
|
||||
Capture CaptureOptions
|
||||
Host string `mapstructure:"Host"`
|
||||
BinPath string `mapstructure:"BinPath"`
|
||||
Language string
|
||||
DisableShutdownCountdown bool `mapstructure:"DisableShutdownCountdown"` // Skip the in-game shutdown countdown (lets scripts restart the server unattended). Previously named DisableSoftCrash — legacy key still accepted.
|
||||
ShutdownCountdownSeconds int // Seconds to count down before shutting down (default 10; ignored when DisableShutdownCountdown is true)
|
||||
ShutdownDrainSeconds int // Additional seconds to wait for sessions to disconnect naturally before force-closing (default 30)
|
||||
HideLoginNotice bool // Hide the Erupe notice on login
|
||||
LoginNotices []string // MHFML string of the login notices displayed
|
||||
PatchServerManifest string // Manifest patch server override
|
||||
PatchServerFile string // File patch server override
|
||||
DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion
|
||||
DisableSaveIntegrityCheck bool // Skip SHA-256 hash verification on load (needed for cross-server save transfers)
|
||||
ClientMode string
|
||||
RealClientMode Mode
|
||||
QuestCacheExpiry int // Number of seconds to keep quest data cached
|
||||
CommandPrefix string // The prefix for commands
|
||||
AutoCreateAccount bool // Automatically create accounts if they don't exist
|
||||
LoopDelay int // Delay in milliseconds between each loop iteration
|
||||
DefaultCourses []uint16
|
||||
EarthStatus int32
|
||||
EarthID int32
|
||||
EarthMonsters []int32
|
||||
SaveDumps SaveDumpOptions
|
||||
Screenshots ScreenshotsOptions
|
||||
Capture CaptureOptions
|
||||
|
||||
DebugOptions DebugOptions
|
||||
GameplayOptions GameplayOptions
|
||||
@@ -356,6 +357,11 @@ func registerDefaults() {
|
||||
viper.SetDefault("AutoCreateAccount", true)
|
||||
viper.SetDefault("LoopDelay", 50)
|
||||
viper.SetDefault("ShutdownCountdownSeconds", 10)
|
||||
viper.SetDefault("ShutdownDrainSeconds", 30)
|
||||
// Back-compat: old configs use DisableSoftCrash. RegisterAlias makes Viper
|
||||
// treat reads/writes of the old key as the new key, so existing
|
||||
// config.json files keep working without modification.
|
||||
viper.RegisterAlias("DisableSoftCrash", "DisableShutdownCountdown")
|
||||
viper.SetDefault("DefaultCourses", []uint16{1, 23, 24})
|
||||
viper.SetDefault("EarthMonsters", []int32{0, 0, 0, 0})
|
||||
|
||||
|
||||
@@ -142,26 +142,26 @@ func TestLoadConfigDefaultModeWhenInvalid(t *testing.T) {
|
||||
// TestConfigStruct tests Config structure creation with all fields
|
||||
func TestConfigStruct(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Host: "localhost",
|
||||
BinPath: "/opt/erupe",
|
||||
Language: "en",
|
||||
DisableSoftCrash: false,
|
||||
HideLoginNotice: false,
|
||||
LoginNotices: []string{"Welcome"},
|
||||
PatchServerManifest: "http://patch.example.com/manifest",
|
||||
PatchServerFile: "http://patch.example.com/files",
|
||||
Host: "localhost",
|
||||
BinPath: "/opt/erupe",
|
||||
Language: "en",
|
||||
DisableShutdownCountdown: false,
|
||||
HideLoginNotice: false,
|
||||
LoginNotices: []string{"Welcome"},
|
||||
PatchServerManifest: "http://patch.example.com/manifest",
|
||||
PatchServerFile: "http://patch.example.com/files",
|
||||
DeleteOnSaveCorruption: false,
|
||||
DisableSaveIntegrityCheck: false,
|
||||
ClientMode: "ZZ",
|
||||
RealClientMode: ZZ,
|
||||
QuestCacheExpiry: 3600,
|
||||
CommandPrefix: "!",
|
||||
AutoCreateAccount: false,
|
||||
LoopDelay: 100,
|
||||
DefaultCourses: []uint16{1, 2, 3},
|
||||
EarthStatus: 0,
|
||||
EarthID: 0,
|
||||
EarthMonsters: []int32{100, 101, 102},
|
||||
ClientMode: "ZZ",
|
||||
RealClientMode: ZZ,
|
||||
QuestCacheExpiry: 3600,
|
||||
CommandPrefix: "!",
|
||||
AutoCreateAccount: false,
|
||||
LoopDelay: 100,
|
||||
DefaultCourses: []uint16{1, 2, 3},
|
||||
EarthStatus: 0,
|
||||
EarthID: 0,
|
||||
EarthMonsters: []int32{100, 101, 102},
|
||||
SaveDumps: SaveDumpOptions{
|
||||
Enabled: true,
|
||||
RawEnabled: false,
|
||||
|
||||
@@ -149,26 +149,26 @@ func TestGetOutboundIP4(t *testing.T) {
|
||||
// TestConfigStructTypes verifies Config struct fields have correct types
|
||||
func TestConfigStructTypes(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Host: "localhost",
|
||||
BinPath: "/path/to/bin",
|
||||
Language: "en",
|
||||
DisableSoftCrash: false,
|
||||
HideLoginNotice: false,
|
||||
LoginNotices: []string{"Notice"},
|
||||
PatchServerManifest: "http://patch.example.com",
|
||||
PatchServerFile: "http://files.example.com",
|
||||
Host: "localhost",
|
||||
BinPath: "/path/to/bin",
|
||||
Language: "en",
|
||||
DisableShutdownCountdown: false,
|
||||
HideLoginNotice: false,
|
||||
LoginNotices: []string{"Notice"},
|
||||
PatchServerManifest: "http://patch.example.com",
|
||||
PatchServerFile: "http://files.example.com",
|
||||
DeleteOnSaveCorruption: false,
|
||||
DisableSaveIntegrityCheck: false,
|
||||
ClientMode: "ZZ",
|
||||
RealClientMode: ZZ,
|
||||
QuestCacheExpiry: 3600,
|
||||
CommandPrefix: "!",
|
||||
AutoCreateAccount: false,
|
||||
LoopDelay: 100,
|
||||
DefaultCourses: []uint16{1, 2, 3},
|
||||
EarthStatus: 1,
|
||||
EarthID: 1,
|
||||
EarthMonsters: []int32{1, 2, 3},
|
||||
ClientMode: "ZZ",
|
||||
RealClientMode: ZZ,
|
||||
QuestCacheExpiry: 3600,
|
||||
CommandPrefix: "!",
|
||||
AutoCreateAccount: false,
|
||||
LoopDelay: 100,
|
||||
DefaultCourses: []uint16{1, 2, 3},
|
||||
EarthStatus: 1,
|
||||
EarthID: 1,
|
||||
EarthMonsters: []int32{1, 2, 3},
|
||||
SaveDumps: SaveDumpOptions{
|
||||
Enabled: true,
|
||||
RawEnabled: false,
|
||||
|
||||
Reference in New Issue
Block a user