From d61c037a35e592cd7361a47c3aa79df13c1380c2 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 1 Dec 2018 18:33:20 -0500 Subject: [PATCH] Leverage the TGS4 chat tagging system for the new game message (#41697) Allows specifying the tag to send the game announce message to on TGS4 Needed by downstreams --- code/__DEFINES/tgs.dm | 26 +++- code/__HELPERS/unsorted.dm | 23 +++ .../configuration/entries/general.dm | 7 + code/controllers/subsystem/ticker.dm | 3 +- code/modules/tgs/core/core.dm | 77 +++++----- code/modules/tgs/core/datum.dm | 5 + code/modules/tgs/core/tgs_version.dm | 22 +++ code/modules/tgs/includes.dm | 1 + code/modules/tgs/v4/commands.dm | 138 +++++++++--------- config/config.txt | 12 +- tgstation.dme | 1 + 11 files changed, 196 insertions(+), 119 deletions(-) create mode 100644 code/modules/tgs/core/tgs_version.dm diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm index d104809d8e91..45a7e46ce3b6 100644 --- a/code/__DEFINES/tgs.dm +++ b/code/__DEFINES/tgs.dm @@ -107,6 +107,22 @@ var/commit //full sha of compiled commit var/origin_commit //full sha of last known remote commit. This may be null if the TGS repository is not currently tracking a remote branch +//represents a version of tgstation-server +/datum/tgs_version + var/suite //The suite version, can be >=3 + + //this group of variables can be null to represent a wild card + var/major //The major version + var/minor //The minor version + var/patch //The patch version + + var/raw_parameter //The unparsed parameter + var/deprefixed_parameter //The version only bit of raw_parameter + +//if the tgs_version is a wildcard version +/datum/tgs_version/proc/Wildcard() + return + //represents a merge of a GitHub pull request /datum/tgs_revision_information/test_merge var/number //pull request number @@ -155,22 +171,22 @@ //FUNCTIONS -//Returns the respective string version of the API +//Returns the respective supported /datum/tgs_version of the API /world/proc/TgsMaximumAPIVersion() return /world/proc/TgsMinimumAPIVersion() return -//Gets the current version of the server tools running the server -/world/proc/TgsVersion() - return - //Returns TRUE if the world was launched under the server tools and the API matches, FALSE otherwise //No function below this succeeds if it returns FALSE /world/proc/TgsAvailable() return +//Gets the current /datum/tgs_version of the server tools running the server +/world/proc/TgsVersion() + return + /world/proc/TgsInstanceName() return diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index b382246f8d4e..f37269a1024b 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1535,3 +1535,26 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) for(var/each_item in items_list) for(var/i in 1 to items_list[each_item]) new each_item(where_to) + +//sends a message to chat +//config_setting should be one of the following +//null - noop +//empty string - use TgsTargetBroadcast with admin_only = FALSE +//other string - use TgsChatBroadcast with the tag that matches config_setting, only works with TGS4, if using TGS3 the above method is used +/proc/send2chat(message, config_setting) + if(config_setting == null || !world.TgsAvailable()) + return + + var/datum/tgs_version/version = world.TgsVersion() + if(config_setting == "" || version.suite == 3) + world.TgsTargetedChatBroadcast(message, FALSE) + return + + var/list/channels_to_use = list() + for(var/I in world.TgsChatChannelInfo()) + var/datum/tgs_chat_channel/channel = I + if(channel.tag == config_setting) + channels_to_use += channel + + if(channels_to_use.len) + world.TgsChatBroadcast() diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index acfa9e7e5510..f2df7787fb49 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -390,6 +390,13 @@ integer = FALSE /datum/config_entry/flag/irc_announce_new_game + deprecated_by = /datum/config_entry/string/chat_announce_new_game + +/datum/config_entry/flag/irc_announce_new_game/DeprecationUpdate(value) + return "" //default broadcast + +/datum/config_entry/string/chat_announce_new_game + config_entry_value = null /datum/config_entry/flag/debug_admin_hrefs diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 10b3e57e020a..1d26ae5efdaf 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -136,8 +136,7 @@ SUBSYSTEM_DEF(ticker) for(var/client/C in GLOB.clients) window_flash(C, ignorepref = TRUE) //let them know lobby has opened up. to_chat(world, "Welcome to [station_name()]!") - if(CONFIG_GET(flag/irc_announce_new_game)) - world.TgsTargetedChatBroadcast("New round starting on [SSmapping.config.map_name]!", FALSE) + send2chat("New round starting on [SSmapping.config.map_name]!", CONFIG_GET(string/chat_announce_new_game)) current_state = GAME_STATE_PREGAME //Everyone who wants to be an observer is now spawned create_observers() diff --git a/code/modules/tgs/core/core.dm b/code/modules/tgs/core/core.dm index 70f012b91333..c1b5c571227e 100644 --- a/code/modules/tgs/core/core.dm +++ b/code/modules/tgs/core/core.dm @@ -1,24 +1,46 @@ /world/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE) var/current_api = TGS_READ_GLOBAL(tgs) if(current_api) - TGS_ERROR_LOG("TgsNew(): TGS API datum already set ([current_api])!") + TGS_ERROR_LOG("TgsNew(): TGS API datum already set ([current_api])! Was TgsNew() called more than once?") return #ifdef TGS_V3_API minimum_required_security_level = TGS_SECURITY_TRUSTED #endif - - var/tgs_version = world.params[TGS_VERSION_PARAMETER] - if(!tgs_version) + var/raw_parameter = world.params[TGS_VERSION_PARAMETER] + if(!raw_parameter) return - var/path = SelectTgsApi(tgs_version) - if(!path) - TGS_ERROR_LOG("Found unsupported API version: [tgs_version]. If this is a valid version please report this, backporting is done on demand.") + var/datum/tgs_version/version = new(raw_parameter) + if(!version.Valid(FALSE)) + TGS_ERROR_LOG("Failed to validate TGS version parameter: [raw_parameter]!") return - TGS_INFO_LOG("Activating API for version [tgs_version]") - var/datum/tgs_api/new_api = new path + var/api_datum + switch(version.suite) + if(3) +#ifndef TGS_V3_API + TGS_ERROR_LOG("Detected V3 API but TGS_V3_API isn't defined!") +#else + switch(version.major) + if(2) + api_datum = /datum/tgs_api/v3210 +#endif + if(4) + switch(version.major) + if(0) + api_datum = /datum/tgs_api/v4 + + if(version.suite != null && version.major != null && version.minor != null && version.patch != null && version.deprefixed_parameter > TgsMaximumAPIVersion()) + TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.") + api_datum = /datum/tgs_api/latest + + if(!api_datum) + TGS_ERROR_LOG("Found unsupported API version: [raw_parameter]. If this is a valid version please report this, backporting is done on demand.") + return + + TGS_INFO_LOG("Activating API for version [version.deprefixed_parameter]") + var/datum/tgs_api/new_api = new api_datum(version) TGS_WRITE_GLOBAL(tgs, new_api) @@ -27,40 +49,11 @@ TGS_WRITE_GLOBAL(tgs, null) TGS_ERROR_LOG("Failed to activate API!") -/world/proc/SelectTgsApi(tgs_version) - //remove the old 3.0 header - tgs_version = replacetext(tgs_version, "/tg/station 13 Server v", "") - - var/list/version_bits = splittext(tgs_version, ".") - - var/super = text2num(version_bits[1]) - var/major = text2num(version_bits[2]) - var/minor = text2num(version_bits[3]) - var/patch = text2num(version_bits[4]) - - switch(super) - if(3) -#ifndef TGS_V3_API - TGS_ERROR_LOG("Detected V3 API but TGS_V3_API isn't defined!") -#else - switch(major) - if(2) - return /datum/tgs_api/v3210 -#endif - if(4) - switch(major) - if(0) - return /datum/tgs_api/v4 - - if(super != null && major != null && minor != null && patch != null && tgs_version > TgsMaximumAPIVersion()) - TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.") - return /datum/tgs_api/latest - /world/TgsMaximumAPIVersion() - return "4.0.0.0" + return new /datum/tgs_version("4.0.x.x") /world/TgsMinimumAPIVersion() - return "3.2.0.0" + return new /datum/tgs_version("3.2.0.0") /world/TgsInitializationComplete() var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) @@ -90,7 +83,9 @@ return TGS_READ_GLOBAL(tgs) != null /world/TgsVersion() - return world.params[TGS_VERSION_PARAMETER] + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + return api.version /world/TgsInstanceName() var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) diff --git a/code/modules/tgs/core/datum.dm b/code/modules/tgs/core/datum.dm index 17e2e8422122..dc46ac6aac90 100644 --- a/code/modules/tgs/core/datum.dm +++ b/code/modules/tgs/core/datum.dm @@ -1,6 +1,11 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null) /datum/tgs_api + var/datum/tgs_version/version + +/datum/tgs_api/New(datum/tgs_version/version) + . = ..() + src.version = version /datum/tgs_api/latest parent_type = /datum/tgs_api/v4 diff --git a/code/modules/tgs/core/tgs_version.dm b/code/modules/tgs/core/tgs_version.dm new file mode 100644 index 000000000000..6b3aff677d51 --- /dev/null +++ b/code/modules/tgs/core/tgs_version.dm @@ -0,0 +1,22 @@ +/datum/tgs_version/New(raw_parameter) + src.raw_parameter = raw_parameter + deprefixed_parameter = replacetext(raw_parameter, "/tg/station 13 Server v", "") + var/list/version_bits = splittext(deprefixed_parameter, ".") + + suite = text2num(version_bits[1]) + if(version_bits.len > 1) + major = text2num(version_bits[2]) + if(version_bits.len > 2) + minor = text2num(version_bits[3]) + if(version_bits.len == 4) + patch = text2num(version_bits[4]) + +/datum/tgs_version/proc/Valid(allow_wildcards = FALSE) + if(suite == null) + return FALSE + if(allow_wildcards) + return TRUE + return !Wildcard() + +/datum/tgs_version/Wildcard() + return major == null || minor == null || patch == null diff --git a/code/modules/tgs/includes.dm b/code/modules/tgs/includes.dm index e5417e98274a..b67ecc78de72 100644 --- a/code/modules/tgs/includes.dm +++ b/code/modules/tgs/includes.dm @@ -1,6 +1,7 @@ #include "core\_definitions.dm" #include "core\core.dm" #include "core\datum.dm" +#include "core\tgs_version.dm" #ifdef TGS_V3_API #include "v3210\api.dm" #include "v3210\commands.dm" diff --git a/code/modules/tgs/v4/commands.dm b/code/modules/tgs/v4/commands.dm index 1d9951bc0411..1e19119ecc52 100644 --- a/code/modules/tgs/v4/commands.dm +++ b/code/modules/tgs/v4/commands.dm @@ -1,69 +1,69 @@ -/datum/tgs_api/v4/proc/ListCustomCommands() - var/results = list() - custom_commands = list() - for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command) - var/datum/tgs_chat_command/stc = new I - var/command_name = stc.name - if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\"")) - TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!") - continue - - if(results[command_name]) - var/datum/other = custom_commands[command_name] - TGS_ERROR_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!") - continue - results += list(list("name" = command_name, "help_text" = stc.help_text, "admin_only" = stc.admin_only)) - custom_commands[command_name] = stc - - var/commands_file = chat_commands_json_path - if(!commands_file) - return - text2file(json_encode(results), commands_file) - -/datum/tgs_api/v4/proc/HandleCustomCommand(command_json) - var/list/data = json_decode(command_json) - var/command = data["command"] - var/user = data["user"] - var/params = data["params"] - - var/datum/tgs_chat_user/u = new - u.id = user["id"] - u.friendly_name = user["friendlyName"] - u.mention = user["mention"] - u.channel = DecodeChannel(user["channel"]) - - var/datum/tgs_chat_command/sc = custom_commands[command] - if(sc) - var/result = sc.Run(u, params) - if(result == null) - result = "" - return result - return "Unknown command: [command]!" - -/* - -The MIT License - -Copyright (c) 2017 Jordan Brown - -Permission is hereby granted, free of charge, -to any person obtaining a copy of this software and -associated documentation files (the "Software"), to -deal in the Software without restriction, including -without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom -the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR -ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ +/datum/tgs_api/v4/proc/ListCustomCommands() + var/results = list() + custom_commands = list() + for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command) + var/datum/tgs_chat_command/stc = new I + var/command_name = stc.name + if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\"")) + TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!") + continue + + if(results[command_name]) + var/datum/other = custom_commands[command_name] + TGS_ERROR_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!") + continue + results += list(list("name" = command_name, "help_text" = stc.help_text, "admin_only" = stc.admin_only)) + custom_commands[command_name] = stc + + var/commands_file = chat_commands_json_path + if(!commands_file) + return + text2file(json_encode(results), commands_file) + +/datum/tgs_api/v4/proc/HandleCustomCommand(command_json) + var/list/data = json_decode(command_json) + var/command = data["command"] + var/user = data["user"] + var/params = data["params"] + + var/datum/tgs_chat_user/u = new + u.id = user["id"] + u.friendly_name = user["friendlyName"] + u.mention = user["mention"] + u.channel = DecodeChannel(user["channel"]) + + var/datum/tgs_chat_command/sc = custom_commands[command] + if(sc) + var/result = sc.Run(u, params) + if(result == null) + result = "" + return result + return "Unknown command: [command]!" + +/* + +The MIT License + +Copyright (c) 2017 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/config/config.txt b/config/config.txt index f06f50a5c85c..be35aa42263e 100644 --- a/config/config.txt +++ b/config/config.txt @@ -423,8 +423,16 @@ MINUTE_CLICK_LIMIT 400 ##How long to wait between messaging admins about occurences of a unique error #ERROR_MSG_DELAY 50 -## Send a message to IRC when starting a new game -#IRC_ANNOUNCE_NEW_GAME + +## Chat Announce Options +## Various messages to be sent to game chats +## Uncommenting these will enable them, by default they will be broadcast to Game chat channels on TGS3 or non-admin channels on TGS4 +## If using TGS4, the string option can be set as a chat channel tag to limit the message to channels of that tag type (case-sensitive) +## i.e. CHAT_ANNOUNCE_NEW_GAME chat_channel_tag + +## Send a message with the station name starting a new game +#CHAT_ANNOUNCE_NEW_GAME + ## Allow admin hrefs that don't use the new token system, will eventually be removed DEBUG_ADMIN_HREFS diff --git a/tgstation.dme b/tgstation.dme index e66974cfd433..e24a38094e9c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2647,6 +2647,7 @@ #include "code\modules\surgery\organs\vocal_cords.dm" #include "code\modules\tgs\event_handler.dm" #include "code\modules\tgs\includes.dm" +#include "code\modules\tgs\core\tgs_version.dm" #include "code\modules\tgui\external.dm" #include "code\modules\tgui\states.dm" #include "code\modules\tgui\subsystem.dm"