Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSlag_switch from /tg/ #2133

Merged
merged 7 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion check_regex.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ standards:

- exactly:
[
297,
298,
"non-bitwise << uses",
'(?<!\d)(?<!\d\s)(?<!<)<<(?!=|\s\d|\d|<|\/)',
]
Expand Down
2 changes: 2 additions & 0 deletions code/__DEFINES/dcs/signals.dm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#define COMPONENT_GLOB_BLOCK_CINEMATIC 1
/// ingame button pressed (/obj/machinery/button/button)
#define COMSIG_GLOB_BUTTON_PRESSED "!button_pressed"
/// a client (re)connected, after all /client/New() checks have passed : (client/connected_client)
#define COMSIG_GLOB_CLIENT_CONNECT "!client_connect"

// signals from globally accessible objects
/// from SSsun when the sun changes position : (azimuth)
Expand Down
24 changes: 24 additions & 0 deletions code/__DEFINES/lag_switch.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// All of the possible Lag Switch lag mitigation measures
// If you add more do not forget to update MEASURES_AMOUNT accordingly
/// Stops ghosts flying around freely, they can still jump and orbit, staff exempted
#define DISABLE_DEAD_KEYLOOP 1
/// Stops ghosts using zoom/t-ray verbs and resets their view if zoomed out, staff exempted
#define DISABLE_GHOST_ZOOM_TRAY 2
/// Disable runechat and enable the bubbles, speaking mobs with TRAIT_BYPASS_MEASURES exempted
#define DISABLE_RUNECHAT 3
/// Disable icon2html procs from verbs like examine, mobs calling with TRAIT_BYPASS_MEASURES exempted
#define DISABLE_USR_ICON2HTML 4
/// Prevents anyone from joining the game as anything but observer
#define DISABLE_NON_OBSJOBS 5
/// Limit IC/dchat spam to one message every x seconds per client, TRAIT_BYPASS_MEASURES exempted
#define SLOWMODE_SAY 6
/// Disables parallax, as if everyone had disabled their preference, TRAIT_BYPASS_MEASURES exempted
#define DISABLE_PARALLAX 7
/// Disables footsteps, TRAIT_BYPASS_MEASURES exempted
#define DISABLE_FOOTSTEPS 8
/// Disables planet deletion
#define DISABLE_PLANETDEL 9
/// Disables ALL new planet generation, TRAIT_BYPASS_MEASURES exempted
#define DISABLE_PLANETGEN 10

#define MEASURES_AMOUNT 10 // The total number of switches defined above
2 changes: 2 additions & 0 deletions code/__DEFINES/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_SCOOPABLE "scoopable"
//your smooches actually deal damage to their target
#define TRAIT_KISS_OF_DEATH "kiss_of_death"
/// This mob overrides certian SSlag_switch measures with this special trait
#define TRAIT_BYPASS_MEASURES "bypass_lagswitch_measures"
//non-mob traits
/// Used for limb-based paralysis, where replacing the limb will fix it.
#define TRAIT_PARALYSIS "paralysis"
Expand Down
4 changes: 4 additions & 0 deletions code/__HELPERS/icons.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,8 @@ GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of ico
/proc/icon2html(atom/thing, client/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/icon2collapse = thing
Expand Down Expand Up @@ -1354,6 +1356,8 @@ GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of ico
/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)
Expand Down
1 change: 1 addition & 0 deletions code/_globalvars/lists/mobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ GLOBAL_LIST_EMPTY(stealthminID) //reference list with IDs that store ckeys,
//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
Expand Down
1 change: 1 addition & 0 deletions code/_globalvars/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,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_THERMAL_VISION" = TRAIT_THERMAL_VISION,
Expand Down
4 changes: 4 additions & 0 deletions code/_onclick/hud/parallax.dm
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@

/datum/hud/proc/apply_parallax_pref(mob/viewmob)
var/mob/screenmob = viewmob || mymob

if (SSlag_switch.measures[DISABLE_PARALLAX] && !HAS_TRAIT(viewmob, TRAIT_BYPASS_MEASURES))
return FALSE

var/client/C = screenmob.client
if(C.prefs)
var/pref = C.prefs.parallax
Expand Down
4 changes: 4 additions & 0 deletions code/controllers/configuration/entries/general.dm
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,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
config_entry_value = null
min_val = 0
Expand Down
6 changes: 2 additions & 4 deletions code/controllers/subsystem/input.dm
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,5 @@ SUBSYSTEM_DEF(input)
user.set_macros()

