mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
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
|