From 2f6b0a9a0f5d3bbf4c7c38abb31ab79402daf43f Mon Sep 17 00:00:00 2001
From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com>
Date: Fri, 15 Jan 2021 18:03:08 +0000
Subject: [PATCH] world/Reboot refactor (#15268)
* world/Reboot refactor
* Re adds noises
* Im being punished by my own CI!
---
code/controllers/subsystem/ticker.dm | 41 +++++++++++++++-
code/controllers/subsystem/vote.dm | 2 +-
code/game/gamemodes/nuclear/nuclearbomb.dm | 2 +-
code/game/world.dm | 50 +++++++-------------
code/modules/admin/admin.dm | 54 ++++++++++++++++++----
code/modules/admin/topic.dm | 2 +
code/modules/unit_tests/unit_test.dm | 2 +-
7 files changed, 104 insertions(+), 49 deletions(-)
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index c5f8ebc1380..44b1593b395 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -59,6 +59,8 @@ SUBSYSTEM_DEF(ticker)
var/mode_result = "undefined"
/// Server end state (Did we end properly or reboot or nuke or what)
var/end_state = "undefined"
+ /// Time the real reboot kicks in
+ var/real_reboot_time = 0
/datum/controller/subsystem/ticker/Initialize()
login_music = pick(\
@@ -128,9 +130,9 @@ SUBSYSTEM_DEF(ticker)
spawn(50)
if(mode.station_was_nuked)
- world.Reboot("Station destroyed by Nuclear Device.", "nuke")
+ reboot_helper("Station destroyed by Nuclear Device.", "nuke")
else
- world.Reboot("Round ended.", "proper completion")
+ reboot_helper("Round ended.", "proper completion")
/datum/controller/subsystem/ticker/proc/setup()
cultdat = setupcult()
@@ -541,3 +543,38 @@ SUBSYSTEM_DEF(ticker)
var/datum/trade_destination/D = new loc_type
GLOB.weighted_randomevent_locations[D] = D.viable_random_events.len
GLOB.weighted_mundaneevent_locations[D] = D.viable_mundane_events.len
+
+// Easy handler to make rebooting the world not a massive sleep in world/Reboot()
+/datum/controller/subsystem/ticker/proc/reboot_helper(reason, end_string, delay)
+ // Admins delayed round end. Just alert and dont bother with anything else.
+ if(delay_end)
+ to_chat(world, "An admin has delayed the round end.")
+ return
+
+ if(!isnull(delay))
+ // Delay time was present. Use that.
+ delay = max(0, delay)
+ else
+ // Use default restart timeout
+ delay = restart_timeout
+
+ to_chat(world, "Rebooting world in [delay/10] [delay > 10 ? "seconds" : "second"]. [reason]")
+
+ real_reboot_time = world.time + delay
+ UNTIL(world.time > real_reboot_time) // Hold it here
+
+ // And if we re-delayed, bail again
+ if(delay_end)
+ to_chat(world, "Reboot was cancelled by an admin.")
+ return
+
+ if(end_string)
+ end_state = end_string
+
+ // Play a haha funny noise
+ var/round_end_sound = pick(GLOB.round_end_sounds)
+ var/sound_length = GLOB.round_end_sounds[round_end_sound]
+ world << round_end_sound
+ sleep(sound_length)
+
+ world.Reboot()
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index b5928e09859..a60f4835316 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -180,7 +180,7 @@ SUBSYSTEM_DEF(vote)
if(restart)
- world.Reboot("Restart vote successful.", "restart vote")
+ SSticker.reboot_helper("Restart vote successful.", "restart vote")
return .
diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm
index 58a197c3302..99e9dfaeb35 100644
--- a/code/game/gamemodes/nuclear/nuclearbomb.dm
+++ b/code/game/gamemodes/nuclear/nuclearbomb.dm
@@ -382,7 +382,7 @@ GLOBAL_VAR(bomb_set)
//kinda shit but I couldn't get permission to do what I wanted to do.
if(!SSticker.mode.check_finished())//If the mode does not deal with the nuke going off so just reboot because everyone is stuck as is
- world.Reboot("Station destroyed by Nuclear Device.", "nuke - unhandled ending")
+ SSticker.reboot_helper("Station destroyed by Nuclear Device.", "nuke - unhandled ending")
return
return
diff --git a/code/game/world.dm b/code/game/world.dm
index 9cfb62b1f82..d2fb442c5f9 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -102,9 +102,9 @@ GLOBAL_LIST_EMPTY(world_topic_handlers)
wth = new wth()
return wth.invoke(input)
-/world/Reboot(var/reason, end_string, var/time)
+/world/Reboot(reason, fast_track = FALSE)
//special reboot, do none of the normal stuff
- if(reason == 1) // Do NOT change this to if(reason). You WILL break the entirety of world rebooting
+ if((reason == 1) || fast_track) // Do NOT change this to if(reason). You WILL break the entirety of world rebooting
if(usr)
if(!check_rights(R_SERVER))
message_admins("[key_name_admin(usr)] attempted to restart the server via the Profiler, without access.")
@@ -112,71 +112,53 @@ GLOBAL_LIST_EMPTY(world_topic_handlers)
return
message_admins("[key_name_admin(usr)] has requested an immediate world restart via client side debugging tools")
log_admin("[key_name(usr)] has requested an immediate world restart via client side debugging tools")
- spawn(0)
to_chat(world, "Rebooting world immediately due to host request")
rustg_log_close_all() // Past this point, no logging procs can be used, at risk of data loss.
- TgsReboot()
+ // Now handle a reboot
if(config && config.shutdown_on_reboot)
sleep(0)
if(GLOB.shutdown_shell_command)
shell(GLOB.shutdown_shell_command)
del(world)
+ TgsEndProcess() // We want to shutdown on reboot. That means kill our TGS process "gracefully", instead of the watchdog crying
return
else
+ TgsReboot() // Tell TGS we did a reboot
return ..(1)
- var/delay
- if(!isnull(time))
- delay = max(0,time)
- else
- delay = SSticker.restart_timeout
- if(SSticker.delay_end)
- to_chat(world, "An admin has delayed the round end.")
- return
- to_chat(world, "Rebooting world in [delay/10] [delay > 10 ? "seconds" : "second"]. [reason]")
-
- var/round_end_sound = pick(GLOB.round_end_sounds)
- var/sound_length = GLOB.round_end_sounds[round_end_sound]
- if(delay > sound_length) // If there's time, play the round-end sound before rebooting
- spawn(delay - sound_length)
- if(!SSticker.delay_end)
- world << round_end_sound
- sleep(delay)
- if(SSticker.delay_end)
- to_chat(world, "Reboot was cancelled by an admin.")
- return
- log_game("Rebooting world. [reason]")
- //kick_clients_in_lobby("The round came to an end with you in the lobby.", 1)
-
- if(end_string)
- SSticker.end_state = end_string
-
- Master.Shutdown() //run SS shutdowns
- rustg_log_close_all() // Past this point, no logging procs can be used, at risk of data loss.
- TgsReboot()
+ // If we got here, we are in a "normal" reboot
+ Master.Shutdown() // Shutdown subsystems
+ // If we were running unit tests, finish that run
#ifdef UNIT_TESTS
FinishTestRun()
return
#endif
+ // If we had an update or pending TM, set a 60 second timeout
var/secs_before_auto_reconnect = 10
if(GLOB.pending_server_update)
secs_before_auto_reconnect = 60
to_chat(world, "Reboot will take a little longer, due to pending updates.")
+ // Send the reboot banner to all players
for(var/client/C in GLOB.clients)
C << output(list2params(list(secs_before_auto_reconnect)), "browseroutput:reboot")
- if(config.server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite
+ if(config.server) // If you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite
C << link("byond://[config.server]")
+ // And begin the real shutdown
+ rustg_log_close_all() // Past this point, no logging procs can be used, at risk of data loss.
if(config && config.shutdown_on_reboot)
sleep(0)
if(GLOB.shutdown_shell_command)
shell(GLOB.shutdown_shell_command)
+ rustg_log_close_all() // Past this point, no logging procs can be used, at risk of data loss.
del(world)
+ TgsEndProcess() // We want to shutdown on reboot. That means kill our TGS process "gracefully", instead of the watchdog crying
return
else
+ TgsReboot() // We did a normal reboot. Tell TGS we did a normal reboot.
..(0)
/world/proc/load_mode()
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index a91eb296413..1d63a00874f 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -363,16 +363,47 @@ GLOBAL_VAR_INIT(nologevent, 0)
if(!check_rights(R_SERVER))
return
- var/delay = input("What delay should the restart have (in seconds)?", "Restart Delay", 5) as num|null
- if(isnull(delay))
- return
- else
- delay = delay * 10
- message_admins("[key_name_admin(usr)] has initiated a server restart with a delay of [delay/10] seconds")
- log_admin("[key_name(usr)] has initiated a server restart with a delay of [delay/10] seconds")
- SSticker.delay_end = 0
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Reboot Server") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- world.Reboot("Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key].", "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", delay)
+ // Give an extra popup if they are rebooting a live server
+ var/is_live_server = TRUE
+ if(usr.client.is_connecting_from_localhost())
+ is_live_server = FALSE
+
+ var/list/options = list("Regular Restart", "Hard Restart")
+ if(world.TgsAvailable()) // TGS lets you kill the process entirely
+ options += "Terminate Process (Kill and restart DD)"
+
+ var/result = input(usr, "Select reboot method", "World Reboot", options[1]) as null|anything in options
+
+ if(is_live_server)
+ if(alert(usr, "WARNING: THIS IS A LIVE SERVER, NOT A LOCAL TEST SERVER. DO YOU STILL WANT TO RESTART","This server is live","Restart","Cancel") != "Restart")
+ return FALSE
+
+ if(result)
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "Reboot World") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ var/init_by = "Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]."
+ switch(result)
+
+ if("Regular Restart")
+ var/delay = input("What delay should the restart have (in seconds)?", "Restart Delay", 5) as num|null
+ if(!delay)
+ return FALSE
+
+
+ // These are pasted each time so that they dont false send if reboot is cancelled
+ message_admins("[key_name_admin(usr)] has initiated a server restart of type [result]")
+ log_admin("[key_name(usr)] has initiated a server restart of type [result]")
+ SSticker.delay_end = FALSE // We arent delayed anymore
+ SSticker.reboot_helper(init_by, "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", delay * 10)
+
+ if("Hard Restart")
+ message_admins("[key_name_admin(usr)] has initiated a server restart of type [result]")
+ log_admin("[key_name(usr)] has initiated a server restart of type [result]")
+ world.Reboot(fast_track = TRUE)
+
+ if("Terminate Process (Kill and restart DD)")
+ message_admins("[key_name_admin(usr)] has initiated a server restart of type [result]")
+ log_admin("[key_name(usr)] has initiated a server restart of type [result]")
+ world.TgsEndProcess() // Just nuke the entire process if we are royally fucked
/datum/admins/proc/end_round()
set category = "Server"
@@ -576,6 +607,8 @@ GLOBAL_VAR_INIT(nologevent, 0)
SSticker.delay_end = !SSticker.delay_end
log_admin("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].")
message_admins("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].", 1)
+ if(SSticker.delay_end)
+ SSticker.real_reboot_time = 0 // Immediately show the "Admin delayed round end" message
return //alert("Round end delayed", null, null, null, null, null)
if(SSticker.ticker_going)
SSticker.ticker_going = FALSE
@@ -883,3 +916,4 @@ GLOBAL_VAR_INIT(gamma_ship_location, 1) // 0 = station , 1 = space
continue
result[1]++
return result
+
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index ae72433bd77..fb21ec0380b 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -317,6 +317,8 @@
SSticker.delay_end = !SSticker.delay_end
log_admin("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].")
message_admins("[key_name_admin(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].", 1)
+ if(SSticker.delay_end)
+ SSticker.real_reboot_time = 0 // If they set this at round end, show the "Reboot was cancelled by an admin" message instantly
href_list["secretsadmin"] = "check_antagonist"
else if(href_list["simplemake"])
diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm
index 036ee996781..8fd15ce81cc 100644
--- a/code/modules/unit_tests/unit_test.dm
+++ b/code/modules/unit_tests/unit_test.dm
@@ -70,7 +70,7 @@ GLOBAL_VAR(test_log)
CHECK_TICK
- world.Reboot("Unit Test Reboot", "tests ended", 0)
+ SSticker.reboot_helper("Unit Test Reboot", "tests ended", 0)
// OTHER MISC PROCS RELATED TO UNIT TESTS //