/datum/controller/subsystem/input/fire()
var/list/clients = GLOB.clients // Let's sing the list cache song
for(var/i in 1 to clients.len)
var/client/C = clients[i]
C.keyLoop()
for(var/mob/user as anything in GLOB.keyloop_list)
user.focus?.keyLoop(user.client)
156 changes: 156 additions & 0 deletions code/controllers/subsystem/lag_switch.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/// 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, DISABLE_PARALLAX, DISABLE_FOOTSTEPS, DISABLE_PLANETDEL)
/// 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. (<a href='?_src_=holder;[HrefToken()];change_lag_switch_option=CANCEL'>CANCEL</a>)")
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()
if(DISABLE_PARALLAX)
if (state)
to_chat(world, span_boldannounce("Parallax has been disabled for performance concerns."))
else
to_chat(world, span_boldannounce("Parallax has been re-enabled."))

for (var/mob/mob as anything in GLOB.mob_list)
mob.hud_used?.update_parallax_pref()
if(DISABLE_FOOTSTEPS)
if (state)
to_chat(world, span_boldannounce("Footstep sounds have been disabled for performance concerns."))
else
to_chat(world, span_boldannounce("Footstep sounds have been re-enabled."))
if(DISABLE_PLANETDEL)
if (state)
to_chat(world, span_boldannounce("Planet deletion and regeneration has been disabled for performance concerns."))
else
to_chat(world, span_boldannounce("Planet deletion has been re-enabled."))
if(DISABLE_PLANETGEN)
if (state)
to_chat(world, span_boldannounce("Planet generation has been disabled for performance concerns. You can still dock at already-generated planets."))
else
to_chat(world, span_boldannounce("Planet generation has been re-enabled."))

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
2 changes: 2 additions & 0 deletions code/datums/chatmessage.dm
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,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()

Expand Down
10 changes: 9 additions & 1 deletion code/datums/components/footstep.dm
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#define SHOULD_DISABLE_FOOTSTEPS(source) ((SSlag_switch.measures[DISABLE_FOOTSTEPS] && !(HAS_TRAIT(source, TRAIT_BYPASS_MEASURES))) || HAS_TRAIT(source, TRAIT_SILENT_FOOTSTEPS))

///Footstep component. Plays footsteps at parents location when it is appropriate.
/datum/component/footstep
///How many steps the parent has taken since the last time a footstep was played.
Expand Down Expand Up @@ -71,6 +73,9 @@
/datum/component/footstep/proc/play_simplestep()
SIGNAL_HANDLER

if (SHOULD_DISABLE_FOOTSTEPS(parent))
return

var/turf/open/T = prepare_step()
if(!T)
return
Expand All @@ -94,8 +99,9 @@
/datum/component/footstep/proc/play_humanstep()
SIGNAL_HANDLER

if(HAS_TRAIT(parent, TRAIT_SILENT_FOOTSTEPS))
if (SHOULD_DISABLE_FOOTSTEPS(parent))
return

var/turf/open/T = prepare_step()
if(!T)
return
Expand All @@ -115,3 +121,5 @@
GLOB.barefootstep[T.barefootstep][2] * volume,
TRUE,
GLOB.barefootstep[T.barefootstep][3] + e_range, falloff_distance = 1)

