[MIRROR] refactors statpanel to use tgui API [MDB IGNORE] (#13646)

* refactors statpanel to use tgui API

* fix

Co-authored-by: magatsuchi <88991542+magatsuchi@users.noreply.github.com>
Co-authored-by: Tom <8881105+tf-4@users.noreply.github.com>
This commit is contained in:
SkyratBot
2022-05-20 16:02:16 +02:00
committed by GitHub
parent 2856b73bc9
commit a079903ecb
20 changed files with 1418 additions and 1420 deletions

View File

@@ -1,8 +0,0 @@
/// Bare minimum required verbs for stat panel operation
GLOBAL_LIST_INIT(stat_panel_verbs, list(
/client/verb/set_tab,
/client/verb/send_tabs,
/client/verb/remove_tabs,
/client/verb/reset_tabs,
/client/verb/panel_ready
))

View File

@@ -43,9 +43,8 @@
for(var/thing in verbs_list) for(var/thing in verbs_list)
var/procpath/verb_to_add = thing var/procpath/verb_to_add = thing
output_list[++output_list.len] = list(verb_to_add.category, verb_to_add.name) output_list[++output_list.len] = list(verb_to_add.category, verb_to_add.name)
output_list = url_encode(json_encode(output_list))
target << output("[output_list];", "statbrowser:add_verb_list") target.stat_panel.send_message("add_verb_list", output_list)
/** /**
* handles removing verb and sending it to browser to update, use this for removing verbs * handles removing verb and sending it to browser to update, use this for removing verbs
@@ -91,6 +90,5 @@
for(var/thing in verbs_list) for(var/thing in verbs_list)
var/procpath/verb_to_remove = thing var/procpath/verb_to_remove = thing
output_list[++output_list.len] = list(verb_to_remove.category, verb_to_remove.name) output_list[++output_list.len] = list(verb_to_remove.category, verb_to_remove.name)
output_list = url_encode(json_encode(output_list))
target << output("[output_list];", "statbrowser:remove_verb_list") target.stat_panel.send_message("remove_verb_list", output_list)

View File

@@ -412,7 +412,7 @@
var/turf/T = get_turf(src) var/turf/T = get_turf(src)
if(T && (isturf(loc) || isturf(src)) && user.TurfAdjacent(T)) if(T && (isturf(loc) || isturf(src)) && user.TurfAdjacent(T))
user.listed_turf = T user.listed_turf = T
user.client << output("[url_encode(json_encode(T.name))];", "statbrowser:create_listedturf") user.client.stat_panel.send_message("create_listedturf", T.name)
///The base proc of when something is right clicked on when alt is held - generally use alt_click_secondary instead ///The base proc of when something is right clicked on when alt is held - generally use alt_click_secondary instead
/atom/proc/alt_click_on_secondary(atom/A) /atom/proc/alt_click_on_secondary(atom/A)
@@ -436,7 +436,7 @@
var/turf/T = get_turf(A) var/turf/T = get_turf(A)
if(T && user.TurfAdjacent(T)) if(T && user.TurfAdjacent(T))
user.listed_turf = T user.listed_turf = T
user.client << output("[url_encode(json_encode(T.name))];", "statbrowser:create_listedturf") user.client.stat_panel.send_message("create_listedturf", T.name)
/mob/proc/TurfAdjacent(turf/T) /mob/proc/TurfAdjacent(turf/T)
return T.Adjacent(src) return T.Adjacent(src)

View File

@@ -6,14 +6,14 @@ SUBSYSTEM_DEF(statpanels)
priority = FIRE_PRIORITY_STATPANEL priority = FIRE_PRIORITY_STATPANEL
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
var/list/currentrun = list() var/list/currentrun = list()
var/encoded_global_data var/list/global_data
var/mc_data_encoded var/list/mc_data
var/list/cached_images = list() var/list/cached_images = list()
///how many subsystem fires between most tab updates ///how many subsystem fires between most tab updates
var/default_wait = 10 var/default_wait = 10
///how many subsystem fires between updates of the status tab ///how many subsystem fires between updates of the status tab
var/status_wait = 12 var/status_wait = 6
///how many subsystem fires between updates of the MC tab ///how many subsystem fires between updates of the MC tab
var/mc_wait = 5 var/mc_wait = 5
///how many full runs this subsystem has completed. used for variable rate refreshes. ///how many full runs this subsystem has completed. used for variable rate refreshes.
@@ -24,7 +24,7 @@ SUBSYSTEM_DEF(statpanels)
num_fires++ num_fires++
var/datum/map_config/cached = SSmapping.next_map_config var/datum/map_config/cached = SSmapping.next_map_config
/* SKYRAT EDIT CHANGE /* SKYRAT EDIT CHANGE
var/list/global_data = list( global_data = list(
"Map: [SSmapping.config?.map_name || "Loading..."]", "Map: [SSmapping.config?.map_name || "Loading..."]",
cached ? "Next Map: [cached.map_name]" : null, cached ? "Next Map: [cached.map_name]" : null,
"Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]", "Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]",
@@ -56,28 +56,27 @@ SUBSYSTEM_DEF(statpanels)
var/ETA = SSshuttle.emergency.getModeStr() var/ETA = SSshuttle.emergency.getModeStr()
if(ETA) if(ETA)
global_data += "[ETA] [SSshuttle.emergency.getTimerStr()]" global_data += "[ETA] [SSshuttle.emergency.getTimerStr()]"
encoded_global_data = url_encode(json_encode(global_data))
src.currentrun = GLOB.clients.Copy() src.currentrun = GLOB.clients.Copy()
mc_data_encoded = null mc_data = null
var/list/currentrun = src.currentrun var/list/currentrun = src.currentrun
while(length(currentrun)) while(length(currentrun))
var/client/target = currentrun[length(currentrun)] var/client/target = currentrun[length(currentrun)]
currentrun.len-- currentrun.len--
if(!target.statbrowser_ready) if(!target.stat_panel.is_ready())
continue continue
if(target.stat_tab == "Status" && num_fires % status_wait == 0) if(target.stat_tab == "Status" && num_fires % status_wait == 0)
set_status_tab(target) set_status_tab(target)
if(!target.holder) if(!target.holder)
target << output("", "statbrowser:remove_admin_tabs") target.stat_panel.send_message("remove_admin_tabs")
else else
target << output("[!!(target.prefs.toggles & SPLIT_ADMIN_TABS)]", "statbrowser:update_split_admin_tabs") 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)) if(!("MC" in target.panel_tabs) || !("Tickets" in target.panel_tabs))
target << output("[url_encode(target.holder.href_token)]", "statbrowser:add_admin_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))) if(target.stat_tab == "MC" && ((num_fires % mc_wait == 0) || target?.prefs.read_preference(/datum/preference/toggle/fast_mc_refresh)))
set_MC_tab(target) set_MC_tab(target)
@@ -86,7 +85,7 @@ SUBSYSTEM_DEF(statpanels)
set_tickets_tab(target) set_tickets_tab(target)
if(!length(GLOB.sdql2_queries) && ("SDQL2" in target.panel_tabs)) if(!length(GLOB.sdql2_queries) && ("SDQL2" in target.panel_tabs))
target << output("", "statbrowser:remove_sdql2") 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) else if(length(GLOB.sdql2_queries) && (target.stat_tab == "SDQL2" || !("SDQL2" in target.panel_tabs)) && num_fires % default_wait == 0)
set_SDQL2_tab(target) set_SDQL2_tab(target)
@@ -97,9 +96,10 @@ SUBSYSTEM_DEF(statpanels)
if(num_fires % default_wait == 0) if(num_fires % default_wait == 0)
set_spells_tab(target, target_mob) set_spells_tab(target, target_mob)
if(target_mob?.listed_turf && num_fires % default_wait == 0) if(target_mob?.listed_turf && num_fires % default_wait == 0)
if(!target_mob.TurfAdjacent(target_mob.listed_turf)) if(!target_mob.TurfAdjacent(target_mob.listed_turf) || isnull(target_mob.listed_turf))
target << output("", "statbrowser:remove_listedturf") target.stat_panel.send_message("remove_listedturf")
target_mob.listed_turf = null target_mob.listed_turf = null
else if(target.stat_tab == target_mob?.listed_turf.name || !(target_mob?.listed_turf.name in target.panel_tabs)) else if(target.stat_tab == target_mob?.listed_turf.name || !(target_mob?.listed_turf.name in target.panel_tabs))
@@ -109,23 +109,25 @@ SUBSYSTEM_DEF(statpanels)
return return
/datum/controller/subsystem/statpanels/proc/set_status_tab(client/target) /datum/controller/subsystem/statpanels/proc/set_status_tab(client/target)
if(!encoded_global_data)//statbrowser hasnt fired yet and we were called from immediate_send_stat_data() if(!global_data)//statbrowser hasnt fired yet and we were called from immediate_send_stat_data()
return return
var/ping_str = url_encode("Ping: [round(target.lastping, 1)]ms (Average: [round(target.avgping, 1)]ms)") target.stat_panel.send_message("update_stat", list(
var/other_str = url_encode(json_encode(target.mob?.get_status_tab_items())) global_data = global_data,
target << output("[encoded_global_data];[ping_str];[other_str]", "statbrowser:update") 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) /datum/controller/subsystem/statpanels/proc/set_MC_tab(client/target)
var/turf/eye_turf = get_turf(target.eye) var/turf/eye_turf = get_turf(target.eye)
var/coord_entry = url_encode(COORD(eye_turf)) var/coord_entry = COORD(eye_turf)
if(!mc_data_encoded) if(!mc_data)
generate_mc_data() generate_mc_data()
target << output("[mc_data_encoded];[coord_entry]", "statbrowser:update_mc") 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) /datum/controller/subsystem/statpanels/proc/set_tickets_tab(client/target)
var/list/ahelp_tickets = GLOB.ahelp_tickets.stat_entry() var/list/ahelp_tickets = GLOB.ahelp_tickets.stat_entry()
target << output("[url_encode(json_encode(ahelp_tickets))];", "statbrowser:update_tickets") target.stat_panel.send_message("update_tickets", ahelp_tickets)
var/datum/interview_manager/m = GLOB.interviews var/datum/interview_manager/m = GLOB.interviews
// get open interview count // get open interview count
@@ -153,7 +155,7 @@ SUBSYSTEM_DEF(statpanels)
) )
// Push update // Push update
target << output("[url_encode(json_encode(data))];", "statbrowser:update_interviews") target.stat_panel.send_message("update_interviews", data)
/datum/controller/subsystem/statpanels/proc/set_SDQL2_tab(client/target) /datum/controller/subsystem/statpanels/proc/set_SDQL2_tab(client/target)
var/list/sdql2A = list() var/list/sdql2A = list()
@@ -163,7 +165,7 @@ SUBSYSTEM_DEF(statpanels)
sdql2B = query.generate_stat() sdql2B = query.generate_stat()
sdql2A += sdql2B sdql2A += sdql2B
target << output(url_encode(json_encode(sdql2A)), "statbrowser:update_sdql2") target.stat_panel.send_message("update_sdql2", sdql2A)
/datum/controller/subsystem/statpanels/proc/set_spells_tab(client/target, mob/target_mob) /datum/controller/subsystem/statpanels/proc/set_spells_tab(client/target, mob/target_mob)
var/list/proc_holders = target_mob.get_proc_holders() var/list/proc_holders = target_mob.get_proc_holders()
@@ -172,11 +174,7 @@ SUBSYSTEM_DEF(statpanels)
for(var/proc_holder_list as anything in proc_holders) for(var/proc_holder_list as anything in proc_holders)
target.spell_tabs |= proc_holder_list[1] target.spell_tabs |= proc_holder_list[1]
var/proc_holders_encoded = "" target.stat_panel.send_message("update_spells", list(spell_tabs = target.spell_tabs, proc_holders_encoded = proc_holders))
if(length(proc_holders))
proc_holders_encoded = url_encode(json_encode(proc_holders))
target << output("[url_encode(json_encode(target.spell_tabs))];[proc_holders_encoded]", "statbrowser:update_spells")
/datum/controller/subsystem/statpanels/proc/set_turf_examine_tab(client/target, mob/target_mob) /datum/controller/subsystem/statpanels/proc/set_turf_examine_tab(client/target, mob/target_mob)
var/list/overrides = list() var/list/overrides = list()
@@ -213,11 +211,11 @@ SUBSYSTEM_DEF(statpanels)
else else
turfitems[++turfitems.len] = list("[turf_content.name]", REF(turf_content)) turfitems[++turfitems.len] = list("[turf_content.name]", REF(turf_content))
turfitems = url_encode(json_encode(turfitems)) turfitems = turfitems
target << output("[turfitems];", "statbrowser:update_listedturf") target.stat_panel.send_message("update_listedturf", turfitems)
/datum/controller/subsystem/statpanels/proc/generate_mc_data() /datum/controller/subsystem/statpanels/proc/generate_mc_data()
var/list/mc_data = list( mc_data = list(
list("CPU:", world.cpu), list("CPU:", world.cpu),
list("Instances:", "[num2text(world.contents.len, 10)]"), list("Instances:", "[num2text(world.contents.len, 10)]"),
list("World Time:", "[world.time]"), list("World Time:", "[world.time]"),
@@ -231,11 +229,10 @@ SUBSYSTEM_DEF(statpanels)
for(var/datum/controller/subsystem/sub_system as anything in Master.subsystems) 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(), "\ref[sub_system]") mc_data[++mc_data.len] = list("\[[sub_system.state_letter()]][sub_system.name]", sub_system.stat_entry(), "\ref[sub_system]")
mc_data[++mc_data.len] = list("Camera Net", "Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]", "\ref[GLOB.cameranet]") mc_data[++mc_data.len] = list("Camera Net", "Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]", "\ref[GLOB.cameranet]")
mc_data_encoded = url_encode(json_encode(mc_data))
///immediately update the active statpanel tab of the target client ///immediately update the active statpanel tab of the target client
/datum/controller/subsystem/statpanels/proc/immediate_send_stat_data(client/target) /datum/controller/subsystem/statpanels/proc/immediate_send_stat_data(client/target)
if(!target.statbrowser_ready) if(!target.stat_panel.is_ready())
return FALSE return FALSE
if(target.stat_tab == "Status") if(target.stat_tab == "Status")
@@ -249,7 +246,7 @@ SUBSYSTEM_DEF(statpanels)
if(target_mob?.listed_turf) if(target_mob?.listed_turf)
if(!target_mob.TurfAdjacent(target_mob.listed_turf)) if(!target_mob.TurfAdjacent(target_mob.listed_turf))
target << output("", "statbrowser:remove_listedturf") target.stat_panel.send_message("removed_listedturf")
target_mob.listed_turf = null target_mob.listed_turf = null
else if(target.stat_tab == target_mob?.listed_turf.name || !(target_mob?.listed_turf.name in target.panel_tabs)) else if(target.stat_tab == target_mob?.listed_turf.name || !(target_mob?.listed_turf.name in target.panel_tabs))
@@ -268,7 +265,7 @@ SUBSYSTEM_DEF(statpanels)
return TRUE return TRUE
if(!length(GLOB.sdql2_queries) && ("SDQL2" in target.panel_tabs)) if(!length(GLOB.sdql2_queries) && ("SDQL2" in target.panel_tabs))
target << output("", "statbrowser:remove_sdql2") target.stat_panel.send_message("remove_sdql2")
else if(length(GLOB.sdql2_queries) && target.stat_tab == "SDQL2") else if(length(GLOB.sdql2_queries) && target.stat_tab == "SDQL2")
set_SDQL2_tab(target) set_SDQL2_tab(target)
@@ -277,41 +274,5 @@ SUBSYSTEM_DEF(statpanels)
SIGNAL_HANDLER SIGNAL_HANDLER
SSstatpanels.cached_images -= REF(src) SSstatpanels.cached_images -= REF(src)
/// verbs that send information from the browser UI /// Stat panel window declaration
/client/verb/set_tab(tab as text|null) /client/var/datum/tgui_window/stat_panel
set name = "Set Tab"
set hidden = TRUE
stat_tab = tab
SSstatpanels.immediate_send_stat_data(src)
/client/verb/send_tabs(tabs as text|null)
set name = "Send Tabs"
set hidden = TRUE
panel_tabs |= tabs
/client/verb/remove_tabs(tabs as text|null)
set name = "Remove Tabs"
set hidden = TRUE
panel_tabs -= tabs
/client/verb/reset_tabs()
set name = "Reset Tabs"
set hidden = TRUE
panel_tabs = list()
/client/verb/panel_ready()
set name = "Panel Ready"
set hidden = TRUE
statbrowser_ready = TRUE
init_verbs()
/client/verb/update_verbs()
set name = "Update Verbs"
set hidden = TRUE
init_verbs()

View File

@@ -819,7 +819,7 @@
if(istype(S, spell)) if(istype(S, spell))
spell_list -= S spell_list -= S
qdel(S) qdel(S)
current?.client << output(null, "statbrowser:check_spells") current?.client.stat_panel.send_message("check_spells")
/datum/mind/proc/RemoveAllSpells() /datum/mind/proc/RemoveAllSpells()
for(var/obj/effect/proc_holder/S in spell_list) for(var/obj/effect/proc_holder/S in spell_list)

View File

@@ -862,7 +862,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
set name = "Debug Stat Panel" set name = "Debug Stat Panel"
set category = "Debug" set category = "Debug"
src << output("", "statbrowser:create_debug") src.stat_panel.send_message("create_debug")
/client/proc/admin_2fa_verify() /client/proc/admin_2fa_verify()
set name = "Verify Admin" set name = "Verify Admin"

View File

@@ -181,9 +181,6 @@
/// our current tab /// our current tab
var/stat_tab var/stat_tab
/// whether our browser is ready or not yet
var/statbrowser_ready = FALSE
/// list of all tabs /// list of all tabs
var/list/panel_tabs = list() var/list/panel_tabs = list()
/// list of tabs containing spells and abilities /// list of tabs containing spells and abilities

View File

@@ -87,7 +87,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(href_list["reload_tguipanel"]) if(href_list["reload_tguipanel"])
nuke_chat() nuke_chat()
if(href_list["reload_statbrowser"]) if(href_list["reload_statbrowser"])
src << browse(file('html/statbrowser.html'), "window=statbrowser") stat_panel.reinitialize()
// Log all hrefs // Log all hrefs
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]") log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
@@ -213,8 +213,12 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
GLOB.clients += src GLOB.clients += src
GLOB.directory[ckey] = src GLOB.directory[ckey] = src
// Instantiate stat panel
stat_panel = new(src, "statbrowser")
stat_panel.subscribe(src, .proc/on_stat_panel_message)
// Instantiate tgui panel // Instantiate tgui panel
tgui_panel = new(src) tgui_panel = new(src, "browseroutput")
set_right_click_menu_mode(TRUE) set_right_click_menu_mode(TRUE)
@@ -344,9 +348,15 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(SSinput.initialized) if(SSinput.initialized)
set_macros() set_macros()
// Initialize tgui panel // Initialize stat panel
src << browse(file('html/statbrowser.html'), "window=statbrowser") stat_panel.initialize(
inline_html = file2text('html/statbrowser.html'),
inline_js = file2text('html/statbrowser.js'),
inline_css = file2text('html/statbrowser.css'),
)
addtimer(CALLBACK(src, .proc/check_panel_loaded), 30 SECONDS) addtimer(CALLBACK(src, .proc/check_panel_loaded), 30 SECONDS)
// Initialize tgui panel
tgui_panel.initialize() tgui_panel.initialize()
if(alert_mob_dupe_login && !holder) if(alert_mob_dupe_login && !holder)
@@ -1129,10 +1139,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
continue continue
panel_tabs |= verb_to_init.category panel_tabs |= verb_to_init.category
verblist[++verblist.len] = list(verb_to_init.category, verb_to_init.name) verblist[++verblist.len] = list(verb_to_init.category, verb_to_init.name)
src << output("[url_encode(json_encode(panel_tabs))];[url_encode(json_encode(verblist))]", "statbrowser:init_verbs") src.stat_panel.send_message("init_verbs", list(panel_tabs = panel_tabs, verblist = verblist))
/client/proc/check_panel_loaded() /client/proc/check_panel_loaded()
if(statbrowser_ready) if(stat_panel.is_ready())
return return
to_chat(src, span_userdanger("Statpanel failed to load, click <a href='?src=[REF(src)];reload_statbrowser=1'>here</a> to reload the panel ")) to_chat(src, span_userdanger("Statpanel failed to load, click <a href='?src=[REF(src)];reload_statbrowser=1'>here</a> to reload the panel "))
@@ -1182,6 +1192,23 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
else else
SSambience.remove_ambience_client(src) SSambience.remove_ambience_client(src)
/**
* Handles incoming messages from the stat-panel TGUI.
*/
/client/proc/on_stat_panel_message(type, payload)
switch(type)
if("Update-Verbs")
init_verbs()
if("Remove-Tabs")
panel_tabs -= payload["tab"]
if("Send-Tabs")
panel_tabs |= payload["tab"]
if("Reset-Tabs")
panel_tabs = list()
if("Set-Tab")
stat_tab = payload["tab"]
SSstatpanels.immediate_send_stat_data(src)
/// Checks if this client has met the days requirement passed in, or if /// Checks if this client has met the days requirement passed in, or if
/// they are exempt from it. /// they are exempt from it.
/// Returns the number of days left, or 0. /// Returns the number of days left, or 0.

View File

@@ -472,14 +472,12 @@
// First we detain them by removing all the verbs they have on client // First we detain them by removing all the verbs they have on client
for (var/v in client.verbs) for (var/v in client.verbs)
var/procpath/verb_path = v var/procpath/verb_path = v
if (!(verb_path in GLOB.stat_panel_verbs)) remove_verb(client, verb_path)
remove_verb(client, verb_path)
// Then remove those on their mob as well // Then remove those on their mob as well
for (var/v in verbs) for (var/v in verbs)
var/procpath/verb_path = v var/procpath/verb_path = v
if (!(verb_path in GLOB.stat_panel_verbs)) remove_verb(src, verb_path)
remove_verb(src, verb_path)
// Then we create the interview form and show it to the client // Then we create the interview form and show it to the client
var/datum/interview/I = GLOB.interviews.interview_for_client(client) var/datum/interview/I = GLOB.interviews.interview_for_client(client)

View File

@@ -924,7 +924,7 @@
LAZYREMOVE(mob_spell_list, S) LAZYREMOVE(mob_spell_list, S)
qdel(S) qdel(S)
if(client) if(client)
client << output(null, "statbrowser:check_spells") client.stat_panel.send_message("check_spells")
/** /**
* Checks to see if the mob can cast normal magic spells. * Checks to see if the mob can cast normal magic spells.

View File

@@ -94,6 +94,7 @@
window.acquire_lock(src) window.acquire_lock(src)
if(!window.is_ready()) if(!window.is_ready())
window.initialize( window.initialize(
strict_mode = TRUE,
fancy = user.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy), fancy = user.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy),
assets = list( assets = list(
get_asset_datum(/datum/asset/simple/tgui), get_asset_datum(/datum/asset/simple/tgui),

View File

@@ -18,6 +18,7 @@
var/message_queue var/message_queue
var/sent_assets = list() var/sent_assets = list()
// Vars passed to initialize proc (and saved for later) // Vars passed to initialize proc (and saved for later)
var/initial_strict_mode
var/initial_fancy var/initial_fancy
var/initial_assets var/initial_assets
var/initial_inline_html var/initial_inline_html
@@ -47,11 +48,15 @@
* state. You can begin sending messages right after initializing. Messages * state. You can begin sending messages right after initializing. Messages
* will be put into the queue until the window finishes loading. * will be put into the queue until the window finishes loading.
* *
* optional assets list List of assets to inline into the html. * optional strict_mode bool - Enables strict error handling and BSOD.
* optional inline_html string Custom HTML to inject. * optional fancy bool - If TRUE and if this is NOT a panel, will hide the window titlebar.
* optional fancy bool If TRUE, will hide the window titlebar. * optional assets list - List of assets to load during initialization.
* optional inline_html string - Custom HTML to inject.
* optional inline_js string - Custom JS to inject.
* optional inline_css string - Custom CSS to inject.
*/ */
/datum/tgui_window/proc/initialize( /datum/tgui_window/proc/initialize(
strict_mode = FALSE,
fancy = FALSE, fancy = FALSE,
assets = list(), assets = list(),
inline_html = "", inline_html = "",
@@ -79,6 +84,7 @@
// Generate page html // Generate page html
var/html = SStgui.basehtml var/html = SStgui.basehtml
html = replacetextEx(html, "\[tgui:windowId]", id) html = replacetextEx(html, "\[tgui:windowId]", id)
html = replacetextEx(html, "\[tgui:strictMode]", strict_mode)
// Inject assets // Inject assets
var/inline_assets_str = "" var/inline_assets_str = ""
for(var/datum/asset/asset in assets) for(var/datum/asset/asset in assets)
@@ -99,7 +105,7 @@
html = replacetextEx(html, "<!-- tgui:inline-html -->", inline_html) html = replacetextEx(html, "<!-- tgui:inline-html -->", inline_html)
// Inject inline JS // Inject inline JS
if (inline_js) if (inline_js)
inline_js = "<script>\n[inline_js]\n</script>" inline_js = "<script>\n'use strict';\n[inline_js]\n</script>"
html = replacetextEx(html, "<!-- tgui:inline-js -->", inline_js) html = replacetextEx(html, "<!-- tgui:inline-js -->", inline_js)
// Inject inline CSS // Inject inline CSS
if (inline_css) if (inline_css)
@@ -113,6 +119,20 @@
if(!is_browser) if(!is_browser)
winset(client, id, "on-close=\"uiclose [id]\"") winset(client, id, "on-close=\"uiclose [id]\"")
/**
* public
*
* Reinitializes the panel with previous data used for initialization.
*/
/datum/tgui_window/proc/reinitialize()
initialize(
strict_mode = initial_strict_mode,
fancy = initial_fancy,
assets = initial_assets,
inline_html = initial_inline_html,
inline_js = initial_inline_js,
inline_css = initial_inline_css)
/** /**
* public * public
* *
@@ -346,12 +366,7 @@
client << link(href_list["url"]) client << link(href_list["url"])
if("cacheReloaded") if("cacheReloaded")
// Reinitialize // Reinitialize
initialize( reinitialize()
fancy = initial_fancy,
assets = initial_assets,
inline_html = initial_inline_html,
inline_js = initial_inline_js,
inline_css = initial_inline_css)
// Resend the assets // Resend the assets
for(var/asset in sent_assets) for(var/asset in sent_assets)
send_asset(asset) send_asset(asset)

View File

@@ -13,9 +13,9 @@
var/broken = FALSE var/broken = FALSE
var/initialized_at var/initialized_at
/datum/tgui_panel/New(client/client) /datum/tgui_panel/New(client/client, id)
src.client = client src.client = client
window = new(client, "browseroutput") window = new(client, id)
window.subscribe(src, .proc/on_message) window.subscribe(src, .proc/on_message)
/datum/tgui_panel/Del() /datum/tgui_panel/Del()
@@ -42,9 +42,11 @@
sleep(1) sleep(1)
initialized_at = world.time initialized_at = world.time
// Perform a clean initialization // Perform a clean initialization
window.initialize(assets = list( window.initialize(
get_asset_datum(/datum/asset/simple/tgui_panel), strict_mode = TRUE,
)) assets = list(
get_asset_datum(/datum/asset/simple/tgui_panel),
))
window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/fontawesome)) window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/fontawesome))
window.send_asset(get_asset_datum(/datum/asset/spritesheet/chat)) window.send_asset(get_asset_datum(/datum/asset/spritesheet/chat))
// Other setup // Other setup

227
html/statbrowser.css Normal file
View File

@@ -0,0 +1,227 @@
body {
font-family: Verdana, Geneva, Tahoma, sans-serif;
font-size: 12px !important;
margin: 0 !important;
padding: 0 !important;
overflow-x: hidden;
overflow-y: scroll;
}
body.dark {
background-color: #131313;
color: #b2c4dd;
scrollbar-base-color: #1c1c1c;
scrollbar-face-color: #3b3b3b;
scrollbar-3dlight-color: #252525;
scrollbar-highlight-color: #252525;
scrollbar-track-color: #1c1c1c;
scrollbar-arrow-color: #929292;
scrollbar-shadow-color: #3b3b3b;
}
#menu {
background-color: #F0F0F0;
position: fixed;
width: 100%;
z-index: 100;
}
.dark #menu {
background-color: #202020;
}
#statcontent {
padding: 7px 7px 7px 7px;
}
a {
color: black;
text-decoration: none
}
.dark a {
color: #b2c4dd;
}
a:hover,
.dark a:hover {
text-decoration: underline;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
background-color: #333;
}
li {
float: left;
}
li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
li a:hover:not(.active) {
background-color: #111;
}
.button-container {
display: inline-flex;
flex-wrap: wrap-reverse;
flex-direction: row;
align-items: flex-start;
overflow-x: hidden;
white-space: pre-wrap;
padding: 0 4px;
}
.button {
background-color: #dfdfdf;
border: 1px solid #cecece;
border-bottom-width: 2px;
color: rgba(0, 0, 0, 0.7);
padding: 6px 4px 4px;
text-align: center;
text-decoration: none;
font-size: 12px;
margin: 0;
cursor: pointer;
transition-duration: 100ms;
order: 3;
min-width: 40px;
}
.dark button {
background-color: #222222;
border-color: #343434;
color: rgba(255, 255, 255, 0.5);
}
.button:hover {
background-color: #ececec;
transition-duration: 0;
}
.dark button:hover {
background-color: #2e2e2e;
}
.button:active,
.button.active {
background-color: #ffffff;
color: black;
border-top-color: #cecece;
border-left-color: #cecece;
border-right-color: #cecece;
border-bottom-color: #ffffff;
}
.dark .button:active,
.dark .button.active {
background-color: #444444;
color: white;
border-top-color: #343434;
border-left-color: #343434;
border-right-color: #343434;
border-bottom-color: #ffffff;
}
.grid-container {
margin: -2px;
margin-right: -15px;
}
.grid-item {
position: relative;
display: inline-block;
width: 100%;
box-sizing: border-box;
overflow: visible;
padding: 3px 2px;
text-decoration: none;
}
@media only screen and (min-width: 300px) {
.grid-item {
width: 50%;
}
}
@media only screen and (min-width: 430px) {
.grid-item {
width: 33%;
}
}
@media only screen and (min-width: 560px) {
.grid-item {
width: 25%;
}
}
@media only screen and (min-width: 770px) {
.grid-item {
width: 20%;
}
}
.grid-item:hover {
z-index: 1;
}
.grid-item:hover .grid-item-text {
width: auto;
text-decoration: underline;
}
.grid-item-text {
display: inline-block;
width: 100%;
background-color: #ffffff;
margin: 0 -6px;
padding: 0 6px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
pointer-events: none;
}
.dark .grid-item-text {
background-color: #131313;
}
.link {
display: inline;
background: none;
border: none;
padding: 7px 14px;
color: black;
text-decoration: none;
cursor: pointer;
font-size: 13px;
margin: 2px 2px;
}
.dark .link {
color: #abc6ec;
}
.link:hover {
text-decoration: underline;
}
img {
-ms-interpolation-mode: nearest-neighbor;
image-rendering: pixelated;
}
.interview_panel_controls,
.interview_panel_stats {
margin-bottom: 10px;
}

File diff suppressed because it is too large Load Diff

1003
html/statbrowser.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -165,7 +165,6 @@
#include "code\__DEFINES\stat.dm" #include "code\__DEFINES\stat.dm"
#include "code\__DEFINES\stat_tracking.dm" #include "code\__DEFINES\stat_tracking.dm"
#include "code\__DEFINES\station.dm" #include "code\__DEFINES\station.dm"
#include "code\__DEFINES\statpanel.dm"
#include "code\__DEFINES\status_effects.dm" #include "code\__DEFINES\status_effects.dm"
#include "code\__DEFINES\storage.dm" #include "code\__DEFINES\storage.dm"
#include "code\__DEFINES\strippable.dm" #include "code\__DEFINES\strippable.dm"

View File

@@ -261,3 +261,21 @@ Byond.winget(null, 'url').then((serverUrl) => {
Byond.command('.quit'); Byond.command('.quit');
}); });
``` ```
## Strict Mode
Strict mode is a flag that you can set on tgui window.
```dm
window.initialize(strict_mode = TRUE)
```
If `TRUE`, unhandled errors and common mistakes result in a blue screen of death with a stack trace of the error, which you can use to debug it. Bluescreened window stops handling incoming messages and closes the active instance of tgui datum if there was one, to avoid a massive spam of errors and help to deal with them one by one.
It can be defined in `window.initialize()` in DM, as shown above, or changed in runtime at runtime via `Byond.strictMode` to `true` or `false`.
```js
Byond.strictMode = true;
```
It is recommended that you keep this **ON** to detect hard to find bugs.

12
tgui/global.d.ts vendored
View File

@@ -71,6 +71,18 @@ type ByondType = {
*/ */
IS_LTE_IE11: boolean; IS_LTE_IE11: boolean;
/**
* If `true`, unhandled errors and common mistakes result in a blue screen
* of death, which stops this window from handling incoming messages and
* closes the active instance of tgui datum if there was one.
*
* It can be defined in window.initialize() in DM, or changed in runtime
* here via this property to `true` or `false`.
*
* It is recommended that you keep this ON to detect hard to find bugs.
*/
strictMode: boolean;
/** /**
* Makes a BYOND call. * Makes a BYOND call.
* *

View File

@@ -6,6 +6,7 @@
<!-- Inlined metadata --> <!-- Inlined metadata -->
<meta id="tgui:windowId" content="[tgui:windowId]"> <meta id="tgui:windowId" content="[tgui:windowId]">
<meta id="tgui:strictMode" content="[tgui:strictMode]">
<!-- Early setup --> <!-- Early setup -->
<script type="text/javascript"> <script type="text/javascript">
@@ -64,6 +65,9 @@
Byond.IS_LTE_IE10 = Byond.TRIDENT !== null && Byond.TRIDENT <= 6; Byond.IS_LTE_IE10 = Byond.TRIDENT !== null && Byond.TRIDENT <= 6;
Byond.IS_LTE_IE11 = Byond.TRIDENT !== null && Byond.TRIDENT <= 7; Byond.IS_LTE_IE11 = Byond.TRIDENT !== null && Byond.TRIDENT <= 7;
// Strict mode flag
Byond.strictMode = Boolean(Number(parseMetaTag('tgui:strictMode')));
// Callbacks for asynchronous calls // Callbacks for asynchronous calls
Byond.__callbacks__ = []; Byond.__callbacks__ = [];
@@ -220,13 +224,13 @@
}; };
Byond.subscribeTo = function (type, listener) { Byond.subscribeTo = function (type, listener) {
listener = function (_type, payload) { var _listener = function (_type, payload) {
if (_type === type) { if (_type === type) {
listener(payload); listener(payload);
} }
}; };
window.update.listeners.push(listener); window.update.flushQueue(_listener);
window.update.flushQueue(listener); window.update.listeners.push(_listener);
}; };
// Asset loaders // Asset loaders
@@ -363,6 +367,7 @@
// ------------------------------------------------------ // ------------------------------------------------------
window.onerror = function (msg, url, line, col, error) { window.onerror = function (msg, url, line, col, error) {
window.onerror.errorCount = (window.onerror.errorCount || 0) + 1;
// Proper stacktrace // Proper stacktrace
var stack = error && error.stack; var stack = error && error.stack;
// Ghetto stacktrace // Ghetto stacktrace
@@ -375,39 +380,51 @@ window.onerror = function (msg, url, line, col, error) {
// Augment the stack // Augment the stack
stack = window.__augmentStack__(stack, error); stack = window.__augmentStack__(stack, error);
// Print error to the page // Print error to the page
var errorRoot = document.getElementById('FatalError'); if (Byond.strictMode) {
var errorStack = document.getElementById('FatalError__stack'); var errorRoot = document.getElementById('FatalError');
if (errorRoot) { var errorStack = document.getElementById('FatalError__stack');
errorRoot.className = 'FatalError FatalError--visible'; if (errorRoot) {
if (window.onerror.__stack__) { errorRoot.className = 'FatalError FatalError--visible';
window.onerror.__stack__ += '\n\n' + stack; if (window.onerror.__stack__) {
window.onerror.__stack__ += '\n\n' + stack;
}
else {
window.onerror.__stack__ = stack;
}
var textProp = Byond.IS_LTE_IE8 ? 'innerText' : 'textContent';
errorStack[textProp] = window.onerror.__stack__;
} }
else { // Set window geometry
window.onerror.__stack__ = stack; var setFatalErrorGeometry = function () {
} Byond.winset(Byond.windowId, {
var textProp = Byond.IS_LTE_IE8 ? 'innerText' : 'textContent'; titlebar: true,
errorStack[textProp] = window.onerror.__stack__; 'is-visible': true,
'can-resize': true,
});
};
setFatalErrorGeometry();
setInterval(setFatalErrorGeometry, 1000);
} }
// Set window geometry
var setFatalErrorGeometry = function () {
Byond.winset(Byond.windowId, {
titlebar: true,
size: '600x600',
'is-visible': true,
'can-resize': true,
});
};
setFatalErrorGeometry();
setInterval(setFatalErrorGeometry, 1000);
// Send logs to the game server // Send logs to the game server
Byond.sendMessage({ if (Byond.strictMode) {
type: 'log', Byond.sendMessage({
fatal: 1, type: 'log',
message: stack, fatal: 1,
}); message: stack,
});
}
else if (window.onerror.errorCount <= 1) {
stack += '\nWindow is in non-strict mode, future errors are suppressed.';
Byond.sendMessage({
type: 'log',
message: stack,
});
}
// Short-circuit further updates // Short-circuit further updates
window.__updateQueue__ = []; if (Byond.strictMode) {
window.update = function () {}; window.update = function () {};
window.update.queue = [];
}
// Prevent default action // Prevent default action
return true; return true;
}; };