From e9448ad37df93db1e595f5642d1822dbe65971a2 Mon Sep 17 00:00:00 2001
From: Zephyr <12817816+ZephyrTFA@users.noreply.github.com>
Date: Sat, 21 Oct 2023 15:18:59 -0400
Subject: [PATCH] Updates signaler investigate code | Adds some nice QOL
changes for signalers | Enforces cooldown on signaler circuitry (#78974)
## About The Pull Request
See title.
If someone was abusing signalers previously to cause server lag, going
into list signalers would actually cause even worse lag as byond sat
there and processed thousands of items into a string over and over,
which would cause string format operations on longer and longer strings,
resulting in more and more overhead. This is bad.
So instead there is now a limit to the size of the list, currently I
have that set to 500 although I am open to increasing and even reducing
the number.
I have also made signalers slightly more intuitive by having the
cooldown actually displayed in the ui as a tooltip instead of just being
a secret feature you didnt know about unless you code dived. Also made
the cooldown actually respected by things such as circuitry where it
didnt even implement the cooldown and would happily send as many signals
as you had items connected to your proximity circuit.
## Why It's Good For The Game
Admins won't accidentally kill the server by trying to parse a lag
machines signal list. Players lagging the server? No, how about the
admins trying to fix it!
## Changelog
:cl:
qol: signalers now tell you their cooldown and also use balloon alerts
/:cl:
---
code/__DEFINES/logging.dm | 3 ++
code/_globalvars/logging.dm | 14 +++++++--
code/modules/admin/verbs/list_exposer.dm | 4 +--
code/modules/assembly/signaler.dm | 30 +++++++++++--------
.../file_system/programs/signalcommander.dm | 19 ++++++++----
.../wiremod/components/action/radio.dm | 16 ++++++++--
tgui/packages/tgui/interfaces/Signaler.js | 3 +-
7 files changed, 64 insertions(+), 25 deletions(-)
diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm
index 0a4424b0d73..daec53a76ec 100644
--- a/code/__DEFINES/logging.dm
+++ b/code/__DEFINES/logging.dm
@@ -5,6 +5,9 @@
/// Admins can still manually request a re-render
#define LOG_UPDATE_TIMEOUT 5 SECONDS
+// The maximum number of entries allowed in the signaler investigate log, keep this relatively small to prevent performance issues when an admin tries to query it
+#define INVESTIGATE_SIGNALER_LOG_MAX_LENGTH 500
+
//Investigate logging defines
#define INVESTIGATE_ACCESSCHANGES "id_card_changes"
#define INVESTIGATE_ATMOS "atmos"
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index 4d0b0698679..fc6919c3a3b 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -60,9 +60,17 @@ GLOBAL_PROTECT(admin_activities)
GLOBAL_LIST_EMPTY(bombers)
GLOBAL_PROTECT(bombers)
-/// All signals here in format: "[src] used [REF(src)] @ location [src.loc]: [freq]/[code]"
-GLOBAL_LIST_EMPTY(lastsignalers)
-GLOBAL_PROTECT(lastsignalers)
+/// Investigate log for signaler usage, use the add_to_signaler_investigate_log proc
+GLOBAL_LIST_EMPTY(investigate_signaler)
+GLOBAL_PROTECT(investigate_signaler)
+
+/// Used to add a text log to the signaler investigation log.
+/// Do not add to the list directly; if the list is too large it can cause lag when an admin tries to view it.
+/proc/add_to_signaler_investigate_log(text)
+ var/log_length = length(GLOB.investigate_signaler)
+ if(log_length >= INVESTIGATE_SIGNALER_LOG_MAX_LENGTH)
+ GLOB.investigate_signaler = GLOB.investigate_signaler.Copy((INVESTIGATE_SIGNALER_LOG_MAX_LENGTH - log_length) + 2)
+ GLOB.investigate_signaler += list(text)
/// Stores who uploaded laws to which silicon-based lifeform, and what the law was
GLOBAL_LIST_EMPTY(lawchanges)
diff --git a/code/modules/admin/verbs/list_exposer.dm b/code/modules/admin/verbs/list_exposer.dm
index 92a37a7f609..445097e8bef 100644
--- a/code/modules/admin/verbs/list_exposer.dm
+++ b/code/modules/admin/verbs/list_exposer.dm
@@ -13,8 +13,8 @@
if(!SSticker.HasRoundStarted())
tgui_alert(usr, "The game hasn't started yet!")
return
- var/data = "Showing last [length(GLOB.lastsignalers)] signalers.
"
- for(var/entry in GLOB.lastsignalers)
+ var/data = "Showing last [length(GLOB.investigate_signaler)] signalers."
+ for(var/entry in GLOB.investigate_signaler)
data += "[entry] "
usr << browse(data, "window=lastsignalers;size=800x500")
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index de9a2740497..d38d20790e1 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -10,15 +10,20 @@
drop_sound = 'sound/items/handling/component_drop.ogg'
pickup_sound = 'sound/items/handling/component_pickup.ogg'
+ /// The code sent by this signaler.
var/code = DEFAULT_SIGNALER_CODE
+ /// The frequency this signaler is set to.
var/frequency = FREQ_SIGNALER
+ /// How long of a cooldown exists on this signaller.
+ var/cooldown_length = 1 SECONDS
+ /// The radio frequency connection this signaler is using.
var/datum/radio_frequency/radio_connection
- ///Holds the mind that commited suicide.
+ /// Holds the mind that commited suicide.
var/datum/mind/suicider
- ///Holds a reference string to the mob, decides how much of a gamer you are.
+ /// Holds a reference string to the mob, decides how much of a gamer you are.
var/suicide_mob
+ /// How many tiles away can you hear when this signaler is used or gets activated.
var/hearing_range = 1
-
/// String containing the last piece of logging data relating to when this signaller has received a signal.
var/last_receive_signal_log
@@ -77,23 +82,26 @@
/obj/item/assembly/signaler/ui_data(mob/user)
var/list/data = list()
data["frequency"] = frequency
+ data["cooldown"] = cooldown_length
data["code"] = code
data["minFrequency"] = MIN_FREE_FREQ
data["maxFrequency"] = MAX_FREE_FREQ
return data
-/obj/item/assembly/signaler/ui_act(action, params)
+/obj/item/assembly/signaler/ui_act(action, params, datum/tgui/ui)
. = ..()
if(.)
return
switch(action)
if("signal")
- if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_SIGNALLER_SEND))
- to_chat(usr, span_warning("[src] is still recharging..."))
- return
- TIMER_COOLDOWN_START(src, COOLDOWN_SIGNALLER_SEND, 1 SECONDS)
+ if(cooldown_length > 0)
+ if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_SIGNALLER_SEND))
+ balloon_alert(ui.user, "recharging!")
+ return
+ TIMER_COOLDOWN_START(src, COOLDOWN_SIGNALLER_SEND, cooldown_length)
INVOKE_ASYNC(src, PROC_REF(signal))
+ balloon_alert(ui.user, "signaled")
. = TRUE
if("freq")
var/new_frequency = sanitize_frequency(unformat_frequency(params["freq"]), TRUE)
@@ -141,10 +149,8 @@
var/time = time2text(world.realtime,"hh:mm:ss")
var/turf/T = get_turf(src)
- var/logging_data
- if(usr)
- logging_data = "[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]"
- GLOB.lastsignalers.Add(logging_data)
+ var/logging_data = "[time] : [key_name(usr)] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]"
+ add_to_signaler_investigate_log(logging_data)
var/datum/signal/signal = new(list("code" = code), logging_data = logging_data)
radio_connection.post_signal(src, signal)
diff --git a/code/modules/modular_computers/file_system/programs/signalcommander.dm b/code/modules/modular_computers/file_system/programs/signalcommander.dm
index 6d636bab370..e8140b62b17 100644
--- a/code/modules/modular_computers/file_system/programs/signalcommander.dm
+++ b/code/modules/modular_computers/file_system/programs/signalcommander.dm
@@ -14,6 +14,10 @@
var/signal_code = DEFAULT_SIGNALER_CODE
/// Radio connection datum used by signalers.
var/datum/radio_frequency/radio_connection
+ /// How long do we cooldown before we can send another signal?
+ var/signal_cooldown_time = 1 SECONDS
+ /// Cooldown store
+ COOLDOWN_DECLARE(signal_cooldown)
/datum/computer_file/program/signal_commander/on_start(mob/living/user)
. = ..()
@@ -26,6 +30,7 @@
/datum/computer_file/program/signal_commander/ui_data(mob/user)
var/list/data = list()
data["frequency"] = signal_frequency
+ data["cooldown"] = signal_cooldown_time
data["code"] = signal_code
data["minFrequency"] = MIN_FREE_FREQ
data["maxFrequency"] = MAX_FREE_FREQ
@@ -55,13 +60,18 @@
if(!radio_connection)
return
+ if(!COOLDOWN_FINISHED(src, signal_cooldown))
+ computer.balloon_alert(usr, "cooling down!")
+ return
+
+ COOLDOWN_START(src, signal_cooldown, signal_cooldown_time)
+ computer.balloon_alert(usr, "signaled")
+
var/time = time2text(world.realtime,"hh:mm:ss")
var/turf/T = get_turf(computer)
- var/logging_data
- if(usr)
- logging_data = "[time] : [usr.key] used [computer] @ location ([T.x],[T.y],[T.z]) : [format_frequency(signal_frequency)]/[signal_code]"
- GLOB.lastsignalers.Add(logging_data)
+ var/logging_data = "[time] : [key_name(usr)] used the computer '[initial(computer.name)]' @ location ([T.x],[T.y],[T.z]) : [format_frequency(signal_frequency)]/[signal_code]"
+ add_to_signaler_investigate_log(logging_data)
var/datum/signal/signal = new(list("code" = signal_code), logging_data = logging_data)
radio_connection.post_signal(computer, signal)
@@ -70,4 +80,3 @@
SSradio.remove_object(computer, signal_frequency)
signal_frequency = new_frequency
radio_connection = SSradio.add_object(computer, signal_frequency, RADIO_SIGNALER)
- return
diff --git a/code/modules/wiremod/components/action/radio.dm b/code/modules/wiremod/components/action/radio.dm
index cd13e0e1e45..74b2c54c1b8 100644
--- a/code/modules/wiremod/components/action/radio.dm
+++ b/code/modules/wiremod/components/action/radio.dm
@@ -28,8 +28,17 @@
/// The ckey of the user who used the shell we were placed in, important for signalling logs.
var/owner_ckey = null
+ /// The radio connection we are using to receive signals.
var/datum/radio_frequency/radio_connection
+ /// How long of a cooldown we have before we can send another signal.
+ var/signal_cooldown_time = 1 SECONDS
+
+/obj/item/circuit_component/radio/Initialize(mapload)
+ . = ..()
+ if(signal_cooldown_time > 0)
+ desc = "[desc] It has a [signal_cooldown_time * 0.1] second cooldown between sending signals."
+
/obj/item/circuit_component/radio/register_shell(atom/movable/shell)
parent_shell = shell
var/potential_fingerprints = shell.fingerprintslast
@@ -65,8 +74,10 @@
INVOKE_ASYNC(src, PROC_REF(handle_radio_input), port)
/obj/item/circuit_component/radio/proc/handle_radio_input(datum/port/input/port)
- var/frequency = freq.value
+ if(!TIMER_COOLDOWN_CHECK(parent, COOLDOWN_SIGNALLER_SEND))
+ return
+ var/frequency = freq.value
if(frequency != current_freq)
SSradio.remove_object(src, current_freq)
radio_connection = SSradio.add_object(src, frequency, RADIO_SIGNALER)
@@ -84,7 +95,8 @@
loggable_strings += ": The last fingerprints on the containing shell was [parent_shell.fingerprintslast]."
var/loggable_string = loggable_strings.Join(" ")
- GLOB.lastsignalers.Add(loggable_string)
+ add_to_signaler_investigate_log(loggable_string)
+ TIMER_COOLDOWN_START(parent, COOLDOWN_SIGNALLER_SEND, signal_cooldown_time)
var/datum/signal/signal = new(list("code" = signal_code, "key" = parent?.owner_id), logging_data = loggable_string)
radio_connection.post_signal(src, signal)
diff --git a/tgui/packages/tgui/interfaces/Signaler.js b/tgui/packages/tgui/interfaces/Signaler.js
index ac353a2d39a..d69509ef3b4 100644
--- a/tgui/packages/tgui/interfaces/Signaler.js
+++ b/tgui/packages/tgui/interfaces/Signaler.js
@@ -16,7 +16,7 @@ export const Signaler = (props, context) => {
export const SignalerContent = (props, context) => {
const { act, data } = useBackend(context);
- const { code, frequency, minFrequency, maxFrequency } = data;
+ const { code, frequency, cooldown, minFrequency, maxFrequency } = data;
const color = 'rgba(13, 13, 213, 0.7)';
const backColor = 'rgba(0, 0, 69, 0.5)';
return (
@@ -94,6 +94,7 @@ export const SignalerContent = (props, context) => {