#undef SHOULD_DISABLE_FOOTSTEPS
3 changes: 1 addition & 2 deletions code/datums/world_topic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,7 @@
.["version"] = GLOB.game_version
.["mode"] = GLOB.master_mode
.["respawn"] = config ? !CONFIG_GET(flag/norespawn) : FALSE
.["enter"] = GLOB.enter_allowed
.["vote"] = CONFIG_GET(flag/allow_vote_mode)
.["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
Expand Down
5 changes: 1 addition & 4 deletions code/game/world.dm
Original file line number Diff line number Diff line change
Expand Up @@ -289,10 +289,7 @@ GLOBAL_VAR(restart_counter)

var/list/features = list()

if(GLOB.master_mode)
features += GLOB.master_mode

if (!GLOB.enter_allowed)
if(LAZYACCESS(SSlag_switch.measures, DISABLE_NON_OBSJOBS))
features += "closed"

var/s = ""
Expand Down
47 changes: 38 additions & 9 deletions code/modules/admin/admin.dm
Original file line number Diff line number Diff line change
Expand Up @@ -624,15 +624,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, "<B>New players may no longer enter the game.</B>", confidential = TRUE)
else
to_chat(world, "<B>New players may now enter the game.</B>", confidential = TRUE)
log_admin("[key_name(usr)] toggled new player game entering.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] toggled new player game entering.</span>")
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"
Expand Down Expand Up @@ -984,3 +981,35 @@
"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("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Lag Switches</title></head><body><h2><B>Lag (Reduction) Switches</B></h2>")
dat += "Automatic Trigger: <a href='?_src_=holder;[HrefToken()];change_lag_switch_option=TOGGLE_AUTO'><b>[SSlag_switch.auto_switch ? "On" : "Off"]</b></a><br/>"
dat += "Population Threshold: <a href='?_src_=holder;[HrefToken()];change_lag_switch_option=NUM'><b>[SSlag_switch.trigger_pop]</b></a><br/>"
dat += "Slowmode Cooldown (toggle On/Off below): <a href='?_src_=holder;[HrefToken()];change_lag_switch_option=SLOWCOOL'><b>[SSlag_switch.slowmode_cooldown/10] seconds</b></a><br/>"
dat += "<br/><b>SET ALL MEASURES: <a href='?_src_=holder;[HrefToken()];change_lag_switch=ALL_ON'>ON</a> | <a href='?_src_=holder;[HrefToken()];change_lag_switch=ALL_OFF'>OFF</a></b><br/>"
dat += "<br/>Disable ghosts zoom and t-ray verbs (except staff): <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_GHOST_ZOOM_TRAY]'><b>[SSlag_switch.measures[DISABLE_GHOST_ZOOM_TRAY] ? "On" : "Off"]</b></a><br/>"
dat += "Disable planet deletion: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_PLANETDEL]'><b>[SSlag_switch.measures[DISABLE_PLANETDEL] ? "On" : "Off"]</b></a><br/>"
dat += "Disable <b>ALL</b> planet GENERATION: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_PLANETGEN]'><b>[SSlag_switch.measures[DISABLE_PLANETGEN] ? "On" : "Off"]</b></a><br/>"
dat += "Disable late joining: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_NON_OBSJOBS]'><b>[SSlag_switch.measures[DISABLE_NON_OBSJOBS] ? "On" : "Off"]</b></a><br/>"
dat += "<br/>============! MAD GHOSTS ZONE !============<br/>"
dat += "Disable deadmob keyLoop (except staff, informs dchat): <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_DEAD_KEYLOOP]'><b>[SSlag_switch.measures[DISABLE_DEAD_KEYLOOP] ? "On" : "Off"]</b></a><br/>"
dat += "==========================================<br/>"
dat += "<br/><b>Measures below can be bypassed with a <abbr title='TRAIT_BYPASS_MEASURES'><u>special trait</u></abbr></b><br/>"
dat += "Slowmode say verb (informs world): <a href='?_src_=holder;[HrefToken()];change_lag_switch=[SLOWMODE_SAY]'><b>[SSlag_switch.measures[SLOWMODE_SAY] ? "On" : "Off"]</b></a><br/>"
dat += "Disable runechat: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_RUNECHAT]'><b>[SSlag_switch.measures[DISABLE_RUNECHAT] ? "On" : "Off"]</b></a> - <span style='font-size:80%'>trait applies to speaker</span><br/>"
dat += "Disable examine icons: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_USR_ICON2HTML]'><b>[SSlag_switch.measures[DISABLE_USR_ICON2HTML] ? "On" : "Off"]</b></a> - <span style='font-size:80%'>trait applies to examiner</span><br/>"
dat += "Disable parallax: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_PARALLAX]'><b>[SSlag_switch.measures[DISABLE_PARALLAX] ? "On" : "Off"]</b></a> - <span style='font-size:80%'>trait applies to character</span><br />"
dat += "Disable footsteps: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_FOOTSTEPS]'><b>[SSlag_switch.measures[DISABLE_FOOTSTEPS] ? "On" : "Off"]</b></a> - <span style='font-size:80%'>trait applies to character</span><br />"
dat += "</body></html>"
usr << browse(dat.Join(), "window=lag_switch_panel;size=420x480")
Loading