Skip to content

Commit

Permalink
Update TGS DMAPI
Browse files Browse the repository at this point in the history
  • Loading branch information
tgstation-server committed Nov 12, 2024
1 parent 3991d5e commit 279c680
Show file tree
Hide file tree
Showing 25 changed files with 1,194 additions and 316 deletions.
360 changes: 285 additions & 75 deletions code/__DEFINES/tgs.dm

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion code/modules/tgs/LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License

Copyright (c) 2017 Jordan Brown
Copyright (c) 2017-2024 Jordan Brown

Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
Expand Down
4 changes: 2 additions & 2 deletions code/modules/tgs/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# DMAPI Internals

This folder should be placed on it's own inside a codebase that wishes to use the TGS DMAPI. Warranty void if modified.
This folder should be placed on its own inside a codebase that wishes to use the TGS DMAPI. Warranty void if modified.

- [includes.dm](./includes.dm) is the file that should be included by DM code, it handles including the rest.
- The [core](./core) folder includes all code not directly part of any API version.
- The other versioned folders contain code for the different DMAPI versions.
- [v3210](./v3210) contains the final TGS3 API.
- [v4](./v4) is the legacy DMAPI 4 (Used in TGS 4.0.X versions).
- [v5](./v5) is the current DMAPI version used by TGS4 >=4.1.
- [v5](./v5) is the current DMAPI version used by TGS >=4.1.
- [LICENSE](./LICENSE) is the MIT license for the DMAPI.

APIs communicate with TGS in two ways. All versions implement TGS -> DM communication using /world/Topic. DM -> TGS communication, called the bridge method, is different for each version.
3 changes: 2 additions & 1 deletion code/modules/tgs/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
This folder contains all DMAPI code not directly involved in an API.

- [_definitions.dm](./definitions.dm) contains defines needed across DMAPI internals.
- [byond_world_export.dm](./byond_world_export.dm) contains the default `/datum/tgs_http_handler` implementation which uses `world.Export()`.
- [core.dm](./core.dm) contains the implementations of the `/world/proc/TgsXXX()` procs. Many map directly to the `/datum/tgs_api` functions. It also contains the /datum selection and setup code.
- [datum.dm](./datum.dm) contains the `/datum/tgs_api` declarations that all APIs must implement.
- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition
- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition
8 changes: 8 additions & 0 deletions code/modules/tgs/core/_definitions.dm
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
#if DM_VERSION < 510
#error The TGS DMAPI does not support BYOND versions < 510!
#endif

#define TGS_UNIMPLEMENTED "___unimplemented"
#define TGS_VERSION_PARAMETER "server_service_version"

#ifndef TGS_DEBUG_LOG
#define TGS_DEBUG_LOG(message)
#endif
22 changes: 22 additions & 0 deletions code/modules/tgs/core/byond_world_export.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/datum/tgs_http_handler/byond_world_export

/datum/tgs_http_handler/byond_world_export/PerformGet(url)
// This is an infinite sleep until we get a response
var/export_response = world.Export(url)
TGS_DEBUG_LOG("byond_world_export: Export complete")

if(!export_response)
TGS_ERROR_LOG("byond_world_export: Failed request: [url]")
return new /datum/tgs_http_result(null, FALSE)

var/content = export_response["CONTENT"]
if(!content)
TGS_ERROR_LOG("byond_world_export: Failed request, missing content!")
return new /datum/tgs_http_result(null, FALSE)

var/response_json = TGS_FILE2TEXT_NATIVE(content)
if(!response_json)
TGS_ERROR_LOG("byond_world_export: Failed request, failed to load content!")
return new /datum/tgs_http_result(null, FALSE)

return new /datum/tgs_http_result(response_json, TRUE)
33 changes: 28 additions & 5 deletions code/modules/tgs/core/core.dm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/world/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
/world/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE, datum/tgs_http_handler/http_handler = null)
var/current_api = TGS_READ_GLOBAL(tgs)
if(current_api)
TGS_ERROR_LOG("API datum already set (\ref[current_api] ([current_api]))! Was TgsNew() called more than once?")
Expand Down Expand Up @@ -42,11 +42,11 @@

