From 0d1ef29662ac01a41ee6fa999ee538f4529e7f97 Mon Sep 17 00:00:00 2001 From: Wayland-Smithy <64715958+Wayland-Smithy@users.noreply.github.com> Date: Wed, 7 Jul 2021 16:02:52 -0700 Subject: [PATCH] Drastic Lag Mitigation Subsystem: SSlag_switch (#59717) Requested by oranges and inspired by the upcoming event. A new subsyetem, non-processing (for now), aimed at providing some toggle switches that can be flipped as a last ditch effort to save some CPU cycles by sacrificing some non-critical mechanics. Below you can see each individual toggle. Screenshot of the admin panel: image Surely there are more opportunities for toggles I missed, but adding new ones is not very difficult at all. Why It's Good For The Game Better performance during extreme pop, I hope. Changelog cl code: Introduces the Lag Switch subsystem for when a smoother experience is worth trading a few bells and whistles for. Performance enhancement measures can be togged by admins with the Show Lag Switches admin verb or enabled automatically at a pop amount set via config. config: Added a new config var: number/auto_lag_switch_pop --- code/__DEFINES/dcs/signals.dm | 2 + code/__DEFINES/lag_switch.dm | 10 ++ code/__DEFINES/traits.dm | 2 + code/__HELPERS/icons.dm | 4 + code/_globalvars/lists/mobs.dm | 1 + code/_globalvars/traits.dm | 1 + .../configuration/entries/general.dm | 4 + code/controllers/subsystem/input.dm | 2 +- code/controllers/subsystem/lag_switch.dm | 133 ++++++++++++++++++ code/controllers/subsystem/ticker.dm | 1 - code/datums/chatmessage.dm | 2 + code/datums/world_topic.dm | 2 +- code/game/world.dm | 2 +- code/modules/admin/admin.dm | 46 ++++-- code/modules/admin/admin_verbs.dm | 1 + code/modules/admin/topic.dm | 52 +++++++ .../nukeop/equipment/nuclearbomb.dm | 2 +- code/modules/client/client_defines.dm | 2 + code/modules/client/client_procs.dm | 1 + .../modules/mob/dead/new_player/new_player.dm | 16 ++- code/modules/mob/dead/observer/observer.dm | 14 ++ code/modules/mob/living/death.dm | 3 + code/modules/mob/living/living_say.dm | 8 +- code/modules/mob/mob_lists.dm | 5 + code/modules/mob/mob_say.dm | 6 + config/admins.txt | 1 + config/config.txt | 3 + tgstation.dme | 2 + 28 files changed, 306 insertions(+), 22 deletions(-) create mode 100644 code/__DEFINES/lag_switch.dm create mode 100644 code/controllers/subsystem/lag_switch.dm diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 1fbd794856e..c71232b6d69 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -40,6 +40,8 @@ #define LINKED_UP (1<<0) /// an obj/item is created! (obj/item/created_item) #define COMSIG_GLOB_NEW_ITEM "!new_item" +/// a client (re)connected, after all /client/New() checks have passed : (client/connected_client) +#define COMSIG_GLOB_CLIENT_CONNECT "!client_connect" /// a weather event of some kind occured #define COMSIG_WEATHER_TELEGRAPH(event_type) "!weather_telegraph [event_type]" #define COMSIG_WEATHER_START(event_type) "!weather_start [event_type]" diff --git a/code/__DEFINES/lag_switch.dm b/code/__DEFINES/lag_switch.dm new file mode 100644 index 00000000000..fd1fc579a63 --- /dev/null +++ b/code/__DEFINES/lag_switch.dm @@ -0,0 +1,10 @@ +// All of the possible Lag Switch lag mitigation measures +// If you add more do not forget to update MEASURES_AMOUNT accordingly +#define DISABLE_DEAD_KEYLOOP 1 // Stops ghosts flying around freely, they can still jump and orbit, staff exempted +#define DISABLE_GHOST_ZOOM_TRAY 2 // Stops ghosts using zoom/t-ray verbs and resets their view if zoomed out, staff exempted +#define DISABLE_RUNECHAT 3 // Disable runechat and enable the bubbles, speaking mobs with TRAIT_BYPASS_MEASURES exempted +#define DISABLE_USR_ICON2HTML 4 // Disable icon2html procs from verbs like examine, mobs calling with TRAIT_BYPASS_MEASURES exempted +#define DISABLE_NON_OBSJOBS 5 // Prevents anyone from joining the game as anything but observer +#define SLOWMODE_SAY 6 // Limit IC/dchat spam to one message every x seconds per client, TRAIT_BYPASS_MEASURES exempted + +#define MEASURES_AMOUNT 6 // The total number of switches defined above diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 8971cfc322f..74d1ec23f83 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -323,6 +323,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_BLOODSHOT_EYES "bloodshot_eyes" /// This mob should never close UI even if it doesn't have a client #define TRAIT_PRESERVE_UI_WITHOUT_CLIENT "preserve_ui_without_client" +/// This mob overrides certian SSlag_switch measures with this special trait +#define TRAIT_BYPASS_MEASURES "bypass_lagswitch_measures" #define TRAIT_NOBLEED "nobleed" //This carbon doesn't bleed /// This atom can ignore the "is on a turf" check for simple AI datum attacks, allowing them to attack from bags or lockers as long as any other conditions are met diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 0b78028f036..5d7f87ade1a 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1124,6 +1124,8 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0 /proc/icon2html(thing, target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null) if (!thing) return + if(SSlag_switch.measures[DISABLE_USR_ICON2HTML] && usr && !HAS_TRAIT(usr, TRAIT_BYPASS_MEASURES)) + return var/key var/icon/I = thing @@ -1225,6 +1227,8 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0 /proc/costly_icon2html(thing, target, sourceonly = FALSE) if (!thing) return + if(SSlag_switch.measures[DISABLE_USR_ICON2HTML] && usr && !HAS_TRAIT(usr, TRAIT_BYPASS_MEASURES)) + return if (isicon(thing)) return icon2html(thing, target) diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 77fec9e6c88..ff6f2b79b25 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -17,6 +17,7 @@ GLOBAL_LIST_INIT(dangerous_turfs, typecacheof(list( //This is for procs to replace all the goddamn 'in world's that are chilling around the code GLOBAL_LIST_EMPTY(player_list) //all mobs **with clients attached**. +GLOBAL_LIST_EMPTY(keyloop_list) //as above but can be limited to boost performance GLOBAL_LIST_EMPTY(mob_list) //all mobs, including clientless GLOBAL_LIST_EMPTY(mob_directory) //mob_id -> mob GLOBAL_LIST_EMPTY(alive_mob_list) //all alive mobs, including clientless. Excludes /mob/dead/new_player diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index bd2fa7f736a..57fb14ae2d7 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -88,6 +88,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_CANNOT_OPEN_PRESENTS" = TRAIT_CANNOT_OPEN_PRESENTS, "TRAIT_PRESENT_VISION" = TRAIT_PRESENT_VISION, "TRAIT_DISK_VERIFIER" = TRAIT_DISK_VERIFIER, + "TRAIT_BYPASS_MEASURES" = TRAIT_BYPASS_MEASURES, "TRAIT_NOMOBSWAP" = TRAIT_NOMOBSWAP, "TRAIT_XRAY_VISION" = TRAIT_XRAY_VISION, "TRAIT_WEB_WEAVER" = TRAIT_WEB_WEAVER, diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index e0812de3637..c690e7e9efe 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -299,6 +299,10 @@ /datum/config_entry/flag/maprotation +/datum/config_entry/number/auto_lag_switch_pop //Number of clients at which drastic lag mitigation measures kick in + config_entry_value = null + min_val = 0 + /datum/config_entry/number/soft_popcap default = null min_val = 0 diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm index 311eec6e684..dd389e0dfbc 100644 --- a/code/controllers/subsystem/input.dm +++ b/code/controllers/subsystem/input.dm @@ -35,5 +35,5 @@ SUBSYSTEM_DEF(input) user.set_macros() /datum/controller/subsystem/input/fire() - for(var/mob/user as anything in GLOB.player_list) + for(var/mob/user as anything in GLOB.keyloop_list) user.focus?.keyLoop(user.client) diff --git a/code/controllers/subsystem/lag_switch.dm b/code/controllers/subsystem/lag_switch.dm new file mode 100644 index 00000000000..f8bbd13e297 --- /dev/null +++ b/code/controllers/subsystem/lag_switch.dm @@ -0,0 +1,133 @@ +/// The subsystem for controlling drastic performance enhancements aimed at reducing server load for a smoother albeit slightly duller gaming experience +SUBSYSTEM_DEF(lag_switch) + name = "Lag Switch" + flags = SS_NO_FIRE + + /// If the lag switch measures should attempt to trigger automatically, TRUE if a config value exists + var/auto_switch = FALSE + /// Amount of connected clients at which the Lag Switch should engage, set via config or admin panel + var/trigger_pop = INFINITY - 1337 + /// List of bools corresponding to code/__DEFINES/lag_switch.dm + var/static/list/measures[MEASURES_AMOUNT] + /// List of measures that toggle automatically + var/list/auto_measures = list(DISABLE_GHOST_ZOOM_TRAY, DISABLE_RUNECHAT, DISABLE_USR_ICON2HTML) + /// Timer ID for the automatic veto period + var/veto_timer_id + /// Cooldown between say verb uses when slowmode is enabled + var/slowmode_cooldown = 3 SECONDS + +/datum/controller/subsystem/lag_switch/Initialize(start_timeofday) + for(var/i = 1, i <= measures.len, i++) + measures[i] = FALSE + var/auto_switch_pop = CONFIG_GET(number/auto_lag_switch_pop) + if(auto_switch_pop) + auto_switch = TRUE + trigger_pop = auto_switch_pop + RegisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT, .proc/client_connected) + return ..() + +/datum/controller/subsystem/lag_switch/proc/client_connected(datum/source, client/connected) + SIGNAL_HANDLER + if(TGS_CLIENT_COUNT < trigger_pop) + return + + auto_switch = FALSE + UnregisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT) + veto_timer_id = addtimer(CALLBACK(src, .proc/set_all_measures, TRUE, TRUE), 20 SECONDS, TIMER_STOPPABLE) + message_admins("Lag Switch population threshold reached. Automatic activation of lag mitigation measures occuring in 20 seconds. (CANCEL)") + log_admin("Lag Switch population threshold reached. Automatic activation of lag mitigation measures occuring in 20 seconds.") + +/// (En/Dis)able automatic triggering of switches based on client count +/datum/controller/subsystem/lag_switch/proc/toggle_auto_enable() + auto_switch = !auto_switch + if(auto_switch) + RegisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT, .proc/client_connected) + else + UnregisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT) + +/// Called from an admin chat link +/datum/controller/subsystem/lag_switch/proc/cancel_auto_enable_in_progress() + if(!veto_timer_id) + return FALSE + + deltimer(veto_timer_id) + veto_timer_id = null + return TRUE + +/// Update the slowmode timer length and clear existing ones if reduced +/datum/controller/subsystem/lag_switch/proc/change_slowmode_cooldown(length) + if(!length) + return FALSE + + var/length_secs = length SECONDS + if(length_secs <= 0) + length_secs = 1 // one tick because cooldowns do not like 0 + + if(length_secs < slowmode_cooldown) + for(var/client/C as anything in GLOB.clients) + COOLDOWN_RESET(C, say_slowmode) + + slowmode_cooldown = length_secs + if(measures[SLOWMODE_SAY]) + to_chat(world, span_boldannounce("Slowmode timer has been changed to [length] seconds by an admin.")) + return TRUE + +/// Handle the state change for individual measures +/datum/controller/subsystem/lag_switch/proc/set_measure(measure_key, state) + if(isnull(measure_key) || isnull(state)) + stack_trace("SSlag_switch.set_measure() was called with a null arg") + return FALSE + if(isnull(LAZYACCESS(measures, measure_key))) + stack_trace("SSlag_switch.set_measure() was called with a measure_key not in the list of measures") + return FALSE + if(measures[measure_key] == state) + return TRUE + + measures[measure_key] = state + + switch(measure_key) + if(DISABLE_DEAD_KEYLOOP) + if(state) + for(var/mob/user as anything in GLOB.player_list) + if(user.stat == DEAD && !user.client?.holder) + GLOB.keyloop_list -= user + deadchat_broadcast(span_big("To increase performance Observer freelook is now disabled. Please use Orbit, Teleport, and Jump to look around."), message_type = DEADCHAT_ANNOUNCEMENT) + else + GLOB.keyloop_list |= GLOB.player_list + deadchat_broadcast("Observer freelook has been re-enabled. Enjoy your wooshing.", message_type = DEADCHAT_ANNOUNCEMENT) + if(DISABLE_GHOST_ZOOM_TRAY) + if(state) // if enabling make sure current ghosts are updated + for(var/mob/dead/observer/ghost in GLOB.dead_mob_list) + if(!ghost.client) + continue + if(!ghost.client.holder && ghost.client.view_size.getView() != ghost.client.view_size.default) + ghost.client.view_size.resetToDefault() + if(SLOWMODE_SAY) + if(state) + to_chat(world, span_boldannounce("Slowmode for IC/dead chat has been enabled with [slowmode_cooldown/10] seconds between messages.")) + else + for(var/client/C as anything in GLOB.clients) + COOLDOWN_RESET(C, say_slowmode) + to_chat(world, span_boldannounce("Slowmode for IC/dead chat has been disabled by an admin.")) + if(DISABLE_NON_OBSJOBS) + world.update_status() + + return TRUE + +/// Helper to loop over all measures for mass changes +/datum/controller/subsystem/lag_switch/proc/set_all_measures(state, automatic = FALSE) + if(isnull(state)) + stack_trace("SSlag_switch.set_all_measures() was called with a null state arg") + return FALSE + + if(automatic) + message_admins("Lag Switch enabling automatic measures now.") + log_admin("Lag Switch enabling automatic measures now.") + veto_timer_id = null + for(var/i = 1, i <= auto_measures.len, i++) + set_measure(auto_measures[i], state) + return TRUE + + for(var/i = 1, i <= measures.len, i++) + set_measure(i, state) + return TRUE diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 31dda2fc6cd..81add481df7 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -43,7 +43,6 @@ SUBSYSTEM_DEF(ticker) var/news_report - var/late_join_disabled var/roundend_check_paused = FALSE diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm index 78152337492..eeccb32aaff 100644 --- a/code/datums/chatmessage.dm +++ b/code/datums/chatmessage.dm @@ -232,6 +232,8 @@ * * spans - Additional classes to be added to the message */ /mob/proc/create_chat_message(atom/movable/speaker, datum/language/message_language, raw_message, list/spans, runechat_flags = NONE) + if(SSlag_switch.measures[DISABLE_RUNECHAT] && !HAS_TRAIT(speaker, TRAIT_BYPASS_MEASURES)) + return // Ensure the list we are using, if present, is a copy so we don't modify the list provided to us spans = spans ? spans.Copy() : list() diff --git a/code/datums/world_topic.dm b/code/datums/world_topic.dm index 6663f223ce3..63dcd56a542 100644 --- a/code/datums/world_topic.dm +++ b/code/datums/world_topic.dm @@ -142,7 +142,7 @@ . = list() .["version"] = GLOB.game_version .["respawn"] = config ? !CONFIG_GET(flag/norespawn) : FALSE - .["enter"] = GLOB.enter_allowed + .["enter"] = !LAZYACCESS(SSlag_switch.measures, DISABLE_NON_OBSJOBS) .["ai"] = CONFIG_GET(flag/allow_ai) .["host"] = world.host ? world.host : null .["round_id"] = GLOB.round_id diff --git a/code/game/world.dm b/code/game/world.dm index de4a2a02af5..1bf6d27fc6c 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -276,7 +276,7 @@ GLOBAL_VAR(restart_counter) var/list/features = list() - if (!GLOB.enter_allowed) + if(LAZYACCESS(SSlag_switch.measures, DISABLE_NON_OBSJOBS)) features += "closed" var/s = "" diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index c9bd7b74d7a..1138cba4f4e 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -587,15 +587,12 @@ set category = "Server" set desc="People can't enter" set name="Toggle Entering" - GLOB.enter_allowed = !( GLOB.enter_allowed ) - if (!( GLOB.enter_allowed )) - to_chat(world, "New players may no longer enter the game.", confidential = TRUE) - else - to_chat(world, "New players may now enter the game.", confidential = TRUE) - log_admin("[key_name(usr)] toggled new player game entering.") - message_admins(span_adminnotice("[key_name_admin(usr)] toggled new player game entering.")) - world.update_status() - SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Entering", "[GLOB.enter_allowed ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(!SSlag_switch.initialized) + return + SSlag_switch.set_measure(DISABLE_NON_OBSJOBS, !SSlag_switch.measures[DISABLE_NON_OBSJOBS]) + log_admin("[key_name(usr)] toggled new player game entering. Lag Switch at index ([DISABLE_NON_OBSJOBS])") + message_admins("[key_name_admin(usr)] toggled new player game entering [SSlag_switch.measures[DISABLE_NON_OBSJOBS] ? "OFF" : "ON"].") + SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Entering", "[!SSlag_switch.measures[DISABLE_NON_OBSJOBS] ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /datum/admins/proc/toggleAI() set category = "Server" @@ -825,6 +822,9 @@ tgui_alert(usr, "You cannot manage jobs before the job subsystem is initialized!") return + if(SSlag_switch.measures[DISABLE_NON_OBSJOBS]) + dat += "
Lag Switch \"Disable non-observer late joining\" is ON. Only Observers may join!
" + dat += "" for(var/j in SSjob.occupations) @@ -957,3 +957,31 @@ "Admin login: [key_name(src)]") if(string) message_admins("[string]") + +/datum/admins/proc/show_lag_switch_panel() + set category = "Admin.Game" + set name = "Show Lag Switches" + set desc="Display the controls for drastic lag mitigation measures." + + if(!SSlag_switch.initialized) + to_chat(usr, span_notice("The Lag Switch subsystem has not yet been initialized.")) + return + if(!check_rights()) + return + + var/list/dat = list("Lag Switches

Lag (Reduction) Switches

") + dat += "Automatic Trigger: [SSlag_switch.auto_switch ? "On" : "Off"]
" + dat += "Population Threshold: [SSlag_switch.trigger_pop]
" + dat += "Slowmode Cooldown (toggle On/Off below): [SSlag_switch.slowmode_cooldown/10] seconds
" + dat += "
SET ALL MEASURES: ON | OFF
" + dat += "
Disable ghosts zoom and t-ray verbs (except staff): [SSlag_switch.measures[DISABLE_GHOST_ZOOM_TRAY] ? "On" : "Off"]
" + dat += "Disable late joining: [SSlag_switch.measures[DISABLE_NON_OBSJOBS] ? "On" : "Off"]
" + dat += "
============! MAD GHOSTS ZONE !============
" + dat += "Disable deadmob keyLoop (except staff, informs dchat): [SSlag_switch.measures[DISABLE_DEAD_KEYLOOP] ? "On" : "Off"]
" + dat += "==========================================
" + dat += "
Measures below can be bypassed with a special trait
" + dat += "Slowmode say verb (informs world): [SSlag_switch.measures[SLOWMODE_SAY] ? "On" : "Off"]
" + dat += "Disable runechat: [SSlag_switch.measures[DISABLE_RUNECHAT] ? "On" : "Off"] - trait applies to speaker
" + dat += "Disable examine icons: [SSlag_switch.measures[DISABLE_USR_ICON2HTML] ? "On" : "Off"] - trait applies to examiner
" + dat += "" + usr << browse(dat.Join(), "window=lag_switch_panel;size=420x420") diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 2f2f7fe92fa..9258e2f3cf6 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -28,6 +28,7 @@ GLOBAL_PROTECT(admin_verbs_admin) return list( /client/proc/invisimin, /*allows our mob to go invisible/visible*/ // /datum/admins/proc/show_traitor_panel, /*interface which shows a mob's mind*/ -Removed due to rare practical use. Moved to debug verbs ~Errorage + /datum/admins/proc/show_lag_switch_panel, /datum/admins/proc/show_player_panel, /*shows an interface for individual players, with various links (links require additional flags*/ /datum/verbs/menu/Admin/verb/playerpanel, /client/proc/game_panel, /*game panel, allows to change game-mode etc*/ diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 94e8ba104fb..17088f47a49 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1674,6 +1674,58 @@ GLOB.station_goals += G modify_goals() + else if(href_list["change_lag_switch"]) + if(!check_rights(R_ADMIN)) + return + + switch(href_list["change_lag_switch"]) + if("ALL_ON") + SSlag_switch.set_all_measures(TRUE) + log_admin("[key_name(usr)] turned all Lag Switch measures ON.") + message_admins("[key_name_admin(usr)] turned all Lag Switch measures ON.") + if("ALL_OFF") + SSlag_switch.set_all_measures(FALSE) + log_admin("[key_name(usr)] turned all Lag Switch measures OFF.") + message_admins("[key_name_admin(usr)] turned all Lag Switch measures OFF.") + else + var/switch_index = text2num(href_list["change_lag_switch"]) + if(!SSlag_switch.set_measure(switch_index, !LAZYACCESS(SSlag_switch.measures, switch_index))) + to_chat(src, span_danger("Something went wrong when trying to toggle that Lag Switch. Check runtimes for more info."), confidential = TRUE) + else + log_admin("[key_name(usr)] turned a Lag Switch measure at index ([switch_index]) [LAZYACCESS(SSlag_switch.measures, switch_index) ? "ON" : "OFF"]") + message_admins("[key_name_admin(usr)] turned a Lag Switch measure [LAZYACCESS(SSlag_switch.measures, switch_index) ? "ON" : "OFF"]") + + src.show_lag_switch_panel() + + else if(href_list["change_lag_switch_option"]) + if(!check_rights(R_ADMIN)) + return + + switch(href_list["change_lag_switch_option"]) + if("CANCEL") + if(SSlag_switch.cancel_auto_enable_in_progress()) + log_admin("[key_name(usr)] canceled the automatic Lag Switch activation in progress.") + message_admins("[key_name_admin(usr)] canceled the automatic Lag Switch activation in progress.") + return // return here to avoid (re)rendering the panel for this case + if("TOGGLE_AUTO") + SSlag_switch.toggle_auto_enable() + log_admin("[key_name(usr)] toggled automatic Lag Switch activation [SSlag_switch.auto_switch ? "ON" : "OFF"].") + message_admins("[key_name_admin(usr)] toggled automatic Lag Switch activation [SSlag_switch.auto_switch ? "ON" : "OFF"].") + if("NUM") + var/new_num = input("Enter new threshold value:", "Num") as null|num + if(!isnull(new_num)) + SSlag_switch.trigger_pop = new_num + log_admin("[key_name(usr)] set the Lag Switch automatic trigger pop to [new_num].") + message_admins("[key_name_admin(usr)] set the Lag Switch automatic trigger pop to [new_num].") + if("SLOWCOOL") + var/new_num = input("Enter new cooldown in seconds:", "Num") as null|num + if(!isnull(new_num)) + SSlag_switch.change_slowmode_cooldown(new_num) + log_admin("[key_name(usr)] set the Lag Switch slowmode cooldown to [new_num] seconds.") + message_admins("[key_name_admin(usr)] set the Lag Switch slowmode cooldown to [new_num] seconds.") + + src.show_lag_switch_panel() + else if(href_list["viewruntime"]) var/datum/error_viewer/error_viewer = locate(href_list["viewruntime"]) if(!istype(error_viewer)) diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm index ebe6e5d96d4..408fe9b4d52 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm @@ -474,7 +474,7 @@ GLOBAL_VAR(station_nuke_source) SSticker.roundend_check_paused = FALSE return - GLOB.enter_allowed = FALSE + SSlag_switch.set_measure(DISABLE_NON_OBSJOBS, TRUE) var/off_station = 0 var/turf/bomb_location = get_turf(src) diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 748de9c50d4..b32b1936975 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -32,6 +32,8 @@ var/externalreplyamount = 0 ///When was the last time we warned them about not cryoing without an ahelp, set to -5 minutes so that rounstart cryo still warns COOLDOWN_DECLARE(cryo_warned) + ///Tracks say() usage for ic/dchat while slowmode is enabled + COOLDOWN_DECLARE(say_slowmode) ///////// //OTHER// ///////// diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 40d25049c18..d26025f8b66 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -448,6 +448,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( view_size.setZoomMode() fit_viewport() Master.UpdateTickRate() + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_CLIENT_CONNECT, src) ////////////// //DISCONNECT// diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index a0f3a10d651..7a276f4b212 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -181,7 +181,7 @@ to_chat(usr, span_danger("The round is either not ready, or has already finished...")) return - if(!GLOB.enter_allowed) + if(SSlag_switch.measures[DISABLE_NON_OBSJOBS]) to_chat(usr, span_notice("There is an administrative lock on entering the game!")) return @@ -214,7 +214,10 @@ ready = PLAYER_NOT_READY return FALSE - var/this_is_like_playing_right = tgui_alert(usr, "Are you sure you wish to observe? You will not be able to play this round!","Player Setup",list("Yes","No")) + var/less_input_message + if(SSlag_switch.measures[DISABLE_DEAD_KEYLOOP]) + less_input_message = " - Notice: Observer freelook is currently disabled." + var/this_is_like_playing_right = tgui_alert(usr, "Are you sure you wish to observe? You will not be able to play this round![less_input_message]","Player Setup",list("Yes","No")) if(QDELETED(src) || !src.client || this_is_like_playing_right != "Yes") ready = PLAYER_NOT_READY @@ -295,10 +298,6 @@ tgui_alert(usr, get_job_unavailable_error_message(error, rank)) return FALSE - if(SSticker.late_join_disabled) - tgui_alert(usr, "An administrator has disabled late join spawning.") - return FALSE - var/arrivals_docked = TRUE if(SSshuttle.arrivals) close_spawn_windows() //In case we get held up @@ -388,7 +387,10 @@ /mob/dead/new_player/proc/LateChoices() - var/list/dat = list("
Round Duration: [DisplayTimeText(world.time - SSticker.round_start_time)]
") + var/list/dat = list() + if(SSlag_switch.measures[DISABLE_NON_OBSJOBS]) + dat += "
Only Observers may join at this time.

" + dat += "
Round Duration: [DisplayTimeText(world.time - SSticker.round_start_time)]
" if(SSshuttle.emergency) switch(SSshuttle.emergency.mode) if(SHUTTLE_ESCAPE) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index a4d64d5c9a8..9500f17f546 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -365,6 +365,8 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp return client.view_size.setDefault(getScreenSize(client.prefs.widescreenpref))//Let's reset so people can't become allseeing gods SStgui.on_transfer(src, mind.current) // Transfer NanoUIs. + if(mind.current.stat == DEAD && SSlag_switch.measures[DISABLE_DEAD_KEYLOOP]) + to_chat(src, span_warning("To leave your body again use the Ghost verb.")) mind.current.key = key mind.current.client.init_verbs() return TRUE @@ -520,6 +522,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set name = "View Range" set desc = "Change your view range." + if(SSlag_switch.measures[DISABLE_GHOST_ZOOM_TRAY] && !client?.holder) + to_chat(usr, span_notice("That verb is currently globally disabled.")) + return + var/max_view = client.prefs.unlock_content ? GHOST_MAX_VIEW_RANGE_MEMBER : GHOST_MAX_VIEW_RANGE_DEFAULT if(client.view_size.getView() == client.view_size.default) var/list/views = list() @@ -534,6 +540,11 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp /mob/dead/observer/verb/add_view_range(input as num) set name = "Add View Range" set hidden = TRUE + + if(SSlag_switch.measures[DISABLE_GHOST_ZOOM_TRAY] && !client?.holder) + to_chat(usr, span_notice("That verb is currently globally disabled.")) + return + var/max_view = client.prefs.unlock_content ? GHOST_MAX_VIEW_RANGE_MEMBER : GHOST_MAX_VIEW_RANGE_DEFAULT if(input) client.rescale_view(input, 0, ((max_view*2)+1) - 15) @@ -954,6 +965,9 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set desc = "Toggles a view of sub-floor objects" var/static/t_ray_view = FALSE + if(SSlag_switch.measures[DISABLE_GHOST_ZOOM_TRAY] && !client?.holder && !t_ray_view) + to_chat(usr, span_notice("That verb is currently globally disabled.")) + return t_ray_view = !t_ray_view var/list/t_ray_images = list() diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index f17219adc58..551a7ddff03 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -76,6 +76,9 @@ var/turf/T = get_turf(src) if(mind && mind.name && mind.active && !istype(T.loc, /area/ctf)) deadchat_broadcast(" has died at [get_area_name(T)].", "[mind.name]", follow_target = src, turf_target = T, message_type=DEADCHAT_DEATHRATTLE) + if(SSlag_switch.measures[DISABLE_DEAD_KEYLOOP] && !client?.holder) + to_chat(src, span_deadsay(span_big("Observer freelook is disabled.\nPlease use Orbit, Teleport, and Jump to look around."))) + ghostize(TRUE) if(mind) mind.store_memory("Time of death: [tod]", 0) set_drugginess(0) diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index 5e5111b0b1d..4ac954fd6e2 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -148,6 +148,12 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( say_dead(original_message) return + if(client && SSlag_switch.measures[SLOWMODE_SAY] && !HAS_TRAIT(src, TRAIT_BYPASS_MEASURES) && !forced && src == usr) + if(!COOLDOWN_FINISHED(client, say_slowmode)) + to_chat(src, span_warning("Message not sent due to slowmode. Please wait [SSlag_switch.slowmode_cooldown/10] seconds between messages.\n\"[message]\"")) + return + COOLDOWN_START(client, say_slowmode, SSlag_switch.slowmode_cooldown) + if(!can_speak_basic(original_message, ignore_spam, forced)) return @@ -345,7 +351,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( //speech bubble var/list/speech_bubble_recipients = list() for(var/mob/M in listening) - if(M.client && !M.client.prefs.chat_on_map) + if(M.client && (!M.client.prefs.chat_on_map || (SSlag_switch.measures[DISABLE_RUNECHAT] && !HAS_TRAIT(src, TRAIT_BYPASS_MEASURES)))) speech_bubble_recipients.Add(M.client) var/image/I = image('icons/mob/talk.dmi', src, "[bubble_type][say_test(message)]", FLY_LAYER) I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA diff --git a/code/modules/mob/mob_lists.dm b/code/modules/mob/mob_lists.dm index a89739544b0..f6babd95831 100644 --- a/code/modules/mob/mob_lists.dm +++ b/code/modules/mob/mob_lists.dm @@ -49,6 +49,10 @@ /mob/proc/add_to_player_list() SHOULD_CALL_PARENT(TRUE) GLOB.player_list |= src + if(client.holder) + GLOB.keyloop_list |= src + else if(stat != DEAD || !SSlag_switch?.measures[DISABLE_DEAD_KEYLOOP]) + GLOB.keyloop_list |= src if(!SSticker?.mode) return if(stat == DEAD) @@ -60,6 +64,7 @@ /mob/proc/remove_from_player_list() SHOULD_CALL_PARENT(TRUE) GLOB.player_list -= src + GLOB.keyloop_list -= src if(!SSticker?.mode) return if(stat == DEAD) diff --git a/code/modules/mob/mob_say.dm b/code/modules/mob/mob_say.dm index 37a42f4bb17..76f44ab130d 100644 --- a/code/modules/mob/mob_say.dm +++ b/code/modules/mob/mob_say.dm @@ -60,6 +60,12 @@ to_chat(src, span_danger("You cannot talk in deadchat (muted).")) return + if(SSlag_switch.measures[SLOWMODE_SAY] && !HAS_TRAIT(src, TRAIT_BYPASS_MEASURES) && src == usr) + if(!COOLDOWN_FINISHED(client, say_slowmode)) + to_chat(src, span_warning("Message not sent due to slowmode. Please wait [SSlag_switch.slowmode_cooldown/10] seconds between messages.\n\"[message]\"")) + return + COOLDOWN_START(client, say_slowmode, SSlag_switch.slowmode_cooldown) + if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT)) return diff --git a/config/admins.txt b/config/admins.txt index 3d3885d0d16..70293c3880b 100644 --- a/config/admins.txt +++ b/config/admins.txt @@ -144,3 +144,4 @@ StyleMistake = Game Master actioninja = Game Master bobbahbrown = Game Master Jaredfogle = Game Master +WaylandSmithy = Game Master diff --git a/config/config.txt b/config/config.txt index 4cb4daeeddd..23787ed4604 100644 --- a/config/config.txt +++ b/config/config.txt @@ -316,6 +316,9 @@ NOTE_FRESH_DAYS 91.31055 ## Notes older then this will be completely faded out. NOTE_STALE_DAYS 365.2422 +## Uncomment to allow drastic performence enhancemet measures to turn on automatically once there are equal or more clients than the configured amount (will also prompt admin for veto) +#AUTO_LAG_SWITCH_POP 75 + ##Note: all population caps can be used with each other if desired. ## Uncomment for 'soft' population caps, players will be warned while joining if the living crew exceeds the listed number. diff --git a/tgstation.dme b/tgstation.dme index 272ffd2578c..b35027c7f76 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -78,6 +78,7 @@ #include "code\__DEFINES\is_helpers.dm" #include "code\__DEFINES\jobs.dm" #include "code\__DEFINES\keybinding.dm" +#include "code\__DEFINES\lag_switch.dm" #include "code\__DEFINES\language.dm" #include "code\__DEFINES\layers.dm" #include "code\__DEFINES\lighting.dm" @@ -329,6 +330,7 @@ #include "code\controllers\subsystem\input.dm" #include "code\controllers\subsystem\ipintel.dm" #include "code\controllers\subsystem\job.dm" +#include "code\controllers\subsystem\lag_switch.dm" #include "code\controllers\subsystem\language.dm" #include "code\controllers\subsystem\lighting.dm" #include "code\controllers\subsystem\machines.dm"