diff --git a/code/__DEFINES/server_tools.dm b/code/__DEFINES/server_tools.dm
deleted file mode 100644
index a1764880c3..0000000000
--- a/code/__DEFINES/server_tools.dm
+++ /dev/null
@@ -1,128 +0,0 @@
-// /tg/station 13 server tools API
-#define SERVICE_API_VERSION_STRING "3.2.0.2"
-
-//CONFIGURATION
-//use this define if you want to do configuration outside of this file
-#ifndef SERVER_TOOLS_EXTERNAL_CONFIGURATION
-//Comment this out once you've filled in the below
-#error /tg/station server tools interface unconfigured
-
-//Required interfaces (fill in with your codebase equivalent):
-
-//create a global variable named `Name` and set it to `Value`
-//These globals must not be modifiable from anywhere outside of the server tools
-#define SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(Name, Value)
-//Read the value in the global variable `Name`
-#define SERVER_TOOLS_READ_GLOBAL(Name)
-//Set the value in the global variable `Name` to `Value`
-#define SERVER_TOOLS_WRITE_GLOBAL(Name, Value)
-//display an announcement `message` from the server to all players
-#define SERVER_TOOLS_WORLD_ANNOUNCE(message)
-//Write a string `message` to a server log
-#define SERVER_TOOLS_LOG(message)
-//Notify current in-game administrators of a string `event`
-#define SERVER_TOOLS_NOTIFY_ADMINS(event)
-//The current amount of connected clients
-#define SERVER_TOOLS_CLIENT_COUNT
-#endif
-
-//Required hooks:
-
-//Put this somewhere in /world/New() that is always run
-#define SERVER_TOOLS_ON_NEW ServiceInit()
-//Put this somewhere in /world/Topic(T, Addr, Master, Keys) that is always run before T is modified
-#define SERVER_TOOLS_ON_TOPIC var/service_topic_return = ServiceCommand(params2list(T)); if(service_topic_return) return service_topic_return
-//Put at the beginning of world/Reboot(reason)
-#define SERVER_TOOLS_ON_REBOOT ServiceReboot()
-
-//Optional callable functions:
-
-//Returns the string version of the API
-#define SERVER_TOOLS_API_VERSION ServiceAPIVersion()
-//Returns TRUE if the world was launched under the server tools and the API matches, FALSE otherwise
-//No function below this succeed if this is FALSE
-#define SERVER_TOOLS_PRESENT RunningService()
-//Gets the current version of the service running the server
-#define SERVER_TOOLS_VERSION ServiceVersion()
-//Forces a hard reboot of BYOND by ending the process
-//unlike del(world) clients will try to reconnect
-//If the service has not requested a shutdown, the world will reboot shortly after
-#define SERVER_TOOLS_REBOOT_BYOND world.ServiceEndProcess()
-/*
- Gets the list of any testmerged github pull requests
-
- "[PR Number]" => list(
- "title" -> PR title
- "commit" -> Full hash of commit merged
- "author" -> Github username of the author of the PR
- )
-*/
-#define SERVER_TOOLS_PR_LIST GetTestMerges()
-//Sends a message to connected game chats
-#define SERVER_TOOLS_CHAT_BROADCAST(message) world.ChatBroadcast(message)
-//Sends a message to connected admin chats
-#define SERVER_TOOLS_RELAY_BROADCAST(message) world.AdminBroadcast(message)
-
-//IMPLEMENTATION
-
-#define REBOOT_MODE_NORMAL 0
-#define REBOOT_MODE_HARD 1
-#define REBOOT_MODE_SHUTDOWN 2
-
-#define SERVICE_WORLD_PARAM "server_service"
-#define SERVICE_VERSION_PARAM "server_service_version"
-#define SERVICE_INSTANCE_PARAM "server_instance"
-#define SERVICE_PR_TEST_JSON "prtestjob.json"
-#define SERVICE_INTERFACE_DLL "TGDreamDaemonBridge.dll"
-#define SERVICE_INTERFACE_FUNCTION "DDEntryPoint"
-
-#define SERVICE_CMD_HARD_REBOOT "hard_reboot"
-#define SERVICE_CMD_GRACEFUL_SHUTDOWN "graceful_shutdown"
-#define SERVICE_CMD_WORLD_ANNOUNCE "world_announce"
-#define SERVICE_CMD_LIST_CUSTOM "list_custom_commands"
-#define SERVICE_CMD_API_COMPATIBLE "api_compat"
-#define SERVICE_CMD_PLAYER_COUNT "client_count"
-
-#define SERVICE_CMD_PARAM_KEY "serviceCommsKey"
-#define SERVICE_CMD_PARAM_COMMAND "command"
-#define SERVICE_CMD_PARAM_SENDER "sender"
-#define SERVICE_CMD_PARAM_CUSTOM "custom"
-
-#define SERVICE_JSON_PARAM_HELPTEXT "help_text"
-#define SERVICE_JSON_PARAM_ADMINONLY "admin_only"
-#define SERVICE_JSON_PARAM_REQUIREDPARAMETERS "required_parameters"
-
-#define SERVICE_REQUEST_KILL_PROCESS "killme"
-#define SERVICE_REQUEST_IRC_BROADCAST "irc"
-#define SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE "send2irc"
-#define SERVICE_REQUEST_WORLD_REBOOT "worldreboot"
-#define SERVICE_REQUEST_API_VERSION "api_ver"
-
-#define SERVICE_RETURN_SUCCESS "SUCCESS"
-
-/*
-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/code/__DEFINES/tgs.config.dm b/code/__DEFINES/tgs.config.dm
new file mode 100644
index 0000000000..00cbae7629
--- /dev/null
+++ b/code/__DEFINES/tgs.config.dm
@@ -0,0 +1,19 @@
+#define TGS_EXTERNAL_CONFIGURATION
+#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(##Name, ##Value); GLOBAL_PROTECT(##Name)
+#define TGS_READ_GLOBAL(Name) GLOB.##Name
+#define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value
+#define TGS_WORLD_ANNOUNCE(message) to_chat(world, "[html_encode(##message)]")
+#define TGS_INFO_LOG(message) log_world("TGS: Info: [##message]")
+#define TGS_ERROR_LOG(message) log_world("TGS: Error: [##message]")
+#define TGS_NOTIFY_ADMINS(event) message_admins(##event)
+#define TGS_CLIENT_COUNT GLOB.clients.len
+#define TGS_PROTECT_DATUM(Path)\
+##Path/can_vv_get(var_name){\
+ return FALSE;\
+}\
+##Path/vv_edit_var(var_name, var_value){\
+ return FALSE;\
+}\
+##Path/CanProcCall(procname){\
+ return FALSE;\
+}
diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm
new file mode 100644
index 0000000000..651be685c5
--- /dev/null
+++ b/code/__DEFINES/tgs.dm
@@ -0,0 +1,202 @@
+//tgstation-server DMAPI
+
+//All functions and datums outside this document are subject to change with any version and should not be relied on
+
+//CONFIGURATION
+
+//create this define if you want to do configuration outside of this file
+#ifndef TGS_EXTERNAL_CONFIGURATION
+
+//Comment this out once you've filled in the below
+#error TGS API unconfigured
+
+//Required interfaces (fill in with your codebase equivalent):
+
+//create a global variable named `Name` and set it to `Value`
+//These globals must not be modifiable from anywhere outside of the server tools
+#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value)
+
+//Read the value in the global variable `Name`
+#define TGS_READ_GLOBAL(Name)
+
+//Set the value in the global variable `Name` to `Value`
+#define TGS_WRITE_GLOBAL(Name, Value)
+
+//Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game priveledge escalation
+#define TGS_PROTECT_DATUM(Path)
+
+//display an announcement `message` from the server to all players
+#define TGS_WORLD_ANNOUNCE(message)
+
+//Notify current in-game administrators of a string `event`
+#define TGS_NOTIFY_ADMINS(event)
+
+//Write an info `message` to a server log
+#define TGS_INFO_LOG(message)
+
+//Write an error `message` to a server log
+#define TGS_ERROR_LOG(message)
+
+//Get the number of connected /clients
+#define TGS_CLIENT_COUNT
+
+#endif
+
+//EVENT CODES
+
+//TODO
+
+//REQUIRED HOOKS
+
+//Call this somewhere in /world/New() that is always run
+//event_handler: optional user defined event handler. The default behaviour is to broadcast the event in english to all connected admin channels
+/world/proc/TgsNew(datum/tgs_event_handler/event_handler)
+ return
+
+//Call this when your initializations are complete and your game is ready to play before any player interactions happen
+//This may use world.sleep_offline to make this happen so ensure no changes are made to it while this call is running
+/world/proc/TgsInitializationComplete()
+ return
+
+//Put this somewhere in /world/Topic(T, Addr, Master, Keys) that is always run before T is modified
+#define TGS_TOPIC var/tgs_topic_return = TgsTopic(T); if(tgs_topic_return) return tgs_topic_return
+
+//Call this at the beginning of world/Reboot(reason)
+/world/proc/TgsReboot()
+ return
+
+//DATUM DEFINITIONS
+//unless otherwise specified all datums defined here should be considered read-only, warranty void if written
+
+//represents git revision information about the current world build
+/datum/tgs_revision_information
+ 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 merge of a GitHub pull request
+/datum/tgs_revision_information/test_merge
+ var/number //pull request number
+ var/title //pull request title
+ var/body //pull request body
+ var/author //pull request github author
+ var/url //link to pull request html
+ var/pull_request_commit //commit of the pull request when it was merged
+ var/time_merged //timestamp of when the merge commit for the pull request was created
+ var/comment //optional comment left by the one who initiated the test merge
+
+//represents a connected chat channel
+/datum/tgs_chat_channel
+ var/id //internal channel representation
+ var/friendly_name //user friendly channel name
+ var/server_name //server name the channel resides on
+ var/provider_name //chat provider for the channel
+ var/is_admin_channel //if the server operator has marked this channel for game admins only
+ var/is_private_channel //if this is a private chat channel
+
+//represents a chat user
+/datum/tgs_chat_user
+ var/id //Internal user representation
+ var/friendly_name //The user's public name
+ var/mention //The text to use to ping this user in a message
+ var/datum/tgs_chat_channel/channel //The /datum/tgs_chat_channel this user was from
+
+//user definable callback for handling events
+/datum/tgs_event_handler/proc/HandleEvent(event_code)
+ return
+
+//user definable chat command
+/datum/tgs_chat_command
+ var/name = "" //the string to trigger this command on a chat bot. e.g. TGS3_BOT: do_this_command
+ var/help_text = "" //help text for this command
+ var/admin_only = FALSE //set to TRUE if this command should only be usable by registered chat admins
+
+//override to implement command
+//sender: The tgs_chat_user who send to command
+//params: The trimmed string following the command name
+//The return value will be stringified and sent to the appropriate chat
+/datum/tgs_chat_command/proc/Run(datum/tgs_chat_user/sender, params)
+ CRASH("[type] has no implementation for Run()")
+
+//FUNCTIONS
+
+//Returns the respective string 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
+
+/world/proc/TgsInstanceName()
+ return
+
+//Get the current `/datum/tgs_revision_information`
+/world/proc/TgsRevision()
+ return
+
+//Gets a list of active `/datum/tgs_revision_information/test_merge`s
+/world/proc/TgsTestMerges()
+ return
+
+//Forces a hard reboot of BYOND by ending the process
+//unlike del(world) clients will try to reconnect
+//If the service has not requested a shutdown, the next server will take over
+/world/proc/TgsEndProcess()
+ return
+
+//Gets a list of connected tgs_chat_channel
+/world/proc/TgsChatChannelInfo()
+ return
+
+//Sends a message to connected game chats
+//message: The message to send
+//channels: optional channels to limit the broadcast to
+/world/proc/TgsChatBroadcast(message, list/channels)
+ return
+
+//Send a message to non-admin connected chats
+//message: The message to send
+//admin_only: If TRUE, message will instead be sent to only admin connected chats
+/world/proc/TgsTargetedChatBroadcast(message, admin_only)
+ return
+
+//Send a private message to a specific user
+//message: The message to send
+//user: The /datum/tgs_chat_user to send to
+/world/proc/TgsChatPrivateMessage(message, datum/tgs_chat_user/user)
+ return
+
+/*
+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/code/_globalvars/configuration.dm b/code/_globalvars/configuration.dm
index f580eab3d5..7c5715e1bb 100644
--- a/code/_globalvars/configuration.dm
+++ b/code/_globalvars/configuration.dm
@@ -1,6 +1,6 @@
GLOBAL_REAL(config, /datum/controller/configuration)
-GLOBAL_DATUM_INIT(revdata, /datum/getrev, new)
+GLOBAL_DATUM(revdata, /datum/getrev)
GLOBAL_VAR(host)
GLOBAL_VAR(station_name)
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index 2b1e9bcda9..0cc0c622d7 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -199,11 +199,14 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
// Sort subsystems by display setting for easy access.
sortTim(subsystems, /proc/cmp_subsystem_display)
// Set world options.
- if(sleep_offline_after_initializations)
- world.sleep_offline = TRUE
world.fps = CONFIG_GET(number/fps)
var/initialized_tod = REALTIMEOFDAY
+
+ world.TgsInitializationComplete()
+ if(sleep_offline_after_initializations)
+ world.sleep_offline = TRUE
sleep(1)
+
if(sleep_offline_after_initializations && CONFIG_GET(flag/resume_after_initializations))
world.sleep_offline = FALSE
initializations_finished_with_no_players_logged_in = initialized_tod < REALTIMEOFDAY - 10
diff --git a/code/controllers/subsystem/server_maint.dm b/code/controllers/subsystem/server_maint.dm
index dd68443bd7..dd45f9146f 100644
--- a/code/controllers/subsystem/server_maint.dm
+++ b/code/controllers/subsystem/server_maint.dm
@@ -55,8 +55,8 @@ SUBSYSTEM_DEF(server_maint)
co.ehjax_send(data = "roundrestart")
if(server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite
C << link("byond://[server]")
- if(SERVER_TOOLS_PRESENT)
- SSblackbox.record_feedback("text", "server_tools", 1, SERVER_TOOLS_VERSION)
- SSblackbox.record_feedback("text", "server_tools_api", 1, SERVER_TOOLS_API_VERSION)
+ var/tgsversion = world.TgsVersion()
+ if(tgsversion)
+ SSblackbox.record_feedback("text", "server_tools", 1, tgsversion)
#undef PING_BUFFER_TIME
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 270970c61b..0471afceea 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -158,7 +158,7 @@ SUBSYSTEM_DEF(ticker)
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))
- SERVER_TOOLS_CHAT_BROADCAST("New round starting on [SSmapping.config.map_name]!")
+ world.TgsTargetedChatBroadcast("New round starting on [SSmapping.config.map_name]!", FALSE)
current_state = GAME_STATE_PREGAME
//Everyone who wants to be an observer is now spawned
create_observers()
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index c6c0f2353a..59953a4ce1 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -1,3 +1,6 @@
+/datum/proc/CanProcCall(procname)
+ return TRUE
+
/datum/proc/can_vv_get(var_name)
return TRUE
diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm
index a9fa7e4e10..b459b31fcb 100644
--- a/code/datums/helper_datums/getrev.dm
+++ b/code/datums/helper_datums/getrev.dm
@@ -5,7 +5,7 @@
var/date
/datum/getrev/New()
- testmerge = SERVER_TOOLS_PR_LIST
+ testmerge = world.TgsTestMerges()
log_world("Running /tg/ revision:")
var/list/logs = world.file2list(".git/logs/HEAD")
if(logs)
@@ -21,7 +21,8 @@
log_world(commit)
for(var/line in testmerge)
if(line)
- var/tmcommit = testmerge[line]["commit"]
+ var/datum/tgs_revision_information/test_merge/tm = line
+ var/tmcommit = tm.commit
log_world("Test merge active of PR #[line] commit [tmcommit]")
SSblackbox.record_feedback("nested tally", "testmerged_prs", 1, list("[line]", "[tmcommit]"))
if(originmastercommit)
@@ -34,11 +35,12 @@
return ""
. = header ? "The following pull requests are currently test merged:
" : ""
for(var/line in testmerge)
- var/cm = testmerge[line]["commit"]
- var/details = ": '" + html_encode(testmerge[line]["title"]) + "' by " + html_encode(testmerge[line]["author"]) + " at commit " + html_encode(copytext(cm, 1, min(length(cm), 11)))
+ var/datum/tgs_revision_information/test_merge/tm = line
+ var/cm = tm.pull_request_commit
+ var/details = ": '" + html_encode(tm.title) + "' by " + html_encode(tm.author) + " at commit " + html_encode(copytext(cm, 1, min(length(cm), 11)))
if(details && findtext(details, "\[s\]") && (!usr || !usr.client.holder))
continue
- . += "#[line][details]
"
+ . += "#[tm.number][details]
"
/client/verb/showrevinfo()
set category = "OOC"
@@ -58,9 +60,8 @@
else
to_chat(src, "Master revision unknown")
to_chat(src, "Revision: [GLOB.revdata.commit]")
- if(SERVER_TOOLS_PRESENT)
- to_chat(src, "Server tools version: [SERVER_TOOLS_VERSION]")
- to_chat(src, "Server tools API version: [SERVER_TOOLS_API_VERSION]")
+ if(world.TgsAvailable())
+ to_chat(src, "Server tools version: [world.TgsVersion()]")
to_chat(src, "Current Informational Settings:")
to_chat(src, "Protect Authority Roles From Traitor: [CONFIG_GET(flag/protect_roles_from_antagonist)]")
to_chat(src, "Protect Assistant Role From Traitor: [CONFIG_GET(flag/protect_assistant_from_antagonist)]")
diff --git a/code/datums/world_topic.dm b/code/datums/world_topic.dm
index ec00351b52..9cbccb8fe9 100644
--- a/code/datums/world_topic.dm
+++ b/code/datums/world_topic.dm
@@ -120,8 +120,12 @@
require_comms_key = TRUE
/datum/world_topic/namecheck/Run(list/input)
- var/datum/server_tools_command/namecheck/NC = new
- return NC.Run(input["sender"], input["namecheck"])
+ //Oh this is a hack, someone refactor the functionality out of the chat command PLS
+ var/datum/tgs_chat_command/namecheck/NC = new
+ var/datum/tgs_chat_user/user = new
+ user.friendly_name = input["sender"]
+ user.mention = user.friendly_name
+ return NC.Run(user, input["namecheck"])
/datum/world_topic/adminwho
keyword = "adminwho"
diff --git a/code/game/world.dm b/code/game/world.dm
index cc4246a7c8..bf4af9dcc3 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -17,6 +17,10 @@ GLOBAL_PROTECT(security_mode)
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
+ TgsNew()
+
+ GLOB.revdata = new
+
config.Load()
//SetupLogs depends on the RoundID, so lets check
@@ -25,8 +29,6 @@ GLOBAL_PROTECT(security_mode)
SSdbcore.SetRoundID()
SetupLogs()
- SERVER_TOOLS_ON_NEW
-
load_admins()
LoadVerbs(/datum/verbs/menu)
if(CONFIG_GET(flag/usewhitelist))
@@ -129,7 +131,7 @@ GLOBAL_PROTECT(security_mode)
warning("/tg/station 13 uses many file operations, a few shell()s, and some external call()s. Trusted mode is recommended. You can download our source code for your own browsing and compilation at https://github.com/tgstation/tgstation")
/world/Topic(T, addr, master, key)
- SERVER_TOOLS_ON_TOPIC //redirect to server tools if necessary
+ TGS_TOPIC //redirect to server tools if necessary
var/static/list/topic_handlers = TopicHandlers()
@@ -185,7 +187,7 @@ GLOBAL_PROTECT(security_mode)
qdel(src) //shut it down
/world/Reboot(reason = 0, fast_track = FALSE)
- SERVER_TOOLS_ON_REBOOT
+ TgsReboot()
if (reason || fast_track) //special reboot, do none of the normal stuff
if (usr)
log_admin("[key_name(usr)] Has requested an immediate world restart via client side debugging tools")
@@ -199,7 +201,7 @@ GLOBAL_PROTECT(security_mode)
FinishTestRun()
return
- if(SERVER_TOOLS_PRESENT)
+ if(TgsAvailable())
var/do_hard_reboot
// check the hard reboot counter
var/ruhr = CONFIG_GET(number/rounds_until_hard_restart)
@@ -218,7 +220,7 @@ GLOBAL_PROTECT(security_mode)
if(do_hard_reboot)
log_world("World hard rebooted at [time_stamp()]")
shutdown_logging() // See comment below.
- SERVER_TOOLS_REBOOT_BYOND
+ TgsEndProcess()
log_world("World rebooted at [time_stamp()]")
shutdown_logging() // Past this point, no logging procs can be used, at risk of data loss.
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 577cca303b..d63b36e6e7 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -434,7 +434,7 @@
return
var/list/options = list("Regular Restart", "Hard Restart (No Delay/Feeback Reason)", "Hardest Restart (No actions, just reboot)")
- if(SERVER_TOOLS_PRESENT)
+ if(world.TgsAvailable())
options += "Server Restart (Kill and restart DD)";
var/rebootconfirm
@@ -459,7 +459,7 @@
world.Reboot(fast_track = TRUE)
if("Server Restart (Kill and restart DD)")
to_chat(world, "Server restart - [init_by]")
- SERVER_TOOLS_REBOOT_BYOND
+ world.TgsEndProcess()
/datum/admins/proc/end_round()
set category = "Server"
diff --git a/code/modules/admin/chat_commands.dm b/code/modules/admin/chat_commands.dm
index 02402956d9..dbeb6fce7f 100644
--- a/code/modules/admin/chat_commands.dm
+++ b/code/modules/admin/chat_commands.dm
@@ -1,12 +1,12 @@
#define IRC_STATUS_THROTTLE 5
-/datum/server_tools_command/ircstatus
+/datum/tgs_chat_command/ircstatus
name = "status"
help_text = "Gets the admincount, playercount, gamemode, and true game mode of the server"
admin_only = TRUE
- var/static/last_irc_status = 0
+ var/last_irc_status = 0
-/datum/server_tools_command/ircstatus/Run(sender, params)
+/datum/tgs_chat_command/ircstatus/Run(datum/tgs_chat_user/sender, params)
var/rtod = REALTIMEOFDAY
if(rtod - last_irc_status < IRC_STATUS_THROTTLE)
return
@@ -17,12 +17,12 @@
status += "Players: [GLOB.clients.len] (Active: [get_active_player_count(0,1,0)]). Mode: [SSticker.mode ? SSticker.mode.name : "Not started"]."
return status
-/datum/server_tools_command/irccheck
+/datum/tgs_chat_command/irccheck
name = "check"
help_text = "Gets the playercount, gamemode, and address of the server"
- var/static/last_irc_check = 0
+ var/last_irc_check = 0
-/datum/server_tools_command/irccheck/Run(sender, params)
+/datum/tgs_chat_command/irccheck/Run(datum/tgs_chat_user/sender, params)
var/rtod = REALTIMEOFDAY
if(rtod - last_irc_check < IRC_STATUS_THROTTLE)
return
@@ -30,14 +30,15 @@
var/server = CONFIG_GET(string/server)
return "[GLOB.round_id ? "Round #[GLOB.round_id]: " : ""][GLOB.clients.len] players on [SSmapping.config.map_name]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]" //CIT CHANGE - obfuscates the current gamemode from players
-/datum/server_tools_command/ahelp
+/datum/tgs_chat_command/ahelp
name = "ahelp"
help_text = " |list>>"
- required_parameters = 2
admin_only = TRUE
-/datum/server_tools_command/ahelp/Run(sender, params)
+/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"
var/target = all_params[1]
all_params.Cut(1, 2)
var/id = text2num(target)
@@ -47,52 +48,54 @@
target = AH.initiator_ckey
else
return "Ticket #[id] not found!"
- var/res = IrcPm(target, all_params.Join(" "), sender)
+ var/res = IrcPm(target, all_params.Join(" "), sender.friendly_name)
if(res != "Message Successful")
return res
-/datum/server_tools_command/namecheck
+/datum/tgs_chat_command/namecheck
name = "namecheck"
help_text = "Returns info on the specified target"
- required_parameters = 1
admin_only = TRUE
-/datum/server_tools_command/namecheck/Run(sender, params)
- log_admin("Chat Name Check: [sender] on [params]")
- message_admins("Name checking [params] from [sender]")
+/datum/tgs_chat_command/namecheck/Run(datum/tgs_chat_user/sender, params)
+ params = trim(params)
+ if(!params)
+ return "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)
-/datum/server_tools_command/adminwho
+/datum/tgs_chat_command/adminwho
name = "adminwho"
help_text = "Lists administrators currently on the server"
admin_only = TRUE
-/datum/server_tools_command/adminwho/Run(sender, params)
+/datum/tgs_chat_command/adminwho/Run(datum/tgs_chat_user/sender, params)
return ircadminwho()
GLOBAL_LIST(round_end_notifiees)
-/datum/server_tools_command/notify
+/datum/tgs_chat_command/notify
name = "notify"
help_text = "Pings the invoker when the round ends"
admin_only = TRUE
-/datum/server_tools_command/notify/Run(sender, params)
+/datum/tgs_chat_command/notify/Run(datum/tgs_chat_user/sender, params)
if(!SSticker.IsRoundInProgress() && SSticker.HasRoundStarted())
- return "[sender], the round has already ended!"
+ return "[sender.mention], the round has already ended!"
LAZYINITLIST(GLOB.round_end_notifiees)
GLOB.round_end_notifiees[sender] = TRUE
- return "I will notify [sender] when the round ends."
+ return "I will notify [sender.mention] when the round ends."
-/datum/server_tools_command/sdql
+/datum/tgs_chat_command/sdql
name = "sdql"
help_text = "Runs an SDQL query"
admin_only = TRUE
-/datum/server_tools_command/sdql/Run(sender, params)
+/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."
- GLOB.AdminProcCaller = "CHAT_[sender]" //_ won't show up in ckeys so it'll never match with a real admin
+ 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)
@@ -100,13 +103,13 @@ GLOBAL_LIST(round_end_notifiees)
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(" ")]" : ""]"
-
-/datum/server_tools_command/reload_admins
+
+/datum/tgs_chat_command/reload_admins
name = "reload_admins"
help_text = "Forces the server to reload admins."
admin_only = TRUE
-/datum/server_tools_command/reload_admins/Run(sender, params)
+/datum/tgs_chat_command/reload_admins/Run(datum/tgs_chat_user/sender, params)
load_admins()
- log_admin("[sender] reloaded admins via chat command.")
+ log_admin("[sender.friendly_name] reloaded admins via chat command.")
return "Admins reloaded."
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 2a6b4448d2..f385cdc1bd 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -594,7 +594,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
/proc/send2irc(msg,msg2)
msg = replacetext(replacetext(msg, "\proper", ""), "\improper", "")
msg2 = replacetext(replacetext(msg2, "\proper", ""), "\improper", "")
- SERVER_TOOLS_RELAY_BROADCAST("[msg] | [msg2]")
+ world.TgsTargetedChatBroadcast("[msg] | [msg2]", TRUE)
/proc/send2otherserver(source,msg,type = "Ahelp")
var/comms_key = CONFIG_GET(string/comms_key)
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 7d4d5fa05e..68854b7978 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -110,10 +110,14 @@ GLOBAL_PROTECT(LastAdminCalledProc)
GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention)
GLOBAL_PROTECT(AdminProcCallSpamPrevention)
-/proc/WrapAdminProcCall(target, procname, list/arguments)
+/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
if(target && procname == "Del")
to_chat(usr, "Calling Del() is not allowed")
return
+
+ if(!target.CanProcCall(procname))
+ to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!")
+ return
var/current_caller = GLOB.AdminProcCaller
var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller
if(!ckey)
@@ -137,7 +141,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
GLOB.AdminProcCaller = null
//adv proc call this, ya nerds
-/world/proc/WrapAdminProcCall(target, procname, list/arguments)
+/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
if(target == GLOBAL_PROC)
return call(procname)(arglist(arguments))
else if(target != world)
diff --git a/code/modules/server_tools/st_commands.dm b/code/modules/server_tools/st_commands.dm
deleted file mode 100644
index 1e071550e0..0000000000
--- a/code/modules/server_tools/st_commands.dm
+++ /dev/null
@@ -1,76 +0,0 @@
-/datum/server_tools_command
- var/name = "" //the string to trigger this command on a chat bot. e.g. TGS3_BOT: do_this_command
- var/help_text = "" //help text for this command
- var/required_parameters = 0 //number of parameters required for this command
- var/admin_only = FALSE //set to TRUE if this command should only be usable by registered chat admins
-
-//override to implement command
-//sender is the display name of who sent the command
-//params is the trimmed string following the command name
-/datum/server_tools_command/proc/Run(sender, params)
- CRASH("[type] has no implementation for Run()")
-
-/world/proc/ListServiceCustomCommands(warnings_only)
- if(!warnings_only)
- . = list()
- var/list/command_name_types = list()
- var/list/warned_command_names = warnings_only ? list() : null
- for(var/I in typesof(/datum/server_tools_command) - /datum/server_tools_command)
- var/datum/server_tools_command/stc = I
- var/command_name = initial(stc.name)
- var/static/list/warned_server_tools_names = list()
- if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
- if(warnings_only && !warned_command_names[command_name])
- SERVER_TOOLS_LOG("WARNING: Custom command [command_name] can't be used as it is empty or contains illegal characters!")
- warned_command_names[command_name] = TRUE
- continue
-
- if(command_name_types[command_name])
- if(warnings_only)
- SERVER_TOOLS_LOG("WARNING: Custom commands [command_name_types[command_name]] and [stc] have the same name, only [command_name_types[command_name]] will be available!")
- continue
- command_name_types[stc] = command_name
-
- if(!warnings_only)
- .[command_name] = list(SERVICE_JSON_PARAM_HELPTEXT = initial(stc.help_text), SERVICE_JSON_PARAM_ADMINONLY = initial(stc.admin_only), SERVICE_JSON_PARAM_REQUIREDPARAMETERS = initial(stc.required_parameters))
-
-/world/proc/HandleServiceCustomCommand(command, sender, params)
- var/static/list/cached_custom_server_tools_commands
- if(!cached_custom_server_tools_commands)
- cached_custom_server_tools_commands = list()
- for(var/I in typesof(/datum/server_tools_command) - /datum/server_tools_command)
- var/datum/server_tools_command/stc = I
- cached_custom_server_tools_commands[lowertext(initial(stc.name))] = stc
-
- var/command_type = cached_custom_server_tools_commands[command]
- if(!command_type)
- return FALSE
- var/datum/server_tools_command/stc = new command_type
- return stc.Run(sender, params) || TRUE
-
-/*
-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/code/modules/server_tools/st_interface.dm b/code/modules/server_tools/st_interface.dm
deleted file mode 100644
index ca7d54695b..0000000000
--- a/code/modules/server_tools/st_interface.dm
+++ /dev/null
@@ -1,135 +0,0 @@
-SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(reboot_mode, REBOOT_MODE_NORMAL)
-SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(server_tools_api_compatible, FALSE)
-
-/proc/GetTestMerges()
- if(RunningService(TRUE) && fexists(SERVICE_PR_TEST_JSON))
- . = json_decode(file2text(SERVICE_PR_TEST_JSON))
- if(.)
- return
- return list()
-
-/world/proc/ServiceInit()
- if(!RunningService(TRUE))
- return
- ListServiceCustomCommands(TRUE)
- ExportService("[SERVICE_REQUEST_API_VERSION] [SERVER_TOOLS_API_VERSION]", TRUE)
-
-/proc/RunningService(skip_compat_check = FALSE)
- if(!skip_compat_check && !SERVER_TOOLS_READ_GLOBAL(server_tools_api_compatible))
- return FALSE
- . = world.params[SERVICE_WORLD_PARAM] != null
- if(. && world.system_type != MS_WINDOWS)
- SERVER_TOOLS_LOG("Warning: Server tools world parameter detected but not running on Windows. Aborting initialization!")
- return FALSE
-
-/proc/ServiceVersion()
- if(RunningService(TRUE))
- return world.params[SERVICE_VERSION_PARAM]
-
-/proc/ServiceAPIVersion()
- return SERVICE_API_VERSION_STRING
-
-/world/proc/ExportService(command, skip_compat_check = FALSE)
- . = FALSE
- if(!RunningService(skip_compat_check))
- return
- if(skip_compat_check && !fexists(SERVICE_INTERFACE_DLL))
- CRASH("Service parameter present but no interface DLL detected. This is symptomatic of running a service less than version 3.1! Please upgrade.")
- var/instance = params[SERVICE_INSTANCE_PARAM]
- if(!instance)
- instance = "TG Station Server" //maybe just upgraded
- call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance, command) //trust no retval
- return TRUE
-
-/world/proc/ChatBroadcast(message)
- ExportService("[SERVICE_REQUEST_IRC_BROADCAST] [message]")
-
-/world/proc/AdminBroadcast(message)
- ExportService("[SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE] [message]")
-
-/world/proc/ServiceEndProcess()
- SERVER_TOOLS_LOG("Sending shutdown request!");
- sleep(world.tick_lag) //flush the buffers
- ExportService(SERVICE_REQUEST_KILL_PROCESS)
-
-//called at the exact moment the world is supposed to reboot
-/world/proc/ServiceReboot()
- switch(SERVER_TOOLS_READ_GLOBAL(reboot_mode))
- if(REBOOT_MODE_HARD)
- SERVER_TOOLS_WORLD_ANNOUNCE("Hard reboot triggered, you will automatically reconnect...")
- ServiceEndProcess()
- if(REBOOT_MODE_SHUTDOWN)
- SERVER_TOOLS_WORLD_ANNOUNCE("The server is shutting down...")
- ServiceEndProcess()
- else
- ExportService(SERVICE_REQUEST_WORLD_REBOOT) //just let em know
-
-/world/proc/ServiceCommand(list/params)
- var/their_sCK = params[SERVICE_CMD_PARAM_KEY]
- if(!their_sCK || !RunningService(TRUE))
- return FALSE //continue world/Topic
-
- var/sCK = world.params[SERVICE_WORLD_PARAM]
- if(their_sCK != sCK)
- return "Invalid comms key!";
-
- var/command = params[SERVICE_CMD_PARAM_COMMAND]
- if(!command)
- return "No command!"
-
- switch(command)
- if(SERVICE_CMD_API_COMPATIBLE)
- SERVER_TOOLS_WRITE_GLOBAL(server_tools_api_compatible, TRUE)
- return SERVICE_RETURN_SUCCESS
- if(SERVICE_CMD_HARD_REBOOT)
- if(SERVER_TOOLS_READ_GLOBAL(reboot_mode) != REBOOT_MODE_HARD)
- SERVER_TOOLS_WRITE_GLOBAL(reboot_mode, REBOOT_MODE_HARD)
- SERVER_TOOLS_LOG("Hard reboot requested by service")
- SERVER_TOOLS_NOTIFY_ADMINS("The world will hard reboot at the end of the game. Requested by service.")
- if(SERVICE_CMD_GRACEFUL_SHUTDOWN)
- if(SERVER_TOOLS_READ_GLOBAL(reboot_mode) != REBOOT_MODE_SHUTDOWN)
- SERVER_TOOLS_WRITE_GLOBAL(reboot_mode, REBOOT_MODE_SHUTDOWN)
- SERVER_TOOLS_LOG("Shutdown requested by service")
- message_admins("The world will shutdown at the end of the game. Requested by service.")
- if(SERVICE_CMD_WORLD_ANNOUNCE)
- var/msg = params["message"]
- if(!istext(msg) || !msg)
- return "No message set!"
- SERVER_TOOLS_WORLD_ANNOUNCE(msg)
- return SERVICE_RETURN_SUCCESS
- if(SERVICE_CMD_PLAYER_COUNT)
- return "[SERVER_TOOLS_CLIENT_COUNT]"
- if(SERVICE_CMD_LIST_CUSTOM)
- return json_encode(ListServiceCustomCommands(FALSE))
- else
- var/custom_command_result = HandleServiceCustomCommand(lowertext(command), params[SERVICE_CMD_PARAM_SENDER], params[SERVICE_CMD_PARAM_CUSTOM])
- if(custom_command_result)
- return istext(custom_command_result) ? custom_command_result : SERVICE_RETURN_SUCCESS
- 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/code/modules/tgs/core/_definitions.dm b/code/modules/tgs/core/_definitions.dm
new file mode 100644
index 0000000000..d5e1a0075b
--- /dev/null
+++ b/code/modules/tgs/core/_definitions.dm
@@ -0,0 +1,2 @@
+#define TGS_UNIMPLEMENTED "___unimplemented"
+#define TGS_VERSION_PARAMETER "server_service_version"
diff --git a/code/modules/tgs/core/core.dm b/code/modules/tgs/core/core.dm
new file mode 100644
index 0000000000..79c42ed37b
--- /dev/null
+++ b/code/modules/tgs/core/core.dm
@@ -0,0 +1,144 @@
+/world/TgsNew(datum/tgs_event_handler/event_handler)
+ var/tgs_version = world.params[TGS_VERSION_PARAMETER]
+ if(!tgs_version)
+ 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.")
+
+ TGS_INFO_LOG("Activating API for version [tgs_version]")
+ var/datum/tgs_api/new_api = new path
+
+ var/result = new_api.OnWorldNew(event_handler ? event_handler : new /datum/tgs_event_handler/tgs_default)
+ if(result && result != TGS_UNIMPLEMENTED)
+ TGS_WRITE_GLOBAL(tgs, new_api)
+ else
+ 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)
+ switch(major)
+ if(2)
+ return /datum/tgs_api/v3210
+
+ 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"
+
+/world/TgsMinimumAPIVersion()
+ return "3.2.0.0"
+
+/world/TgsInitializationComplete()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.OnInitializationComplete()
+
+/world/proc/TgsTopic(T)
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ var/result = api.OnTopic(T)
+ if(result != TGS_UNIMPLEMENTED)
+ return result
+
+/world/TgsRevision()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ var/result = api.Revision()
+ if(result != TGS_UNIMPLEMENTED)
+ return result
+
+/world/TgsReboot()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.OnReboot()
+
+/world/TgsAvailable()
+ return TGS_READ_GLOBAL(tgs) != null
+
+/world/TgsVersion()
+ return world.params[TGS_VERSION_PARAMETER]
+
+/world/TgsInstanceName()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ var/result = api.InstanceName()
+ if(result != TGS_UNIMPLEMENTED)
+ return result
+
+/world/TgsTestMerges()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ var/result = api.TestMerges()
+ if(result != TGS_UNIMPLEMENTED)
+ return result
+ return list()
+
+/world/TgsEndProcess()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.EndProcess()
+
+/world/TgsChatChannelInfo()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ var/result = api.ChatChannelInfo()
+ if(result != TGS_UNIMPLEMENTED)
+ return result
+ return list()
+
+/world/TgsChatBroadcast(message, list/channels)
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.ChatBroadcast(message, channels)
+
+/world/TgsTargetedChatBroadcast(message, admin_only)
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.ChatTargetedBroadcast(message, admin_only)
+
+/world/TgsChatPrivateMessage(message, datum/tgs_chat_user/user)
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.ChatPrivateMessage(message, user)
+
+/*
+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/code/modules/tgs/core/datum.dm b/code/modules/tgs/core/datum.dm
new file mode 100644
index 0000000000..5da73bfdc5
--- /dev/null
+++ b/code/modules/tgs/core/datum.dm
@@ -0,0 +1,74 @@
+TGS_DEFINE_AND_SET_GLOBAL(tgs, null)
+
+/datum/tgs_api
+
+/datum/tgs_api/latest
+ parent_type = /datum/tgs_api/v3210
+
+TGS_PROTECT_DATUM(/datum/tgs_api)
+
+/datum/tgs_api/proc/ApiVersion()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/OnWorldNew(datum/tgs_event_handler/event_handler)
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/OnInitializationComplete()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/OnTopic(T)
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/OnReboot()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/InstanceName()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/TestMerges()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/EndProcess()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/Revision()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/ChatChannelInfo()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/ChatBroadcast(message, list/channels)
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/ChatTargetedBroadcast(message, admin_only)
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/ChatPrivateMessage(message, admin_only)
+ return TGS_UNIMPLEMENTED
+
+/*
+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/code/modules/tgs/core/default_event_handler.dm b/code/modules/tgs/core/default_event_handler.dm
new file mode 100644
index 0000000000..c0ea8eec46
--- /dev/null
+++ b/code/modules/tgs/core/default_event_handler.dm
@@ -0,0 +1,30 @@
+/datum/tgs_event_handler/tgs_default/HandleEvent(event_code)
+ //TODO
+ return
+
+/*
+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/code/modules/tgs/includes.dm b/code/modules/tgs/includes.dm
new file mode 100644
index 0000000000..586cbbe427
--- /dev/null
+++ b/code/modules/tgs/includes.dm
@@ -0,0 +1,6 @@
+#include "core\_definitions.dm"
+#include "core\core.dm"
+#include "core\datum.dm"
+#include "core\default_event_handler.dm"
+#include "v3210\api.dm"
+#include "v3210\commands.dm"
diff --git a/code/modules/tgs/v3210/api.dm b/code/modules/tgs/v3210/api.dm
new file mode 100644
index 0000000000..f9b32471de
--- /dev/null
+++ b/code/modules/tgs/v3210/api.dm
@@ -0,0 +1,248 @@
+#define REBOOT_MODE_NORMAL 0
+#define REBOOT_MODE_HARD 1
+#define REBOOT_MODE_SHUTDOWN 2
+
+#define SERVICE_WORLD_PARAM "server_service"
+#define SERVICE_INSTANCE_PARAM "server_instance"
+#define SERVICE_PR_TEST_JSON "prtestjob.json"
+#define SERVICE_INTERFACE_DLL "TGDreamDaemonBridge.dll"
+#define SERVICE_INTERFACE_FUNCTION "DDEntryPoint"
+
+#define SERVICE_CMD_HARD_REBOOT "hard_reboot"
+#define SERVICE_CMD_GRACEFUL_SHUTDOWN "graceful_shutdown"
+#define SERVICE_CMD_WORLD_ANNOUNCE "world_announce"
+#define SERVICE_CMD_LIST_CUSTOM "list_custom_commands"
+#define SERVICE_CMD_API_COMPATIBLE "api_compat"
+#define SERVICE_CMD_PLAYER_COUNT "client_count"
+
+#define SERVICE_CMD_PARAM_KEY "serviceCommsKey"
+#define SERVICE_CMD_PARAM_COMMAND "command"
+#define SERVICE_CMD_PARAM_SENDER "sender"
+#define SERVICE_CMD_PARAM_CUSTOM "custom"
+
+#define SERVICE_REQUEST_KILL_PROCESS "killme"
+#define SERVICE_REQUEST_IRC_BROADCAST "irc"
+#define SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE "send2irc"
+#define SERVICE_REQUEST_WORLD_REBOOT "worldreboot"
+#define SERVICE_REQUEST_API_VERSION "api_ver"
+
+#define SERVICE_RETURN_SUCCESS "SUCCESS"
+
+/datum/tgs_api/v3210
+ var/reboot_mode = REBOOT_MODE_NORMAL
+ var/comms_key
+ var/instance_name
+ var/originmastercommit
+ var/commit
+ var/list/cached_custom_tgs_chat_commands
+ var/warned_revison = FALSE
+ var/warned_custom_commands = FALSE
+
+/datum/tgs_api/v3210/ApiVersion()
+ return "3.2.1.0"
+
+/datum/tgs_api/v3210/proc/trim_left(text)
+ for (var/i = 1 to length(text))
+ if (text2ascii(text, i) > 32)
+ return copytext(text, i)
+ return ""
+
+/datum/tgs_api/v3210/proc/trim_right(text)
+ for (var/i = length(text), i > 0, i--)
+ if (text2ascii(text, i) > 32)
+ return copytext(text, 1, i + 1)
+ return ""
+
+/datum/tgs_api/v3210/proc/file2list(filename)
+ return splittext(trim_left(trim_right(file2text(filename))), "\n")
+
+/datum/tgs_api/v3210/OnWorldNew(datum/tgs_event_handler/event_handler) //don't use event handling in this version
+ . = FALSE
+ comms_key = world.params[SERVICE_WORLD_PARAM]
+ instance_name = world.params[SERVICE_INSTANCE_PARAM]
+ if(!instance_name)
+ instance_name = "TG Station Server" //maybe just upgraded
+
+ var/list/logs = file2list(".git/logs/HEAD")
+ if(logs.len)
+ logs = splittext(logs[logs.len - 1], " ")
+ commit = logs[2]
+ logs = file2list(".git/logs/refs/remotes/origin/master")
+ if(logs.len)
+ originmastercommit = splittext(logs[logs.len - 1], " ")[2]
+
+ if(world.system_type != MS_WINDOWS)
+ TGS_ERROR_LOG("This API version is only supported on Windows. Not running on Windows. Aborting initialization!")
+ return
+ ListServiceCustomCommands(TRUE)
+ ExportService("[SERVICE_REQUEST_API_VERSION] [ApiVersion()]", TRUE)
+ return TRUE
+
+//nothing to do for v3
+/datum/tgs_api/v3210/OnInitializationComplete()
+ return
+
+/datum/tgs_api/v3210/InstanceName()
+ return world.params[SERVICE_INSTANCE_PARAM]
+
+/datum/tgs_api/v3210/proc/ExportService(command, skip_compat_check = FALSE)
+ . = FALSE
+ if(skip_compat_check && !fexists(SERVICE_INTERFACE_DLL))
+ TGS_ERROR_LOG("Service parameter present but no interface DLL detected. This is symptomatic of running a service less than version 3.1! Please upgrade.")
+ return
+ call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
+ return TRUE
+
+/datum/tgs_api/v3210/OnTopic(T)
+ var/list/params = params2list(T)
+ var/their_sCK = params[SERVICE_CMD_PARAM_KEY]
+ if(!their_sCK)
+ return FALSE //continue world/Topic
+
+ if(their_sCK != comms_key)
+ return "Invalid comms key!";
+
+ var/command = params[SERVICE_CMD_PARAM_COMMAND]
+ if(!command)
+ return "No command!"
+
+ switch(command)
+ if(SERVICE_CMD_API_COMPATIBLE)
+ return SERVICE_RETURN_SUCCESS
+ if(SERVICE_CMD_HARD_REBOOT)
+ if(reboot_mode != REBOOT_MODE_HARD)
+ reboot_mode = REBOOT_MODE_HARD
+ TGS_INFO_LOG("Hard reboot requested by service")
+ TGS_NOTIFY_ADMINS("The world will hard reboot at the end of the game. Requested by TGS.")
+ if(SERVICE_CMD_GRACEFUL_SHUTDOWN)
+ if(reboot_mode != REBOOT_MODE_SHUTDOWN)
+ reboot_mode = REBOOT_MODE_SHUTDOWN
+ TGS_INFO_LOG("Shutdown requested by service")
+ TGS_NOTIFY_ADMINS("The world will shutdown at the end of the game. Requested by TGS.")
+ if(SERVICE_CMD_WORLD_ANNOUNCE)
+ var/msg = params["message"]
+ if(!istext(msg) || !msg)
+ return "No message set!"
+ TGS_WORLD_ANNOUNCE(msg)
+ return SERVICE_RETURN_SUCCESS
+ if(SERVICE_CMD_PLAYER_COUNT)
+ return "[TGS_CLIENT_COUNT]"
+ if(SERVICE_CMD_LIST_CUSTOM)
+ return json_encode(ListServiceCustomCommands(FALSE))
+ else
+ var/custom_command_result = HandleServiceCustomCommand(lowertext(command), params[SERVICE_CMD_PARAM_SENDER], params[SERVICE_CMD_PARAM_CUSTOM])
+ if(custom_command_result)
+ return istext(custom_command_result) ? custom_command_result : SERVICE_RETURN_SUCCESS
+ return "Unknown command: [command]"
+
+/datum/tgs_api/v3210/OnReboot()
+ switch(reboot_mode)
+ if(REBOOT_MODE_HARD)
+ TGS_WORLD_ANNOUNCE("Hard reboot triggered, you will automatically reconnect...")
+ EndProcess()
+ if(REBOOT_MODE_SHUTDOWN)
+ TGS_WORLD_ANNOUNCE("The server is shutting down...")
+ EndProcess()
+ else
+ ExportService(SERVICE_REQUEST_WORLD_REBOOT) //just let em know
+
+/datum/tgs_api/v3210/TestMerges()
+ //do the best we can here as the datum can't be completed using the v3 api
+ . = list()
+ if(!fexists(SERVICE_PR_TEST_JSON))
+ return
+ var/list/json = json_decode(file2text(SERVICE_PR_TEST_JSON))
+ if(!json)
+ return
+ for(var/I in json)
+ var/datum/tgs_revision_information/test_merge/tm = new
+ tm.number = text2num(I)
+ var/list/entry = json[I]
+ tm.pull_request_commit = entry["commit"]
+ tm.author = entry["author"]
+ tm.title = entry["title"]
+ . += tm
+
+/datum/tgs_api/v3210/Revision()
+ if(!warned_revison)
+ TGS_ERROR_LOG("Use of TgsRevision on [ApiVersion()] origin_commit only points to master!")
+ warned_revison = TRUE
+ var/datum/tgs_revision_information/ri = new
+ ri.commit = commit
+ ri.origin_commit = originmastercommit
+
+/datum/tgs_api/v3210/EndProcess()
+ sleep(world.tick_lag) //flush the buffers
+ ExportService(SERVICE_REQUEST_KILL_PROCESS)
+
+/datum/tgs_api/v3210/ChatChannelInfo()
+ return list()
+
+/datum/tgs_api/v3210/ChatBroadcast(message, list/channels)
+ if(channels)
+ return TGS_UNIMPLEMENTED
+ ChatTargetedBroadcast(message, TRUE)
+ ChatTargetedBroadcast(message, FALSE)
+
+/datum/tgs_api/v3210/ChatTargetedBroadcast(message, admin_only)
+ ExportService("[admin_only ? SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE : SERVICE_REQUEST_IRC_BROADCAST] [message]")
+
+/datum/tgs_api/v3210/ChatPrivateMessage(message, admin_only)
+ return TGS_UNIMPLEMENTED
+
+#undef REBOOT_MODE_NORMAL
+#undef REBOOT_MODE_HARD
+#undef REBOOT_MODE_SHUTDOWN
+
+#undef SERVICE_WORLD_PARAM
+#undef SERVICE_INSTANCE_PARAM
+#undef SERVICE_PR_TEST_JSON
+#undef SERVICE_INTERFACE_DLL
+#undef SERVICE_INTERFACE_FUNCTION
+
+#undef SERVICE_CMD_HARD_REBOOT
+#undef SERVICE_CMD_GRACEFUL_SHUTDOWN
+#undef SERVICE_CMD_WORLD_ANNOUNCE
+#undef SERVICE_CMD_LIST_CUSTOM
+#undef SERVICE_CMD_API_COMPATIBLE
+#undef SERVICE_CMD_PLAYER_COUNT
+
+#undef SERVICE_CMD_PARAM_KEY
+#undef SERVICE_CMD_PARAM_COMMAND
+#undef SERVICE_CMD_PARAM_SENDER
+#undef SERVICE_CMD_PARAM_CUSTOM
+
+#undef SERVICE_REQUEST_KILL_PROCESS
+#undef SERVICE_REQUEST_IRC_BROADCAST
+#undef SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE
+#undef SERVICE_REQUEST_WORLD_REBOOT
+#undef SERVICE_REQUEST_API_VERSION
+
+#undef SERVICE_RETURN_SUCCESS
+
+/*
+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/code/modules/tgs/v3210/commands.dm b/code/modules/tgs/v3210/commands.dm
new file mode 100644
index 0000000000..71d7e32366
--- /dev/null
+++ b/code/modules/tgs/v3210/commands.dm
@@ -0,0 +1,78 @@
+#define SERVICE_JSON_PARAM_HELPTEXT "help_text"
+#define SERVICE_JSON_PARAM_ADMINONLY "admin_only"
+#define SERVICE_JSON_PARAM_REQUIREDPARAMETERS "required_parameters"
+
+/datum/tgs_api/v3210/proc/ListServiceCustomCommands(warnings_only)
+ if(!warnings_only)
+ . = list()
+ var/list/command_name_types = list()
+ var/list/warned_command_names = warnings_only ? list() : null
+ var/warned_about_the_dangers_of_robutussin = !warnings_only
+ for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
+ if(!warned_about_the_dangers_of_robutussin)
+ TGS_ERROR_LOG("Custom chat commands in [ApiVersion()] lacks the /datum/tgs_chat_user/sender.channel field!")
+ warned_about_the_dangers_of_robutussin = TRUE
+ var/datum/tgs_chat_command/stc = I
+ var/command_name = initial(stc.name)
+ if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
+ if(warnings_only && !warned_command_names[command_name])
+ TGS_ERROR_LOG("Custom command [command_name] can't be used as it is empty or contains illegal characters!")
+ warned_command_names[command_name] = TRUE
+ continue
+
+ if(command_name_types[command_name])
+ if(warnings_only)
+ TGS_ERROR_LOG("Custom commands [command_name_types[command_name]] and [stc] have the same name, only [command_name_types[command_name]] will be available!")
+ continue
+ command_name_types[stc] = command_name
+
+ if(!warnings_only)
+ .[command_name] = list(SERVICE_JSON_PARAM_HELPTEXT = initial(stc.help_text), SERVICE_JSON_PARAM_ADMINONLY = initial(stc.admin_only), SERVICE_JSON_PARAM_REQUIREDPARAMETERS = 0)
+
+/datum/tgs_api/v3210/proc/HandleServiceCustomCommand(command, sender, params)
+ if(!cached_custom_tgs_chat_commands)
+ cached_custom_tgs_chat_commands = list()
+ for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
+ var/datum/tgs_chat_command/stc = I
+ cached_custom_tgs_chat_commands[lowertext(initial(stc.name))] = stc
+
+ var/command_type = cached_custom_tgs_chat_commands[command]
+ if(!command_type)
+ return FALSE
+ var/datum/tgs_chat_command/stc = new command_type
+ var/datum/tgs_chat_user/user = new
+ user.friendly_name = sender
+ user.mention = sender
+ return stc.Run(user, params) || TRUE
+
+/*
+
+#undef SERVICE_JSON_PARAM_HELPTEXT
+#undef SERVICE_JSON_PARAM_ADMINONLY
+#undef SERVICE_JSON_PARAM_REQUIREDPARAMETERS
+
+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/tgstation.dme b/tgstation.dme
index d356f887a4..2f9498c45b 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -74,8 +74,6 @@
#include "code\__DEFINES\robots.dm"
#include "code\__DEFINES\role_preferences.dm"
#include "code\__DEFINES\say.dm"
-#include "code\__DEFINES\server_tools.config.dm"
-#include "code\__DEFINES\server_tools.dm"
#include "code\__DEFINES\shuttles.dm"
#include "code\__DEFINES\sight.dm"
#include "code\__DEFINES\sound.dm"
@@ -83,6 +81,8 @@
#include "code\__DEFINES\stat_tracking.dm"
#include "code\__DEFINES\status_effects.dm"
#include "code\__DEFINES\subsystems.dm"
+#include "code\__DEFINES\tgs.config.dm"
+#include "code\__DEFINES\tgs.dm"
#include "code\__DEFINES\tgui.dm"
#include "code\__DEFINES\time.dm"
#include "code\__DEFINES\tools.dm"
@@ -2448,8 +2448,6 @@
#include "code\modules\ruins\spaceruin_code\whiteshipruin_box.dm"
#include "code\modules\security_levels\keycard_authentication.dm"
#include "code\modules\security_levels\security_levels.dm"
-#include "code\modules\server_tools\st_commands.dm"
-#include "code\modules\server_tools\st_interface.dm"
#include "code\modules\shuttle\arrivals.dm"
#include "code\modules\shuttle\assault_pod.dm"
#include "code\modules\shuttle\computer.dm"
@@ -2562,6 +2560,7 @@
#include "code\modules\surgery\organs\tails.dm"
#include "code\modules\surgery\organs\tongue.dm"
#include "code\modules\surgery\organs\vocal_cords.dm"
+#include "code\modules\tgs\includes.dm"
#include "code\modules\tgui\external.dm"
#include "code\modules\tgui\states.dm"
#include "code\modules\tgui\subsystem.dm"