mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 02:34:00 +00:00
Co-authored-by: Heroman3003 <31296024+Heroman3003@users.noreply.github.com> Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> Co-authored-by: Kashargul <KashL@t-online.de> Co-authored-by: Razgriz <razgriz1032@gmail.com>
305 lines
11 KiB
Plaintext
305 lines
11 KiB
Plaintext
/datum/tgs_api/v5/proc/TopicResponse(error_message = null)
|
|
var/list/response = list()
|
|
if(error_message)
|
|
response[DMAPI5_RESPONSE_ERROR_MESSAGE] = error_message
|
|
return response
|
|
|
|
/datum/tgs_api/v5/proc/ProcessTopicJson(json, check_access_identifier)
|
|
TGS_DEBUG_LOG("ProcessTopicJson(..., [check_access_identifier])")
|
|
var/list/result = ProcessRawTopic(json, check_access_identifier)
|
|
if(!result)
|
|
result = TopicResponse("Runtime error!")
|
|
else if(!length(result))
|
|
return "{}" // quirk of json_encode is an empty list returns "[]"
|
|
|
|
var/response_json = json_encode(result)
|
|
if(length(response_json) > DMAPI5_TOPIC_RESPONSE_LIMIT)
|
|
// cache response chunks and send the first
|
|
var/list/chunks = GenerateChunks(response_json, FALSE)
|
|
var/payload_id = chunks[1][DMAPI5_CHUNK][DMAPI5_CHUNK_PAYLOAD_ID]
|
|
var/cache_key = ResponseTopicChunkCacheKey(payload_id)
|
|
|
|
chunked_topics[cache_key] = chunks
|
|
|
|
response_json = json_encode(chunks[1])
|
|
|
|
return response_json
|
|
|
|
/datum/tgs_api/v5/proc/ProcessRawTopic(json, check_access_identifier)
|
|
TGS_DEBUG_LOG("ProcessRawTopic(..., [check_access_identifier])")
|
|
var/list/topic_parameters = json_decode(json)
|
|
if(!topic_parameters)
|
|
TGS_DEBUG_LOG("ProcessRawTopic: json_decode failed")
|
|
return TopicResponse("Invalid topic parameters json: [json]!");
|
|
|
|
var/their_sCK = topic_parameters[DMAPI5_PARAMETER_ACCESS_IDENTIFIER]
|
|
if(check_access_identifier && their_sCK != access_identifier)
|
|
TGS_DEBUG_LOG("ProcessRawTopic: access identifier check failed")
|
|
return TopicResponse("Failed to decode [DMAPI5_PARAMETER_ACCESS_IDENTIFIER] or it does not match!")
|
|
|
|
var/command = topic_parameters[DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE]
|
|
if(!isnum(command))
|
|
TGS_DEBUG_LOG("ProcessRawTopic: command type check failed")
|
|
return TopicResponse("Failed to decode [DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE]!")
|
|
|
|
return ProcessTopicCommand(command, topic_parameters)
|
|
|
|
/datum/tgs_api/v5/proc/ResponseTopicChunkCacheKey(payload_id)
|
|
return "response[payload_id]"
|
|
|
|
/datum/tgs_api/v5/proc/ProcessTopicCommand(command, list/topic_parameters)
|
|
TGS_DEBUG_LOG("ProcessTopicCommand([command], ...)")
|
|
switch(command)
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_CHAT_COMMAND)
|
|
intercepted_message_queue = list()
|
|
var/list/result = HandleCustomCommand(topic_parameters[DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND])
|
|
if(!result)
|
|
result = TopicResponse("Error running chat command!")
|
|
result[DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES] = intercepted_message_queue
|
|
intercepted_message_queue = null
|
|
return result
|
|
|
|
// VOREStation Edit Start - GetChatCommands command
|
|
if(DMAPI5_TOPIC_COMMAND_GET_CHAT_COMMANDS)
|
|
var/topic_response = list(DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands())
|
|
return json_encode(topic_response)
|
|
// VOREStation Edit - End
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_EVENT_NOTIFICATION)
|
|
var/list/event_notification = topic_parameters[DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION]
|
|
if(!istype(event_notification))
|
|
return TopicResponse("Invalid [DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION]!")
|
|
|
|
var/event_type = event_notification[DMAPI5_EVENT_NOTIFICATION_TYPE]
|
|
if(!isnum(event_type))
|
|
return TopicResponse("Invalid or missing [DMAPI5_EVENT_NOTIFICATION_TYPE]!")
|
|
|
|
var/list/event_parameters = event_notification[DMAPI5_EVENT_NOTIFICATION_PARAMETERS]
|
|
if(event_parameters && !istype(event_parameters))
|
|
. = TopicResponse("Invalid or missing [DMAPI5_EVENT_NOTIFICATION_PARAMETERS]!")
|
|
else
|
|
var/list/response = TopicResponse()
|
|
. = response
|
|
if(event_handler != null)
|
|
var/list/event_call = list(event_type)
|
|
if(event_parameters)
|
|
event_call += event_parameters
|
|
|
|
intercepted_message_queue = list()
|
|
event_handler.HandleEvent(arglist(event_call))
|
|
response[DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES] = intercepted_message_queue
|
|
intercepted_message_queue = null
|
|
|
|
if (event_type == TGS_EVENT_WATCHDOG_DETACH)
|
|
detached = TRUE
|
|
chat_channels.Cut() // https://github.com/tgstation/tgstation-server/issues/1490
|
|
|
|
return
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_CHANGE_PORT)
|
|
var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
|
|
if (!isnum(new_port) || !(new_port > 0))
|
|
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]")
|
|
|
|
if(event_handler != null)
|
|
event_handler.HandleEvent(TGS_EVENT_PORT_SWAP, new_port)
|
|
|
|
//the topic still completes, miraculously
|
|
//I honestly didn't believe byond could do it without exploding
|
|
if(!world.OpenPort(new_port))
|
|
return TopicResponse("Port change failed!")
|
|
|
|
return TopicResponse()
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_CHANGE_REBOOT_STATE)
|
|
var/new_reboot_mode = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_REBOOT_STATE]
|
|
if(!isnum(new_reboot_mode))
|
|
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_REBOOT_STATE]!")
|
|
|
|
if(event_handler != null)
|
|
event_handler.HandleEvent(TGS_EVENT_REBOOT_MODE_CHANGE, reboot_mode, new_reboot_mode)
|
|
|
|
reboot_mode = new_reboot_mode
|
|
return TopicResponse()
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_INSTANCE_RENAMED)
|
|
var/new_instance_name = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME]
|
|
if(!istext(new_instance_name))
|
|
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME]!")
|
|
|
|
if(event_handler != null)
|
|
event_handler.HandleEvent(TGS_EVENT_INSTANCE_RENAMED, new_instance_name)
|
|
|
|
instance_name = new_instance_name
|
|
return TopicResponse()
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_CHAT_CHANNELS_UPDATE)
|
|
TGS_DEBUG_LOG("ProcessTopicCommand: It's a chat update")
|
|
var/list/chat_update_json = topic_parameters[DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE]
|
|
if(!istype(chat_update_json))
|
|
TGS_DEBUG_LOG("ProcessTopicCommand: failed \"[DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE]\" check")
|
|
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE]!")
|
|
|
|
DecodeChannels(chat_update_json)
|
|
return TopicResponse()
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE)
|
|
var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
|
|
if (!isnum(new_port) || !(new_port > 0))
|
|
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]")
|
|
|
|
server_port = new_port
|
|
return TopicResponse()
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_HEALTHCHECK)
|
|
if(event_handler && event_handler.receive_health_checks)
|
|
event_handler.HandleEvent(TGS_EVENT_HEALTH_CHECK)
|
|
var/list/health_check_response = TopicResponse()
|
|
health_check_response[DMAPI5_TOPIC_RESPONSE_CLIENT_COUNT] = TGS_CLIENT_COUNT
|
|
return health_check_response;
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH)
|
|
detached = FALSE
|
|
var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
|
|
var/error_message = null
|
|
if (new_port != null)
|
|
if (!isnum(new_port) || !(new_port > 0))
|
|
error_message = "Invalid [DMAPI5_TOPIC_PARAMETER_NEW_PORT]"
|
|
else
|
|
server_port = new_port
|
|
|
|
var/new_version_string = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]
|
|
if (!istext(new_version_string))
|
|
if(error_message != null)
|
|
error_message += ", "
|
|
error_message += "Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]"
|
|
else
|
|
var/datum/tgs_version/new_version = new(new_version_string)
|
|
if (event_handler)
|
|
event_handler.HandleEvent(TGS_EVENT_WATCHDOG_REATTACH, new_version)
|
|
|
|
version = new_version
|
|
|
|
var/list/reattach_response = TopicResponse(error_message)
|
|
reattach_response[DMAPI5_PARAMETER_CUSTOM_COMMANDS] = ListCustomCommands()
|
|
reattach_response[DMAPI5_PARAMETER_TOPIC_PORT] = GetTopicPort()
|
|
|
|
for(var/eventId in pending_events)
|
|
pending_events[eventId] = TRUE
|
|
|
|
return reattach_response
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_SEND_CHUNK)
|
|
var/list/chunk = topic_parameters[DMAPI5_CHUNK]
|
|
if(!istype(chunk))
|
|
return TopicResponse("Invalid [DMAPI5_CHUNK]!")
|
|
|
|
var/payload_id = chunk[DMAPI5_CHUNK_PAYLOAD_ID]
|
|
if(!isnum(payload_id))
|
|
return TopicResponse("[DMAPI5_CHUNK_PAYLOAD_ID] is not a number!")
|
|
|
|
// Always updated the highest known payload ID
|
|
chunked_requests = max(chunked_requests, payload_id)
|
|
|
|
var/sequence_id = chunk[DMAPI5_CHUNK_SEQUENCE_ID]
|
|
if(!isnum(sequence_id))
|
|
return TopicResponse("[DMAPI5_CHUNK_SEQUENCE_ID] is not a number!")
|
|
|
|
var/total_chunks = chunk[DMAPI5_CHUNK_TOTAL]
|
|
if(!isnum(total_chunks))
|
|
return TopicResponse("[DMAPI5_CHUNK_TOTAL] is not a number!")
|
|
|
|
if(total_chunks == 0)
|
|
return TopicResponse("[DMAPI5_CHUNK_TOTAL] is zero!")
|
|
|
|
var/payload = chunk[DMAPI5_CHUNK_PAYLOAD]
|
|
if(!istext(payload))
|
|
return TopicResponse("[DMAPI5_CHUNK_PAYLOAD] is not text!")
|
|
|
|
var/cache_key = "request[payload_id]"
|
|
var/payloads = chunked_topics[cache_key]
|
|
|
|
if(!payloads)
|
|
payloads = new /list(total_chunks)
|
|
chunked_topics[cache_key] = payloads
|
|
|
|
if(total_chunks != length(payloads))
|
|
chunked_topics -= cache_key
|
|
return TopicResponse("Received differing total chunks for same [DMAPI5_CHUNK_PAYLOAD_ID]! Invalidating [DMAPI5_CHUNK_PAYLOAD_ID]!")
|
|
|
|
var/pre_existing_chunk = payloads[sequence_id + 1]
|
|
if(pre_existing_chunk && pre_existing_chunk != payload)
|
|
chunked_topics -= cache_key
|
|
return TopicResponse("Received differing payload for same [DMAPI5_CHUNK_SEQUENCE_ID]! Invalidating [DMAPI5_CHUNK_PAYLOAD_ID]!")
|
|
|
|
payloads[sequence_id + 1] = payload
|
|
|
|
var/list/missing_sequence_ids = list()
|
|
for(var/i in 1 to total_chunks)
|
|
if(!payloads[i])
|
|
missing_sequence_ids += i - 1
|
|
|
|
if(length(missing_sequence_ids))
|
|
return list(DMAPI5_MISSING_CHUNKS = missing_sequence_ids)
|
|
|
|
chunked_topics -= cache_key
|
|
var/full_json = jointext(payloads, "")
|
|
|
|
return ProcessRawTopic(full_json, FALSE)
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK)
|
|
var/payload_id = topic_parameters[DMAPI5_CHUNK_PAYLOAD_ID]
|
|
if(!isnum(payload_id))
|
|
return TopicResponse("[DMAPI5_CHUNK_PAYLOAD_ID] is not a number!")
|
|
|
|
// Always updated the highest known payload ID
|
|
chunked_requests = max(chunked_requests, payload_id)
|
|
|
|
var/list/missing_chunks = topic_parameters[DMAPI5_MISSING_CHUNKS]
|
|
if(!istype(missing_chunks) || !length(missing_chunks))
|
|
return TopicResponse("Missing or empty [DMAPI5_MISSING_CHUNKS]!")
|
|
|
|
var/sequence_id_to_send = missing_chunks[1]
|
|
if(!isnum(sequence_id_to_send))
|
|
return TopicResponse("[DMAPI5_MISSING_CHUNKS] contained a non-number!")
|
|
|
|
var/cache_key = ResponseTopicChunkCacheKey(payload_id)
|
|
var/list/chunks = chunked_topics[cache_key]
|
|
if(!chunks)
|
|
return TopicResponse("Unknown response chunk set: P[payload_id]!")
|
|
|
|
// sequence IDs in interop chunking are always zero indexed
|
|
var/chunk_to_send = chunks[sequence_id_to_send + 1]
|
|
if(!chunk_to_send)
|
|
return TopicResponse("Sequence ID [sequence_id_to_send] is not present in response chunk P[payload_id]!")
|
|
|
|
if(length(missing_chunks) == 1)
|
|
// sending last chunk, purge the cache
|
|
chunked_topics -= cache_key
|
|
|
|
return chunk_to_send
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST)
|
|
var/message = topic_parameters[DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE]
|
|
if (!istext(message))
|
|
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE]")
|
|
|
|
TGS_WORLD_ANNOUNCE(message)
|
|
return TopicResponse()
|
|
|
|
if(DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT)
|
|
var/event_id = topic_parameters[DMAPI5_EVENT_ID]
|
|
if (!istext(event_id))
|
|
return TopicResponse("Invalid or missing [DMAPI5_EVENT_ID]")
|
|
|
|
TGS_DEBUG_LOG("Completing event ID [event_id]...")
|
|
pending_events[event_id] = TRUE
|
|
return TopicResponse()
|
|
|
|
return TopicResponse("Unknown command: [command]")
|
|
|
|
/datum/tgs_api/v5/proc/WorldBroadcast(message)
|
|
set waitfor = FALSE
|
|
TGS_WORLD_ANNOUNCE(message)
|