var/datum/tgs_version/max_api_version = TgsMaximumApiVersion();
if(version.suite != null && version.minor != null && version.patch != null && version.deprecated_patch != null && version.deprefixed_parameter > max_api_version.deprefixed_parameter)
TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.")
TGS_ERROR_LOG("Detected unknown Interop API version! Defaulting to latest. Update the DMAPI to fix this problem.")
api_datum = /datum/tgs_api/latest

if(!api_datum)
TGS_ERROR_LOG("Found unsupported API version: [raw_parameter]. If this is a valid version please report this, backporting is done on demand.")
TGS_ERROR_LOG("Found unsupported Interop API version: [raw_parameter]. If this is a valid version please report this, backporting is done on demand.")
return

TGS_INFO_LOG("Activating API for version [version.deprefixed_parameter]")
Expand All @@ -55,7 +55,10 @@
TGS_ERROR_LOG("Invalid parameter for event_handler: [event_handler]")
event_handler = null

var/datum/tgs_api/new_api = new api_datum(event_handler, version)
if(!http_handler)
http_handler = new /datum/tgs_http_handler/byond_world_export

var/datum/tgs_api/new_api = new api_datum(event_handler, version, http_handler)

TGS_WRITE_GLOBAL(tgs, new_api)

Expand Down Expand Up @@ -107,6 +110,13 @@
if(api)
return api.ApiVersion()

/world/TgsEngine()
#ifdef OPENDREAM
return TGS_ENGINE_TYPE_OPENDREAM
#else
return TGS_ENGINE_TYPE_BYOND
#endif

/world/TgsInstanceName()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
if(api)
Expand Down Expand Up @@ -153,4 +163,17 @@
/world/TgsSecurityLevel()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
if(api)
api.SecurityLevel()
return api.SecurityLevel()

/world/TgsVisibility()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
if(api)
return api.Visibility()

/world/TgsTriggerEvent(event_name, list/parameters, wait_for_completion = FALSE)
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
if(api)
if(!istype(parameters, /list))
parameters = list()

return api.TriggerEvent(event_name, parameters, wait_for_completion)
21 changes: 19 additions & 2 deletions code/modules/tgs/core/datum.dm
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,22 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null)
var/datum/tgs_version/version
var/datum/tgs_event_handler/event_handler

/datum/tgs_api/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version)
. = ..()
var/list/warned_deprecated_command_runs

/datum/tgs_api/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version, datum/tgs_http_handler/http_handler)
..()
src.event_handler = event_handler
src.version = version

/datum/tgs_api/proc/TerminateWorld()
while(TRUE)
TGS_DEBUG_LOG("About to terminate world. Tick: [world.time], sleep_offline: [world.sleep_offline]")
world.sleep_offline = FALSE // https://www.byond.com/forum/post/2894866
del(world)
world.sleep_offline = FALSE // just in case, this is BYOND after all...
sleep(world.tick_lag)
TGS_DEBUG_LOG("BYOND DIDN'T TERMINATE THE WORLD!!! TICK IS: [world.time], sleep_offline: [world.sleep_offline]")

/datum/tgs_api/latest
parent_type = /datum/tgs_api/v5

Expand Down Expand Up @@ -55,3 +66,9 @@ TGS_PROTECT_DATUM(/datum/tgs_api)

/datum/tgs_api/proc/SecurityLevel()
return TGS_UNIMPLEMENTED

/datum/tgs_api/proc/Visibility()
return TGS_UNIMPLEMENTED

/datum/tgs_api/proc/TriggerEvent(event_name, list/parameters, wait_for_completion)
return FALSE
1 change: 1 addition & 0 deletions code/modules/tgs/core/tgs_version.dm
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/datum/tgs_version/New(raw_parameter)
..()
src.raw_parameter = raw_parameter
deprefixed_parameter = replacetext(raw_parameter, "/tg/station 13 Server v", "")
var/list/version_bits = splittext(deprefixed_parameter, ".")
Expand Down
5 changes: 5 additions & 0 deletions code/modules/tgs/includes.dm
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "core\_definitions.dm"
#include "core\byond_world_export.dm"
#include "core\core.dm"
#include "core\datum.dm"
#include "core\tgs_version.dm"
Expand All @@ -13,5 +14,9 @@

#include "v5\_defines.dm"
#include "v5\api.dm"
#include "v5\bridge.dm"
#include "v5\chunking.dm"
#include "v5\commands.dm"
#include "v5\serializers.dm"
#include "v5\topic.dm"
#include "v5\undefs.dm"
40 changes: 28 additions & 12 deletions code/modules/tgs/v3210/api.dm
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

