From fed38ec2c8b2f283aeae279e89242b96f4e1bc54 Mon Sep 17 00:00:00 2001
From: Letter N <24603524+LetterN@users.noreply.github.com>
Date: Tue, 27 Jan 2026 02:52:19 +0800
Subject: [PATCH] update tgs commands (#7511)
---
code/__HELPERS/chat.dm | 18 +--
code/__HELPERS/game.dm | 16 ++-
code/controllers/subsystem/ticker.dm | 6 +-
code/modules/admin/chat_commands.dm | 155 +++++++++++++-------------
code/modules/admin/verbs/adminhelp.dm | 4 +-
5 files changed, 106 insertions(+), 93 deletions(-)
diff --git a/code/__HELPERS/chat.dm b/code/__HELPERS/chat.dm
index 94982ca90fb..eb97735b983 100644
--- a/code/__HELPERS/chat.dm
+++ b/code/__HELPERS/chat.dm
@@ -36,42 +36,46 @@
*/
/**
- * Sends a message to TGS chat channels.
+ * Asynchronously sends a message to TGS chat channels.
*
- * message - The message to send.
+ * message - The [/datum/tgs_message_content] to send.
* channel_tag - Required. If "", the message with be sent to all connected (Game-type for TGS3) channels. Otherwise, it will be sent to TGS4 channels with that tag (Delimited by ','s).
+ * admin_only - Determines if this communication can only be sent to admin only channels.
*/
-/proc/send2chat(message, channel_tag)
+/proc/send2chat(datum/tgs_message_content/message, channel_tag, admin_only = FALSE)
+ set waitfor = FALSE
if(channel_tag == null || !world.TgsAvailable())
return
var/datum/tgs_version/version = world.TgsVersion()
if(channel_tag == "" || version.suite == 3)
- world.TgsTargetedChatBroadcast(message, FALSE)
+ world.TgsTargetedChatBroadcast(message, admin_only)
return
var/list/channels_to_use = list()
for(var/I in world.TgsChatChannelInfo())
var/datum/tgs_chat_channel/channel = I
var/list/applicable_tags = splittext(channel.custom_tag, ",")
- if(channel_tag in applicable_tags)
+ if((!admin_only || channel.is_admin_channel) && (channel_tag in applicable_tags))
channels_to_use += channel
if(channels_to_use.len)
world.TgsChatBroadcast(message, channels_to_use)
/**
- * Sends a message to TGS admin chat channels.
+ * Asynchronously sends a message to TGS admin chat channels.
*
* category - The category of the mssage.
* message - The message to send.
*/
/proc/send2adminchat(category, message, embed_links = FALSE)
+ set waitfor = FALSE
+
category = replacetext(replacetext(category, "\proper", ""), "\improper", "")
message = replacetext(replacetext(message, "\proper", ""), "\improper", "")
if(!embed_links)
message = GLOB.has_discord_embeddable_links.Replace(replacetext(message, "`", ""), " ```$1``` ")
- world.TgsTargetedChatBroadcast("[category] | [message]", TRUE)
+ world.TgsTargetedChatBroadcast(new /datum/tgs_message_content("[category] | [message]"), TRUE)
/// Handles text formatting for item use hints in examine text
#define EXAMINE_HINT(text) ("" + text + "")
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index aa3ce4be3a3..e9448f9f1f2 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -568,8 +568,8 @@
return hear
///Get active players who are playing in the round
-/proc/get_active_player_count(alive_check = FALSE, afk_check = FALSE, human_check = FALSE)
- var/active_players = 0
+/proc/get_active_player_list(alive_check = FALSE, afk_check = FALSE, human_check = FALSE)
+ var/list/active_players = list()
for(var/mob/player_mob as anything in GLOB.player_list)
if(!player_mob?.client)
continue
@@ -582,8 +582,12 @@
if(isnewplayer(player_mob)) // exclude people in the lobby
continue
if(isobserver(player_mob)) // Ghosts are fine if they were playing once (didn't start as observers)
- // var/mob/observer/dead/ghost_player = player_mob
- // if(ghost_player.started_as_observer) // Exclude people who started as observers
- continue
- active_players++
+ var/mob/observer/dead/ghost_player = player_mob
+ if(ghost_player.started_as_observer) // Exclude people who started as observers
+ continue
+ active_players += player_mob
return active_players
+
+///Counts active players who are playing in the round
+/proc/get_active_player_count(alive_check = FALSE, afk_check = FALSE, human_check = FALSE)
+ return length(get_active_player_list(alive_check, afk_check, human_check))
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 3259b8bc40f..4f344f2258f 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -111,12 +111,11 @@ SUBSYSTEM_DEF(ticker)
if(blackbox)
blackbox.save_all_data_to_sql()
- send2irc("Server", "A round of [mode.name] just ended.")
if(CONFIG_GET(string/chat_roundend_notice_tag))
var/broadcastmessage = "The round has ended."
if(CONFIG_GET(string/chat_reboot_role))
broadcastmessage += "\n\n<@&[CONFIG_GET(string/chat_reboot_role)]>, the server will reboot shortly!"
- send2chat(broadcastmessage, CONFIG_GET(string/chat_roundend_notice_tag))
+ send2chat(new /datum/tgs_message_content(broadcastmessage), CONFIG_GET(string/chat_roundend_notice_tag))
SSdbcore.SetRoundEnd()
SSpersistence.SavePersistence()
@@ -125,7 +124,7 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/proc/on_mc_init_finish()
- send2irc("Server lobby is loaded and open at byond://[config_legacy.serverurl ? config_legacy.serverurl : (config_legacy.server ? config_legacy.server : "[world.address]:[world.port]")]")
+ send2chat(new /datum/tgs_message_content("Server lobby is loaded and open at byond://[config_legacy.serverurl ? config_legacy.serverurl : (config_legacy.server ? config_legacy.server : "[world.address]:[world.port]")]"))
to_chat(world, "Welcome to the pregame lobby!")
to_chat(world, "Please set up your character and select ready. The round will start in [CONFIG_GET(number/lobby_countdown)] seconds.")
SEND_SOUND(world, sound('sound/misc/server-ready.ogg', volume = 100))
@@ -552,6 +551,7 @@ SUBSYSTEM_DEF(ticker)
to_chat(world, "
The round has ended.")
log_game("The round has ended.")
+ send2adminchat("Server", "Round just ended.")
for(var/datum/callback/roundend_callbacks as anything in round_end_events)
roundend_callbacks.InvokeAsync()
diff --git a/code/modules/admin/chat_commands.dm b/code/modules/admin/chat_commands.dm
index d64a78cb689..112fdbd796d 100644
--- a/code/modules/admin/chat_commands.dm
+++ b/code/modules/admin/chat_commands.dm
@@ -1,45 +1,17 @@
-#define IRC_STATUS_THROTTLE 5
-
-/datum/tgs_chat_command/ircstatus
- name = "status"
- help_text = "Gets the admincount, playercount, gamemode, and true game mode of the server"
+/datum/tgs_chat_command/reload_admins
+ name = "reload_admins"
+ help_text = "Forces the server to reload admins."
admin_only = TRUE
- var/last_irc_status = 0
-/datum/tgs_chat_command/ircstatus/Run(datum/tgs_chat_user/sender, params)
- var/rtod = REALTIMEOFDAY
- if(rtod - last_irc_status < IRC_STATUS_THROTTLE)
- return
- last_irc_status = rtod
- var/list/adm = get_admin_counts()
- var/list/allmins = adm["total"]
- var/status = "Admins: [allmins.len] (Active: [english_list(adm["present"])] AFK: [english_list(adm["afk"])] Stealth: [english_list(adm["stealth"])] Skipped: [english_list(adm["noflags"])]). "
- status += "Players: [GLOB.clients.len]" //(Active: [get_active_player_count(0,1,0)]). Mode: [SSticker.mode ? SSticker.mode.name : "Not started"]."
- return status
+/datum/tgs_chat_command/reload_admins/Run(datum/tgs_chat_user/sender, params)
+ ReloadAsync()
+ log_admin("[sender.friendly_name] reloaded admins via chat command.")
+ message_admins("[sender.friendly_name] reloaded admins via chat command.")
+ return "Admins reloaded."
-/datum/tgs_chat_command/irccheck
- name = "check"
- help_text = "Gets the playercount, gamemode, and address of the server"
- var/last_irc_check = 0
-
-/datum/tgs_chat_command/irccheck/Run(datum/tgs_chat_user/sender, params)
- var/rtod = REALTIMEOFDAY
- if(rtod - last_irc_check < IRC_STATUS_THROTTLE)
- return
- last_irc_check = rtod
- var/server = null //CONFIG_GET(string/server)
- //return "[round_id ? "Round #[round_id]: " : ""][clients.len] players on [SSmapping.config_legacy.map_name], Mode: [master_mode]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]"
- var/current_state
- switch(SSticker.current_state)
- if(GAME_STATE_PREGAME)
- current_state = "pregame"
- if(GAME_STATE_SETTING_UP)
- current_state = "starting"
- if(GAME_STATE_PLAYING)
- current_state = "active"
- if(GAME_STATE_FINISHED)
- current_state = "finishing"
- return "[GLOB.clients.len] players on [(LEGACY_MAP_DATUM).name], Mode: [master_mode]; round [current_state] -- Duration [roundduration2text()] -- [server ? server : "[world.internet_address]:[world.port]"]"
+/datum/tgs_chat_command/reload_admins/proc/ReloadAsync()
+ set waitfor = FALSE
+ load_admins()
/datum/tgs_chat_command/ahelp
name = "ahelp"
@@ -49,7 +21,7 @@
/datum/tgs_chat_command/ahelp/Run(datum/tgs_chat_user/sender, params)
var/list/all_params = splittext(params, " ")
if(all_params.len < 2)
- return "Insufficient parameters"
+ return new /datum/tgs_message_content("Insufficient parameters")
var/target = all_params[1]
all_params.Cut(1, 2)
var/id = text2num(target)
@@ -58,10 +30,8 @@
if(AH)
target = AH.initiator_ckey
else
- return "Ticket #[id] not found!"
- var/res = IrcPm(target, all_params.Join(" "), sender.friendly_name)
- if(res != "Message Successful")
- return res
+ return new /datum/tgs_message_content("Ticket #[id] not found!")
+ return new /datum/tgs_message_content(IrcPm(target, all_params.Join(" "), sender.friendly_name))
/datum/tgs_chat_command/namecheck
name = "namecheck"
@@ -71,10 +41,10 @@
/datum/tgs_chat_command/namecheck/Run(datum/tgs_chat_user/sender, params)
params = trim(params)
if(!params)
- return "Insufficient parameters"
+ return new /datum/tgs_message_content("Insufficient parameters")
log_admin("Chat Name Check: [sender.friendly_name] on [params]")
message_admins("Name checking [params] from [sender.friendly_name]")
- return keywords_lookup(params, 1)
+ return new /datum/tgs_message_content(keywords_lookup(params, 1))
/datum/tgs_chat_command/adminwho
name = "adminwho"
@@ -82,22 +52,7 @@
admin_only = TRUE
/datum/tgs_chat_command/adminwho/Run(datum/tgs_chat_user/sender, params)
- return ircadminwho()
-
-GLOBAL_LIST(round_end_notifiees)
-
-/datum/tgs_chat_command/endnotify
- name = "notify"
- help_text = "Pings the invoker when the round ends"
- admin_only = FALSE
-
-/datum/tgs_chat_command/endnotify/Run(datum/tgs_chat_user/sender, params)
- //if(!SSticker.IsRoundInProgress() && SSticker.HasRoundStarted())
- if(SSticker.current_state == GAME_STATE_FINISHED)
- return "[sender.mention], the round has already ended!"
- LAZYINITLIST(GLOB.round_end_notifiees)
- GLOB.round_end_notifiees[sender.mention] = TRUE
- return "I will notify [sender.mention] when the round ends."
+ return new /datum/tgs_message_content(tgsadminwho())
/datum/tgs_chat_command/sdql
name = "sdql"
@@ -106,29 +61,79 @@ GLOBAL_LIST(round_end_notifiees)
/datum/tgs_chat_command/sdql/Run(datum/tgs_chat_user/sender, params)
if(GLOB.AdminProcCaller)
- return "Unable to run query, another admin proc call is in progress. Try again later."
+ return new /datum/tgs_message_content("Unable to run query, another admin proc call is in progress. Try again later.")
GLOB.AdminProcCaller = "CHAT_[sender.friendly_name]" //_ won't show up in ckeys so it'll never match with a real admin
var/list/results = world.SDQL2_query(params, GLOB.AdminProcCaller, GLOB.AdminProcCaller)
GLOB.AdminProcCaller = null
if(!results)
- return "Query produced no output"
+ return new /datum/tgs_message_content("Query produced no output")
var/list/text_res = results.Copy(1, 3)
var/list/refs = results.len > 3 ? results.Copy(4) : null
- . = "[text_res.Join("\n")][refs ? "\nRefs: [refs.Join(" ")]" : ""]"
+ return new /datum/tgs_message_content("[text_res.Join("\n")][refs ? "\nRefs: [refs.Join(" ")]" : ""]")
-/datum/tgs_chat_command/reload_admins
- name = "reload_admins"
- help_text = "Forces the server to reload admins."
+/datum/tgs_chat_command/tgsstatus
+ name = "status"
+ help_text = "Gets the admincount, playercount, gamemode, and true game mode of the server"
admin_only = TRUE
-/datum/tgs_chat_command/reload_admins/Run(datum/tgs_chat_user/sender, params)
- ReloadAsync()
- log_admin("[sender.friendly_name] reloaded admins via chat command.")
- return "Admins reloaded."
+/datum/tgs_chat_command/tgsstatus/Run(datum/tgs_chat_user/sender, params)
+ var/list/adm = get_admin_counts()
+ var/list/allmins = adm["total"]
+ var/status = "Admins: [allmins.len] (Active: [english_list(adm["present"])] AFK: [english_list(adm["afk"])] Stealth: [english_list(adm["stealth"])] Skipped: [english_list(adm["noflags"])]). "
+ status += "Players: [GLOB.clients.len] (Active: [get_active_player_count(FALSE, TRUE, FALSE)]). Round has [SSticker.HasRoundStarted() ? "" : "not "]started."
+ return new /datum/tgs_message_content(status)
-/datum/tgs_chat_command/reload_admins/proc/ReloadAsync()
- set waitfor = FALSE
- load_admins()
+// code/modules/discord/tgs_commands.dm
+/datum/tgs_chat_command/tgscheck
+ name = "check"
+ help_text = "Gets the playercount, gamemode, and address of the server"
+
+/datum/tgs_chat_command/tgscheck/Run(datum/tgs_chat_user/sender, params)
+ var/server = config_legacy.serverurl || config_legacy.server
+ return new /datum/tgs_message_content("[GLOB.round_id ? "Round #[GLOB.round_id]: " : ""][GLOB.clients.len] players on [(LEGACY_MAP_DATUM).name]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]")
+
+/datum/tgs_chat_command/gameversion
+ name = "gameversion"
+ help_text = "Gets the version details from the show-server-revision verb, basically"
+
+/datum/tgs_chat_command/gameversion/Run(datum/tgs_chat_user/sender, params)
+ var/list/msg = list("")
+ msg += "BYOND Server Version: [world.byond_version].[world.byond_build] (Compiled with: [DM_VERSION].[DM_BUILD])\n"
+
+ if (!GLOB.revdata)
+ msg += "No revision information found."
+ else
+ msg += "Revision [copytext_char(GLOB.revdata.commit, 1, 9)]"
+ if (GLOB.revdata.date)
+ msg += " compiled on '[GLOB.revdata.date]'"
+
+ if(GLOB.revdata.originmastercommit)
+ msg += ", from origin commit: <[CONFIG_GET(string/githuburl)]/commit/[GLOB.revdata.originmastercommit]>"
+
+ if(GLOB.revdata.testmerge.len)
+ msg += "\n"
+ for(var/datum/tgs_revision_information/test_merge/PR as anything in GLOB.revdata.testmerge)
+ msg += "PR #[PR.number] at [copytext_char(PR.head_commit, 1, 9)] [PR.title].\n"
+ if (PR.url)
+ msg += "<[PR.url]>\n"
+ return new /datum/tgs_message_content(msg.Join(""))
+
+GLOBAL_LIST(round_end_notifiees)
+/datum/tgs_chat_command/endnotify
+ name = "notify"
+ help_text = "Pings the invoker when the round ends"
+ admin_only = FALSE
+
+/datum/tgs_chat_command/endnotify/Run(datum/tgs_chat_user/sender, params)
+ //if(!SSticker.IsRoundInProgress() && SSticker.HasRoundStarted())
+ if(SSticker.current_state == GAME_STATE_FINISHED)
+ return new /datum/tgs_message_content("[sender.mention], the round has already ended!")
+ LAZYINITLIST(GLOB.round_end_notifiees)
+ if (GLOB.round_end_notifiees[sender.mention])
+ GLOB.round_end_notifiees[sender.mention] = FALSE
+ return new /datum/tgs_message_content("You will no longer be notified when the server restarts")
+ GLOB.round_end_notifiees[sender.mention] = TRUE
+ return new /datum/tgs_message_content("You will now be notified when the server restarts")
/datum/tgs_chat_command/whitelist
name = "whitelist"
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 0f0ace2e845..11ffa27882c 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -613,7 +613,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick/ahelp)
msg2 = replacetext(replacetext(msg2, "\proper", ""), "\improper", "")
world.TgsTargetedChatBroadcast("[msg] | [msg2]", TRUE)
-/proc/ircadminwho()
+/proc/tgsadminwho()
var/list/message = list("Admins: ")
var/list/admin_keys = list()
for(var/adm in GLOB.admins)
@@ -621,7 +621,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick/ahelp)
admin_keys += "[C][C.holder.fakekey ? "(Stealth)" : ""][C.is_afk() ? "(AFK)" : ""]"
for(var/admin in admin_keys)
- if(LAZYLEN(admin_keys) > 1)
+ if(LAZYLEN(message) > 1)
message += ", [admin]"
else
message += "[admin]"