Files
Bubberstation/code/controllers/subsystem/statpanel.dm
Roxy 2603a4f3ee Add reboot countdown to stat panel (#88438)
## About The Pull Request

Rewrite ticker `Reboot` proc slightly to use a timer and callback for
the delay before the reboot, tracks this timer in the stat panel for
players to see. Also adds a verb to cancel a pending reboot independent
of the delay round end verb.

## Why It's Good For The Game

It's nice to be able to see exactly how much time is left until the
server restarts, and it's even nicer to see that the round end has been
delayed when the "admin has delayed round end" message gets buried in a
fast moving chat.

## Changelog

🆑
qol: The time until the server reboots is now visible in the status tab.
admin: Added a cancel reboot verb to the server tab.
/🆑
2024-12-22 05:46:09 +01:00

253 lines
9.4 KiB
Plaintext

SUBSYSTEM_DEF(statpanels)
name = "Stat Panels"
wait = 4
init_order = INIT_ORDER_STATPANELS
init_stage = INITSTAGE_EARLY
priority = FIRE_PRIORITY_STATPANEL
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
flags = SS_NO_INIT
var/list/currentrun = list()
var/list/global_data
var/list/mc_data
///how many subsystem fires between most tab updates
var/default_wait = 10
///how many subsystem fires between updates of the status tab
var/status_wait = 2
///how many subsystem fires between updates of the MC tab
var/mc_wait = 5
///how many full runs this subsystem has completed. used for variable rate refreshes.
var/num_fires = 0
/datum/controller/subsystem/statpanels/fire(resumed = FALSE)
if (!resumed)
num_fires++
var/datum/map_config/cached = SSmap_vote.next_map_config
global_data = list(
"Map: [SSmapping.current_map?.map_name || "Loading..."]",
cached ? "Next Map: [cached.map_name]" : null,
"Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]",
"Server Time: [time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss")]",
"Round Time: [ROUND_TIME()]",
"Station Time: [station_time_timestamp()]",
"Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)"
)
if(SSshuttle.emergency)
var/ETA = SSshuttle.emergency.getModeStr()
if(ETA)
global_data += "[ETA] [SSshuttle.emergency.getTimerStr()]"
if(SSticker.reboot_timer)
var/reboot_time = timeleft(SSticker.reboot_timer)
if(reboot_time)
global_data += "Reboot: [DisplayTimeText(reboot_time, 1)]"
// admin must have delayed round end
else if(SSticker.ready_for_reboot)
global_data += "Reboot: DELAYED"
src.currentrun = GLOB.clients.Copy()
mc_data = null
var/list/currentrun = src.currentrun
while(length(currentrun))
var/client/target = currentrun[length(currentrun)]
currentrun.len--
if(!target.stat_panel.is_ready())
continue
if(target.stat_tab == "Status" && num_fires % status_wait == 0)
set_status_tab(target)
if(!target.holder)
target.stat_panel.send_message("remove_admin_tabs")
else
target.stat_panel.send_message("update_split_admin_tabs", !!(target.prefs.toggles & SPLIT_ADMIN_TABS))
if(!("MC" in target.panel_tabs) || !("Tickets" in target.panel_tabs))
target.stat_panel.send_message("add_admin_tabs", target.holder.href_token)
if(target.stat_tab == "MC" && ((num_fires % mc_wait == 0) || target?.prefs.read_preference(/datum/preference/toggle/fast_mc_refresh)))
set_MC_tab(target)
if(target.stat_tab == "Tickets" && num_fires % default_wait == 0)
set_tickets_tab(target)
if(!length(GLOB.sdql2_queries) && ("SDQL2" in target.panel_tabs))
target.stat_panel.send_message("remove_sdql2")
else if(length(GLOB.sdql2_queries) && (target.stat_tab == "SDQL2" || !("SDQL2" in target.panel_tabs)) && num_fires % default_wait == 0)
set_SDQL2_tab(target)
if(target.mob)
var/mob/target_mob = target.mob
// Handle the action panels of the stat panel
var/update_actions = FALSE
// We're on a spell tab, update the tab so we can see cooldowns progressing and such
if(target.stat_tab in target.spell_tabs)
update_actions = TRUE
// We're not on a spell tab per se, but we have cooldown actions, and we've yet to
// set up our spell tabs at all
if(!length(target.spell_tabs) && locate(/datum/action/cooldown) in target_mob.actions)
update_actions = TRUE
if(update_actions && num_fires % default_wait == 0)
set_action_tabs(target, target_mob)
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/statpanels/proc/set_status_tab(client/target)
#if MIN_COMPILER_VERSION > 515
#warn 516 is most certainly out of beta, remove this beta notice if you haven't already
#endif
var/static/list/beta_notice = list("", "You are on the BYOND 516 beta, various UIs and such may be broken!", "Please report issues, and switch back to BYOND 515 if things are causing too many issues for you.")
if(!global_data)//statbrowser hasnt fired yet and we were called from immediate_send_stat_data()
return
target.stat_panel.send_message("update_stat", list(
"global_data" = (target.byond_version < 516) ? global_data : (global_data + beta_notice),
"ping_str" = "Ping: [round(target.lastping, 1)]ms (Average: [round(target.avgping, 1)]ms)",
"other_str" = target.mob?.get_status_tab_items(),
))
/datum/controller/subsystem/statpanels/proc/set_MC_tab(client/target)
var/turf/eye_turf = get_turf(target.eye)
var/coord_entry = COORD(eye_turf)
if(!mc_data)
generate_mc_data()
target.stat_panel.send_message("update_mc", list("mc_data" = mc_data, "coord_entry" = coord_entry))
/datum/controller/subsystem/statpanels/proc/set_tickets_tab(client/target)
var/list/ahelp_tickets = GLOB.ahelp_tickets.stat_entry()
target.stat_panel.send_message("update_tickets", ahelp_tickets)
var/datum/interview_manager/m = GLOB.interviews
// get open interview count
var/dc = 0
for (var/ckey in m.open_interviews)
var/datum/interview/current_interview = m.open_interviews[ckey]
if (current_interview && !current_interview.owner)
dc++
var/stat_string = "([m.open_interviews.len - dc] online / [dc] disconnected)"
// Prepare each queued interview
var/list/queued = list()
for (var/datum/interview/queued_interview in m.interview_queue)
queued += list(list(
"ref" = REF(queued_interview),
"status" = "\[[queued_interview.pos_in_queue]\]: [queued_interview.owner_ckey][!queued_interview.owner ? " (DC)": ""] \[INT-[queued_interview.id]\]"
))
var/list/data = list(
"status" = list(
"Active:" = "[m.open_interviews.len] [stat_string]",
"Queued:" = "[m.interview_queue.len]",
"Closed:" = "[m.closed_interviews.len]"),
"interviews" = queued
)
// Push update
target.stat_panel.send_message("update_interviews", data)
/datum/controller/subsystem/statpanels/proc/set_SDQL2_tab(client/target)
var/list/sdql2A = list()
sdql2A[++sdql2A.len] = list("", "Access Global SDQL2 List", REF(GLOB.sdql2_vv_statobj))
var/list/sdql2B = list()
for(var/datum/sdql2_query/query as anything in GLOB.sdql2_queries)
sdql2B = query.generate_stat()
sdql2A += sdql2B
target.stat_panel.send_message("update_sdql2", sdql2A)
/// Set up the various action tabs.
/datum/controller/subsystem/statpanels/proc/set_action_tabs(client/target, mob/target_mob)
var/list/actions = target_mob.get_actions_for_statpanel()
target.spell_tabs.Cut()
for(var/action_data in actions)
target.spell_tabs |= action_data[1]
target.stat_panel.send_message("update_spells", list(spell_tabs = target.spell_tabs, actions = actions))
/datum/controller/subsystem/statpanels/proc/generate_mc_data()
mc_data = list(
list("CPU:", world.cpu),
list("Instances:", "[num2text(world.contents.len, 10)]"),
list("World Time:", "[world.time]"),
list("Globals:", GLOB.stat_entry(), text_ref(GLOB)),
list("[config]:", config.stat_entry(), text_ref(config)),
list("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%)) (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)"),
list("Master Controller:", Master.stat_entry(), text_ref(Master)),
list("Failsafe Controller:", Failsafe.stat_entry(), text_ref(Failsafe)),
list("","")
)
#if defined(MC_TAB_TRACY_INFO) || defined(SPACEMAN_DMM)
var/static/tracy_dll
var/static/tracy_present
if(isnull(tracy_dll))
tracy_dll = TRACY_DLL_PATH
tracy_present = fexists(tracy_dll)
if(tracy_present)
if(GLOB.tracy_initialized)
mc_data.Insert(2, list(list("byond-tracy:", "Active (reason: [GLOB.tracy_init_reason || "N/A"])")))
else if(GLOB.tracy_init_error)
mc_data.Insert(2, list(list("byond-tracy:", "Errored ([GLOB.tracy_init_error])")))
else if(fexists(TRACY_ENABLE_PATH))
mc_data.Insert(2, list(list("byond-tracy:", "Queued for next round")))
else
mc_data.Insert(2, list(list("byond-tracy:", "Inactive")))
else
mc_data.Insert(2, list(list("byond-tracy:", "[tracy_dll] not present")))
#endif
for(var/datum/controller/subsystem/sub_system as anything in Master.subsystems)
mc_data[++mc_data.len] = list("\[[sub_system.state_letter()]][sub_system.name]", sub_system.stat_entry(), text_ref(sub_system))
mc_data[++mc_data.len] = list("Camera Net", "Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]", text_ref(GLOB.cameranet))
///immediately update the active statpanel tab of the target client
/datum/controller/subsystem/statpanels/proc/immediate_send_stat_data(client/target)
if(!target.stat_panel.is_ready())
return FALSE
if(target.stat_tab == "Status")
set_status_tab(target)
return TRUE
var/mob/target_mob = target.mob
// Handle actions
var/update_actions = FALSE
if(target.stat_tab in target.spell_tabs)
update_actions = TRUE
if(!length(target.spell_tabs) && locate(/datum/action/cooldown) in target_mob.actions)
update_actions = TRUE
if(update_actions)
set_action_tabs(target, target_mob)
return TRUE
if(!target.holder)
return FALSE
if(target.stat_tab == "MC")
set_MC_tab(target)
return TRUE
if(target.stat_tab == "Tickets")
set_tickets_tab(target)
return TRUE
if(!length(GLOB.sdql2_queries) && ("SDQL2" in target.panel_tabs))
target.stat_panel.send_message("remove_sdql2")
else if(length(GLOB.sdql2_queries) && target.stat_tab == "SDQL2")
set_SDQL2_tab(target)
/// Stat panel window declaration
/client/var/datum/tgui_window/stat_panel