#define SERVICE_RETURN_SUCCESS "SUCCESS"

#define TGS_FILE2LIST(filename) (splittext(trim_left(trim_right(file2text(filename))), "\n"))

/datum/tgs_api/v3210
var/reboot_mode = REBOOT_MODE_NORMAL
var/comms_key
Expand All @@ -53,9 +55,6 @@
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(minimum_required_security_level)
. = FALSE

Expand All @@ -64,13 +63,21 @@
if(!instance_name)
instance_name = "TG Station Server" //maybe just upgraded

var/list/logs = file2list(".git/logs/HEAD")
var/list/logs = TGS_FILE2LIST(".git/logs/HEAD")
if(logs.len)
logs = splittext(logs[logs.len - 1], " ")
commit = logs[2]
logs = file2list(".git/logs/refs/remotes/origin/master")
logs = splittext(logs[logs.len], " ")
if (logs.len >= 2)
commit = logs[2]
else
TGS_ERROR_LOG("Error parsing commit logs")

logs = TGS_FILE2LIST(".git/logs/refs/remotes/origin/master")
if(logs.len)
originmastercommit = splittext(logs[logs.len - 1], " ")[2]
logs = splittext(logs[logs.len], " ")
if (logs.len >= 2)
originmastercommit = logs[2]
else
TGS_ERROR_LOG("Error parsing origin commmit logs")

if(world.system_type != MS_WINDOWS)
TGS_ERROR_LOG("This API version is only supported on Windows. Not running on Windows. Aborting initialization!")
Expand All @@ -92,7 +99,11 @@
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
#if DM_VERSION >= 515
call_ext(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
#else
call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
#endif
return TRUE

/datum/tgs_api/v3210/OnTopic(T)
Expand Down Expand Up @@ -168,7 +179,7 @@
/datum/tgs_api/v3210/Revision()
if(!warned_revison)
var/datum/tgs_version/api_version = ApiVersion()
TGS_ERROR_LOG("Use of TgsRevision on [api_version.deprefixed_parameter] origin_commit only points to master!")
TGS_WARNING_LOG("Use of TgsRevision on [api_version.deprefixed_parameter] origin_commit only points to master!")
warned_revison = TRUE
var/datum/tgs_revision_information/ri = new
ri.commit = commit
Expand All @@ -182,16 +193,19 @@
/datum/tgs_api/v3210/ChatChannelInfo()
return list() // :omegalul:

/datum/tgs_api/v3210/ChatBroadcast(message, list/channels)
/datum/tgs_api/v3210/ChatBroadcast(datum/tgs_message_content/message, list/channels)
if(channels)
return TGS_UNIMPLEMENTED
message = UpgradeDeprecatedChatMessage(message)
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/ChatTargetedBroadcast(datum/tgs_message_content/message, admin_only)
message = UpgradeDeprecatedChatMessage(message)
ExportService("[admin_only ? SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE : SERVICE_REQUEST_IRC_BROADCAST] [message.text]")

/datum/tgs_api/v3210/ChatPrivateMessage(message, datum/tgs_chat_user/user)
UpgradeDeprecatedChatMessage(message)
return TGS_UNIMPLEMENTED

/datum/tgs_api/v3210/SecurityLevel()
Expand Down Expand Up @@ -226,3 +240,5 @@
#undef SERVICE_REQUEST_API_VERSION

#undef SERVICE_RETURN_SUCCESS

#undef TGS_FILE2LIST
12 changes: 9 additions & 3 deletions code/modules/tgs/v3210/commands.dm
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
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!")
TGS_WARNING_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
if(stc.ignore_type == I)
continue

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])
Expand Down Expand Up @@ -44,9 +47,12 @@
user.friendly_name = sender

// Discord hack, fix the mention if it's only numbers (fuck you IRC trolls)
var/regex/discord_id_regex = regex(@"^[0-9]+$")
var/regex/discord_id_regex = regex("^\[0-9\]+$")
if(findtext(sender, discord_id_regex))
sender = "<@[sender]>"

user.mention = sender
return stc.Run(user, params) || TRUE
var/datum/tgs_message_content/result = stc.Run(user, params)
result = UpgradeDeprecatedCommandResponse(result, command)

return result ? result.text : TRUE
Loading

0 comments on commit 279c680

Please sign in to comment.