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"