mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-13 19:22:20 +00:00
This pull request updates the TGS DMAPI to the latest version. Please note any changes that may be breaking or unimplemented in your codebase by checking what changes are in the definitions file: code/__DEFINES/tgs.dm before merging. Co-authored-by: tgstation-server <tgstation-server@users.noreply.github.com>
323 lines
9.6 KiB
Plaintext
323 lines
9.6 KiB
Plaintext
#define TGS4_PARAM_INFO_JSON "tgs_json"
|
|
|
|
#define TGS4_INTEROP_ACCESS_IDENTIFIER "tgs_tok"
|
|
|
|
#define TGS4_RESPONSE_SUCCESS "tgs_succ"
|
|
|
|
#define TGS4_TOPIC_CHANGE_PORT "tgs_port"
|
|
#define TGS4_TOPIC_CHANGE_REBOOT_MODE "tgs_rmode"
|
|
#define TGS4_TOPIC_CHAT_COMMAND "tgs_chat_comm"
|
|
#define TGS4_TOPIC_EVENT "tgs_event"
|
|
#define TGS4_TOPIC_INTEROP_RESPONSE "tgs_interop"
|
|
|
|
#define TGS4_COMM_NEW_PORT "tgs_new_port"
|
|
#define TGS4_COMM_VALIDATE "tgs_validate"
|
|
#define TGS4_COMM_SERVER_PRIMED "tgs_prime"
|
|
#define TGS4_COMM_WORLD_REBOOT "tgs_reboot"
|
|
#define TGS4_COMM_END_PROCESS "tgs_kill"
|
|
#define TGS4_COMM_CHAT "tgs_chat_send"
|
|
|
|
#define TGS4_PARAMETER_COMMAND "tgs_com"
|
|
#define TGS4_PARAMETER_DATA "tgs_data"
|
|
|
|
#define TGS4_PORT_CRITFAIL_MESSAGE " Must exit to let watchdog reboot..."
|
|
|
|
#define EXPORT_TIMEOUT_DS 200
|
|
|
|
/datum/tgs_api/v4
|
|
var/access_identifier
|
|
var/instance_name
|
|
var/json_path
|
|
var/chat_channels_json_path
|
|
var/chat_commands_json_path
|
|
var/server_commands_json_path
|
|
var/reboot_mode = TGS_REBOOT_MODE_NORMAL
|
|
var/security_level
|
|
|
|
var/requesting_new_port = FALSE
|
|
|
|
var/list/intercepted_message_queue
|
|
|
|
var/list/custom_commands
|
|
|
|
var/list/cached_test_merges
|
|
var/datum/tgs_revision_information/cached_revision
|
|
|
|
var/export_lock = FALSE
|
|
var/list/last_interop_response
|
|
|
|
/datum/tgs_api/v4/ApiVersion()
|
|
return new /datum/tgs_version("4.0.0.0")
|
|
|
|
/datum/tgs_api/v4/OnWorldNew(minimum_required_security_level)
|
|
if(minimum_required_security_level == TGS_SECURITY_ULTRASAFE)
|
|
TGS_WARNING_LOG("V4 DMAPI requires safe security!")
|
|
minimum_required_security_level = TGS_SECURITY_SAFE
|
|
|
|
json_path = world.params[TGS4_PARAM_INFO_JSON]
|
|
if(!json_path)
|
|
TGS_ERROR_LOG("Missing [TGS4_PARAM_INFO_JSON] world parameter!")
|
|
return
|
|
var/json_file = file2text(json_path)
|
|
if(!json_file)
|
|
TGS_ERROR_LOG("Missing specified json file: [json_path]")
|
|
return
|
|
var/cached_json = json_decode(json_file)
|
|
if(!cached_json)
|
|
TGS_ERROR_LOG("Failed to decode info json: [json_file]")
|
|
return
|
|
|
|
access_identifier = cached_json["accessIdentifier"]
|
|
server_commands_json_path = cached_json["serverCommandsJson"]
|
|
|
|
if(cached_json["apiValidateOnly"])
|
|
TGS_INFO_LOG("Validating API and exiting...")
|
|
Export(TGS4_COMM_VALIDATE, list(TGS4_PARAMETER_DATA = "[minimum_required_security_level]"))
|
|
TerminateWorld()
|
|
|
|
security_level = cached_json["securityLevel"]
|
|
chat_channels_json_path = cached_json["chatChannelsJson"]
|
|
chat_commands_json_path = cached_json["chatCommandsJson"]
|
|
instance_name = cached_json["instanceName"]
|
|
|
|
ListCustomCommands()
|
|
|
|
var/list/revisionData = cached_json["revision"]
|
|
if(revisionData)
|
|
cached_revision = new
|
|
cached_revision.commit = revisionData["commitSha"]
|
|
cached_revision.origin_commit = revisionData["originCommitSha"]
|
|
|
|
cached_test_merges = list()
|
|
var/list/json = cached_json["testMerges"]
|
|
for(var/entry in json)
|
|
var/datum/tgs_revision_information/test_merge/tm = new
|
|
tm.timestamp = text2num(entry["timeMerged"])
|
|
|
|
var/list/revInfo = entry["revision"]
|
|
if(revInfo)
|
|
tm.commit = revInfo["commitSha"]
|
|
tm.origin_commit = revInfo["originCommitSha"]
|
|
|
|
tm.title = entry["titleAtMerge"]
|
|
tm.body = entry["bodyAtMerge"]
|
|
tm.url = entry["url"]
|
|
tm.author = entry["author"]
|
|
tm.number = entry["number"]
|
|
tm.head_commit = entry["pullRequestRevision"]
|
|
tm.comment = entry["comment"]
|
|
|
|
cached_test_merges += tm
|
|
|
|
return TRUE
|
|
|
|
/datum/tgs_api/v4/OnInitializationComplete()
|
|
Export(TGS4_COMM_SERVER_PRIMED)
|
|
|
|
/datum/tgs_api/v4/OnTopic(T)
|
|
var/list/params = params2list(T)
|
|
var/their_sCK = params[TGS4_INTEROP_ACCESS_IDENTIFIER]
|
|
if(!their_sCK)
|
|
return FALSE //continue world/Topic
|
|
|
|
if(their_sCK != access_identifier)
|
|
return "Invalid comms key!";
|
|
|
|
var/command = params[TGS4_PARAMETER_COMMAND]
|
|
if(!command)
|
|
return "No command!"
|
|
|
|
. = TGS4_RESPONSE_SUCCESS
|
|
|
|
switch(command)
|
|
if(TGS4_TOPIC_CHAT_COMMAND)
|
|
var/result = HandleCustomCommand(params[TGS4_PARAMETER_DATA])
|
|
if(result == null)
|
|
result = "Error running chat command!"
|
|
return result
|
|
if(TGS4_TOPIC_EVENT)
|
|
intercepted_message_queue = list()
|
|
var/list/event_notification = json_decode(params[TGS4_PARAMETER_DATA])
|
|
var/list/event_parameters = event_notification["Parameters"]
|
|
|
|
var/list/event_call = list(event_notification["Type"])
|
|
if(event_parameters)
|
|
event_call += event_parameters
|
|
|
|
if(event_handler != null)
|
|
event_handler.HandleEvent(arglist(event_call))
|
|
|
|
. = json_encode(intercepted_message_queue)
|
|
intercepted_message_queue = null
|
|
return
|
|
if(TGS4_TOPIC_INTEROP_RESPONSE)
|
|
last_interop_response = json_decode(params[TGS4_PARAMETER_DATA])
|
|
return
|
|
if(TGS4_TOPIC_CHANGE_PORT)
|
|
var/new_port = text2num(params[TGS4_PARAMETER_DATA])
|
|
if (!(new_port > 0))
|
|
return "Invalid port: [new_port]"
|
|
|
|
//the topic still completes, miraculously
|
|
//I honestly didn't believe byond could do it
|
|
if(event_handler != null)
|
|
event_handler.HandleEvent(TGS_EVENT_PORT_SWAP, new_port)
|
|
if(!world.OpenPort(new_port))
|
|
return "Port change failed!"
|
|
return
|
|
if(TGS4_TOPIC_CHANGE_REBOOT_MODE)
|
|
var/new_reboot_mode = text2num(params[TGS4_PARAMETER_DATA])
|
|
if(event_handler != null)
|
|
event_handler.HandleEvent(TGS_EVENT_REBOOT_MODE_CHANGE, reboot_mode, new_reboot_mode)
|
|
reboot_mode = new_reboot_mode
|
|
return
|
|
|
|
return "Unknown command: [command]"
|
|
|
|
/datum/tgs_api/v4/proc/Export(command, list/data, override_requesting_new_port = FALSE)
|
|
if(!data)
|
|
data = list()
|
|
data[TGS4_PARAMETER_COMMAND] = command
|
|
var/json = json_encode(data)
|
|
|
|
while(requesting_new_port && !override_requesting_new_port)
|
|
sleep(world.tick_lag)
|
|
|
|
//we need some port open at this point to facilitate return communication
|
|
if(!world.port)
|
|
requesting_new_port = TRUE
|
|
if(!world.OpenPort(0)) //open any port
|
|
TGS_ERROR_LOG("Unable to open random port to retrieve new port![TGS4_PORT_CRITFAIL_MESSAGE]")
|
|
TerminateWorld()
|
|
|
|
//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]")
|
|
TerminateWorld()
|
|
|
|
var/new_port = new_port_json[TGS4_PARAMETER_DATA]
|
|
if(!isnum(new_port) || new_port <= 0)
|
|
TGS_ERROR_LOG("Malformed new port json ([json_encode(new_port_json)])![TGS4_PORT_CRITFAIL_MESSAGE]")
|
|
TerminateWorld()
|
|
|
|
if(new_port != world.port && !world.OpenPort(new_port))
|
|
TGS_ERROR_LOG("Unable to open port [new_port]![TGS4_PORT_CRITFAIL_MESSAGE]")
|
|
TerminateWorld()
|
|
requesting_new_port = FALSE
|
|
|
|
while(export_lock)
|
|
sleep(world.tick_lag)
|
|
export_lock = TRUE
|
|
|
|
last_interop_response = null
|
|
fdel(server_commands_json_path)
|
|
text2file(json, server_commands_json_path)
|
|
|
|
for(var/I = 0; I < EXPORT_TIMEOUT_DS && !last_interop_response; ++I)
|
|
sleep(world.tick_lag)
|
|
|
|
if(!last_interop_response)
|
|
TGS_ERROR_LOG("Failed to get export result for: [json]")
|
|
else
|
|
. = last_interop_response
|
|
|
|
export_lock = FALSE
|
|
|
|
/datum/tgs_api/v4/OnReboot()
|
|
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]
|
|
if(!isnum(port))
|
|
return //this is valid, server may just want use to reboot
|
|
|
|
if(port == 0)
|
|
//to byond 0 means any port and "none" means close vOv
|
|
port = "none"
|
|
|
|
if(!world.OpenPort(port))
|
|
TGS_ERROR_LOG("Unable to set port to [port]!")
|
|
|
|
/datum/tgs_api/v4/InstanceName()
|
|
return instance_name
|
|
|
|
/datum/tgs_api/v4/TestMerges()
|
|
return cached_test_merges.Copy()
|
|
|
|
/datum/tgs_api/v4/EndProcess()
|
|
Export(TGS4_COMM_END_PROCESS)
|
|
|
|
/datum/tgs_api/v4/Revision()
|
|
return cached_revision
|
|
|
|
/datum/tgs_api/v4/ChatBroadcast(datum/tgs_message_content/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
|
|
|
|
message = UpgradeDeprecatedChatMessage(message)
|
|
|
|
if (!length(channels))
|
|
return
|
|
|
|
message = list("message" = message.text, "channelIds" = ids)
|
|
if(intercepted_message_queue)
|
|
intercepted_message_queue += list(message)
|
|
else
|
|
Export(TGS4_COMM_CHAT, message)
|
|
|
|
/datum/tgs_api/v4/ChatTargetedBroadcast(datum/tgs_message_content/message, admin_only)
|
|
var/list/channels = list()
|
|
for(var/I in ChatChannelInfo())
|
|
var/datum/tgs_chat_channel/channel = I
|
|
if (!channel.is_private_channel && ((channel.is_admin_channel && admin_only) || (!channel.is_admin_channel && !admin_only)))
|
|
channels += channel.id
|
|
|
|
message = UpgradeDeprecatedChatMessage(message)
|
|
|
|
if (!length(channels))
|
|
return
|
|
|
|
message = list("message" = message.text, "channelIds" = channels)
|
|
if(intercepted_message_queue)
|
|
intercepted_message_queue += list(message)
|
|
else
|
|
Export(TGS4_COMM_CHAT, message)
|
|
|
|
/datum/tgs_api/v4/ChatPrivateMessage(datum/tgs_message_content/message, datum/tgs_chat_user/user)
|
|
message = UpgradeDeprecatedChatMessage(message)
|
|
message = list("message" = message.text, "channelIds" = list(user.channel.id))
|
|
if(intercepted_message_queue)
|
|
intercepted_message_queue += list(message)
|
|
else
|
|
Export(TGS4_COMM_CHAT, message)
|
|
|
|
/datum/tgs_api/v4/ChatChannelInfo()
|
|
. = list()
|
|
//no caching cause tgs may change this
|
|
var/list/json = json_decode(file2text(chat_channels_json_path))
|
|
for(var/I in json)
|
|
. += DecodeChannel(I)
|
|
|
|
/datum/tgs_api/v4/proc/DecodeChannel(channel_json)
|
|
var/datum/tgs_chat_channel/channel = new
|
|
channel.id = channel_json["id"]
|
|
channel.friendly_name = channel_json["friendlyName"]
|
|
channel.connection_name = channel_json["connectionName"]
|
|
channel.is_admin_channel = channel_json["isAdminChannel"]
|
|
channel.is_private_channel = channel_json["isPrivateChannel"]
|
|
channel.custom_tag = channel_json["tag"]
|
|
return channel
|
|
|
|
/datum/tgs_api/v4/SecurityLevel()
|
|
return security_level
|