diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm index 3fc4fca12db..0cd4a4f7741 100644 --- a/code/__DEFINES/tgs.dm +++ b/code/__DEFINES/tgs.dm @@ -52,8 +52,9 @@ //EVENT CODES -#define TGS_EVENT_PORT_SWAP -2 //before a port change is about to happen, extra parameter is new port -#define TGS_EVENT_REBOOT_MODE_CHANGE -1 //before a reboot mode change, extras parameters are the current and new reboot mode enums +#define TGS_EVENT_REBOOT_MODE_CHANGE -1 //Before a reboot mode change, extras parameters are the current and new reboot mode enums +#define TGS_EVENT_PORT_SWAP -2 //Before a port change is about to happen, extra parameters is new port +#define TGS_EVENT_INSTANCE_RENAMED -3 //Before the instance is renamed, extra prameter is the new name //See the descriptions for the parameters of these codes here: https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/EventType.cs #define TGS_EVENT_REPO_RESET_ORIGIN 0 @@ -67,9 +68,9 @@ #define TGS_EVENT_COMPILE_START 8 #define TGS_EVENT_COMPILE_CANCELLED 9 #define TGS_EVENT_COMPILE_FAILURE 10 -#define TGS_EVENT_COMPILE_COMPLETE 11 +#define TGS_EVENT_COMPILE_COMPLETE 11 // Note, this event fires before the new .dmb is loaded into the watchdog. Consider using the TGS_EVENT_DEPLOYMENT_COMPLETE instead #define TGS_EVENT_INSTANCE_AUTO_UPDATE_START 12 -#define TGS_EVENT_REPO_MERGE_CONFLICT 13 +#define TGS_EVENT_DEPLOYMENT_COMPLETE 13 //OTHER ENUMS @@ -84,6 +85,7 @@ //REQUIRED HOOKS //Call this somewhere in /world/New() that is always run +//IMPORTANT: This function may sleep! Other TGS functions will not succeed until it completes //event_handler: optional user defined event handler. The default behaviour is to broadcast the event in english to all connected admin channels //minimum_required_security_level: The minimum required security level to run the game in which the DMAPI is integrated /world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE) @@ -127,6 +129,10 @@ /datum/tgs_version/proc/Wildcard() return +//if the tgs_version equals some other_version +/datum/tgs_version/proc/Equals(datum/tgs_version/other_version) + return + //represents a merge of a GitHub pull request /datum/tgs_revision_information/test_merge var/number //pull request number @@ -183,7 +189,7 @@ 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 +//No function below this succeeds if it returns FALSE or if TgsNew() has yet to be called /world/proc/TgsAvailable() return @@ -191,6 +197,10 @@ /world/proc/TgsVersion() return +//Gets the current /datum/tgs_version of the DMAPI being used +/world/proc/TgsApiVersion() + return + //Gets the name of the TGS instance running the game /world/proc/TgsInstanceName() return diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm index fa2496ee3b1..d5cbbec618f 100644 --- a/code/datums/helper_datums/getrev.dm +++ b/code/datums/helper_datums/getrev.dm @@ -5,16 +5,18 @@ var/list/testmerge = list() /datum/getrev/New() + commit = rustg_git_revparse("HEAD") + if(commit) + date = rustg_git_commit_date(commit) + originmastercommit = rustg_git_revparse("origin/master") + +/datum/getrev/proc/load_tgs_info() testmerge = world.TgsTestMerges() var/datum/tgs_revision_information/revinfo = world.TgsRevision() if(revinfo) commit = revinfo.commit originmastercommit = revinfo.origin_commit - else - commit = rustg_git_revparse("HEAD") - if(commit) - date = rustg_git_commit_date(commit) - originmastercommit = rustg_git_revparse("origin/master") + date = rustg_git_commit_date(commit) // goes to DD log and config_error.txt log_world(get_log_message()) @@ -77,7 +79,9 @@ msg += "No commit information" if(world.TgsAvailable()) var/datum/tgs_version/version = world.TgsVersion() - msg += "Server tools version: [version.raw_parameter]" + msg += "TGS version: [version.raw_parameter]" + var/datum/tgs_version/api_version = world.TgsApiVersion() + msg += "DMAPI version: [api_version.raw_parameter]" // Game mode odds msg += "
Current Informational Settings:" diff --git a/code/datums/tgs_event_handler.dm b/code/datums/tgs_event_handler.dm new file mode 100644 index 00000000000..a3324121bf5 --- /dev/null +++ b/code/datums/tgs_event_handler.dm @@ -0,0 +1,20 @@ +/datum/tgs_event_handler/impl/HandleEvent(event_code, ...) + switch(event_code) + if(TGS_EVENT_REBOOT_MODE_CHANGE) + var/list/reboot_mode_lookup = list ("[TGS_REBOOT_MODE_NORMAL]" = "be normal", "[TGS_REBOOT_MODE_SHUTDOWN]" = "shutdown the server", "[TGS_REBOOT_MODE_RESTART]" = "hard restart the server") + var old_reboot_mode = args[2] + var new_reboot_mode = args[3] + message_admins("TGS: Reboot will no longer [reboot_mode_lookup["[old_reboot_mode]"]], it will instead [reboot_mode_lookup["[new_reboot_mode]"]]") + if(TGS_EVENT_PORT_SWAP) + message_admins("TGS: Changing port from [world.port] to [args[2]]") + if(TGS_EVENT_INSTANCE_RENAMED) + message_admins("TGS: Instance renamed to from [world.TgsInstanceName()] to [args[2]]") + if(TGS_EVENT_COMPILE_START) + message_admins("TGS: Deployment started, new game version incoming...") + if(TGS_EVENT_COMPILE_CANCELLED) + message_admins("TGS: Deployment cancelled!") + if(TGS_EVENT_COMPILE_FAILURE) + message_admins("TGS: Deployment failed!") + if(TGS_EVENT_DEPLOYMENT_COMPLETE) + message_admins("TGS: Deployment complete!") + to_chat(world, "Server updated, changes will be applied on the next round...") diff --git a/code/game/world.dm b/code/game/world.dm index 5a9082f6b89..4e290ea9b1f 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -33,14 +33,14 @@ GLOBAL_VAR(restart_counter) SetupExternalRSC() - GLOB.config_error_log = GLOB.world_manifest_log = GLOB.world_pda_log = GLOB.world_job_debug_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = GLOB.world_shuttle_log = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set bl - make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once) - TgsNew(minimum_required_security_level = TGS_SECURITY_TRUSTED) + GLOB.config_error_log = GLOB.world_manifest_log = GLOB.world_pda_log = GLOB.world_job_debug_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = GLOB.world_shuttle_log = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set bl GLOB.revdata = new + InitTgs() + config.Load(params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER]) load_admins() @@ -76,6 +76,10 @@ GLOBAL_VAR(restart_counter) if(TEST_RUN_PARAMETER in params) HandleTestRun() +/world/proc/InitTgs() + TgsNew(new /datum/tgs_event_handler/impl, TGS_SECURITY_TRUSTED) + GLOB.revdata.load_tgs_info() + /world/proc/HandleTestRun() //trigger things to run the whole process Master.sleep_offline_after_initializations = FALSE diff --git a/code/modules/tgs/core/core.dm b/code/modules/tgs/core/core.dm index a2f85cec7e7..20a7ebdad95 100644 --- a/code/modules/tgs/core/core.dm +++ b/code/modules/tgs/core/core.dm @@ -90,6 +90,11 @@ if(api) return api.version +/world/TgsApiVersion() + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + return api.ApiVersion() + /world/TgsInstanceName() var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) if(api) diff --git a/code/modules/tgs/core/tgs_version.dm b/code/modules/tgs/core/tgs_version.dm index aa8531dc730..a5dae1241a3 100644 --- a/code/modules/tgs/core/tgs_version.dm +++ b/code/modules/tgs/core/tgs_version.dm @@ -20,3 +20,9 @@ /datum/tgs_version/Wildcard() return minor == null || patch == null + +/datum/tgs_version/Equals(datum/tgs_version/other_version) + if(!istype(other_version)) + return FALSE + + return suite == other_version.suite && minor == other_version.minor && patch == other_version.patch && deprecated_patch == other_version.deprecated_patch diff --git a/code/modules/tgs/v3210/api.dm b/code/modules/tgs/v3210/api.dm index 583c9d5a0b1..cda46da664c 100644 --- a/code/modules/tgs/v3210/api.dm +++ b/code/modules/tgs/v3210/api.dm @@ -39,7 +39,7 @@ var/warned_custom_commands = FALSE /datum/tgs_api/v3210/ApiVersion() - return "3.2.1.0" + return new /datum/tgs_version("3.2.1.0") /datum/tgs_api/v3210/proc/trim_left(text) for (var/i = 1 to length(text)) diff --git a/code/modules/tgs/v4/api.dm b/code/modules/tgs/v4/api.dm index b3d5407fe0d..c564172991b 100644 --- a/code/modules/tgs/v4/api.dm +++ b/code/modules/tgs/v4/api.dm @@ -35,9 +35,9 @@ var/security_level var/requesting_new_port = FALSE - + var/list/intercepted_message_queue - + var/list/custom_commands var/list/cached_test_merges @@ -49,7 +49,7 @@ var/list/last_interop_response /datum/tgs_api/v4/ApiVersion() - return "4.0.0.0" + return new /datum/tgs_version("4.0.0.0") /datum/tgs_api/v4/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level) json_path = world.params[TGS4_PARAM_INFO_JSON] @@ -185,7 +185,7 @@ data = list() data[TGS4_PARAMETER_COMMAND] = command var/json = json_encode(data) - + while(requesting_new_port && !override_requesting_new_port) sleep(1) @@ -199,7 +199,7 @@ //request a new port export_lock = FALSE var/list/new_port_json = Export(TGS4_COMM_NEW_PORT, list(TGS4_PARAMETER_DATA = "[world.port]"), TRUE) //stringify this on purpose - + if(!new_port_json) TGS_ERROR_LOG("No new port response from server![TGS4_PORT_CRITFAIL_MESSAGE]") del(world) @@ -236,7 +236,7 @@ var/list/result = Export(TGS4_COMM_WORLD_REBOOT) if(!result) return - + //okay so the standard TGS4 proceedure is: right before rebooting change the port to whatever was sent to us in the above json's data parameter var/port = result[TGS4_PARAMETER_DATA] @@ -255,7 +255,7 @@ /datum/tgs_api/v4/TestMerges() return cached_test_merges - + /datum/tgs_api/v4/EndProcess() Export(TGS4_COMM_END_PROCESS) diff --git a/code/modules/tgs/v5/api.dm b/code/modules/tgs/v5/api.dm index f1a5c2dea6f..32f80c0ae4c 100644 --- a/code/modules/tgs/v5/api.dm +++ b/code/modules/tgs/v5/api.dm @@ -18,7 +18,7 @@ var/datum/tgs_event_handler/event_handler /datum/tgs_api/v5/ApiVersion() - return "5.0.0" + return new /datum/tgs_version("5.0.0") /datum/tgs_api/v5/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level) src.event_handler = event_handler @@ -26,7 +26,8 @@ server_port = world.params[DMAPI5_PARAM_SERVER_PORT] access_identifier = world.params[DMAPI5_PARAM_ACCESS_IDENTIFIER] - var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = ApiVersion(), DMAPI5_BRIDGE_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands())) + var/datum/tgs_version/api_version = ApiVersion() + var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_BRIDGE_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands())) if(!istype(bridge_response)) TGS_ERROR_LOG("Failed initial bridge request!") return FALSE @@ -42,6 +43,7 @@ security_level = runtime_information[DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL] instance_name = runtime_information[DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME] + version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION]) var/list/revisionData = runtime_information[DMAPI5_RUNTIME_INFORMATION_REVISION] if(istype(revisionData)) @@ -94,8 +96,7 @@ /datum/tgs_api/v5/proc/TopicResponse(error_message = null) var/list/response = list() - if(error_message) - response[DMAPI5_RESPONSE_ERROR_MESSAGE] = error_message + response[DMAPI5_RESPONSE_ERROR_MESSAGE] = error_message return json_encode(response) @@ -127,14 +128,14 @@ intercepted_message_queue = list() var/list/event_notification = topic_parameters[DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION] if(!istype(event_notification)) - return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION]!") + return TopicResponse("Invalid [DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION]!") var/event_type = event_notification[DMAPI5_EVENT_NOTIFICATION_TYPE] if(!isnum(event_type)) return TopicResponse("Invalid or missing [DMAPI5_EVENT_NOTIFICATION_TYPE]!") var/list/event_parameters = event_notification[DMAPI5_EVENT_NOTIFICATION_PARAMETERS] - if(!istype(event_parameters)) + if(event_parameters && !istype(event_parameters)) return TopicResponse("Invalid or missing [DMAPI5_EVENT_NOTIFICATION_PARAMETERS]!") var/list/event_call = list(event_type) @@ -145,8 +146,7 @@ event_handler.HandleEvent(arglist(event_call)) var/list/response = list() - if(intercepted_message_queue.len) - response[DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES] = intercepted_message_queue + response[DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES] = intercepted_message_queue intercepted_message_queue = null return json_encode(response) if(DMAPI5_TOPIC_COMMAND_CHANGE_PORT) @@ -178,6 +178,9 @@ if(!istext(new_instance_name)) return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME]!") + if(event_handler != null) + event_handler.HandleEvent(TGS_EVENT_INSTANCE_RENAMED, new_instance_name) + instance_name = new_instance_name return TopicResponse() if(DMAPI5_TOPIC_COMMAND_CHAT_CHANNELS_UPDATE) @@ -261,17 +264,19 @@ return revision /datum/tgs_api/v5/ChatBroadcast(message, list/channels) - var/list/ids - if(length(channels)) - ids = list() - for(var/I in channels) - var/datum/tgs_chat_channel/channel = I - ids += channel.id + if(!length(channels)) + channels = ChatChannelInfo() + + var/list/ids = list() + for(var/I in channels) + var/datum/tgs_chat_channel/channel = I + ids += channel.id + message = list(DMAPI5_CHAT_MESSAGE_TEXT = message, DMAPI5_CHAT_MESSAGE_CHANNEL_IDS = ids) if(intercepted_message_queue) intercepted_message_queue += list(message) else - Bridge(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE, message) + Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message)) /datum/tgs_api/v5/ChatTargetedBroadcast(message, admin_only) var/list/channels = list() @@ -283,14 +288,14 @@ if(intercepted_message_queue) intercepted_message_queue += list(message) else - Bridge(TGS4_COMM_CHAT, message) + Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message)) /datum/tgs_api/v5/ChatPrivateMessage(message, datum/tgs_chat_user/user) message = list(DMAPI5_CHAT_MESSAGE_TEXT = message, DMAPI5_CHAT_MESSAGE_CHANNEL_IDS = list(user.channel.id)) if(intercepted_message_queue) intercepted_message_queue += list(message) else - Bridge(TGS4_COMM_CHAT, message) + Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message)) /datum/tgs_api/v5/ChatChannelInfo() return chat_channels diff --git a/code/modules/tgs/v5/commands.dm b/code/modules/tgs/v5/commands.dm index c8e89a79971..2775157656a 100644 --- a/code/modules/tgs/v5/commands.dm +++ b/code/modules/tgs/v5/commands.dm @@ -30,11 +30,13 @@ var/datum/tgs_chat_command/sc = custom_commands[command] if(sc) - var/textResponse = sc.Run(u, params) + var/text_response = sc.Run(u, params) var/list/topic_response = list() - if(textResponse != null) - topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE] = textResponse - return topic_response + if(!istext(text_response)) + TGS_ERROR_LOG("Custom command [command] should return a string! Got: \"[text_response]\"") + text_response = null + topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE] = text_response + return json_encode(topic_response) return TopicResponse("Unknown custom chat command: [command]!") /* diff --git a/tgstation.dme b/tgstation.dme index 8c2650f158d..c558060582e 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -347,6 +347,7 @@ #include "code\datums\shuttles.dm" #include "code\datums\soullink.dm" #include "code\datums\spawners_menu.dm" +#include "code\datums\tgs_event_handler.dm" #include "code\datums\verbs.dm" #include "code\datums\weakrefs.dm" #include "code\datums\world_topic.dm"