Merge branch 'sheepishgoat:master' into feature/jobtitles_fix
This commit is contained in:
+289
-79
@@ -1,19 +1,20 @@
|
||||
// tgstation-server DMAPI
|
||||
// The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in IETF RFC 2119.
|
||||
|
||||
#define TGS_DMAPI_VERSION "6.0.3"
|
||||
#define TGS_DMAPI_VERSION "7.3.0"
|
||||
|
||||
// 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 TGS configuration outside of this file.
|
||||
/// Consumers SHOULD create this define if you want to do TGS configuration outside of this file.
|
||||
#ifndef TGS_EXTERNAL_CONFIGURATION
|
||||
|
||||
// Comment this out once you've filled in the below.
|
||||
// Consumers MUST comment this out once you've filled in the below and are not using [TGS_EXTERNAL_CONFIGURATION].
|
||||
#error TGS API unconfigured
|
||||
|
||||
// Uncomment this if you wish to allow the game to interact with TGS 3.
|
||||
// This will raise the minimum required security level of your game to TGS_SECURITY_TRUSTED due to it utilizing call()()
|
||||
// Consumers MUST uncomment this if you wish to allow the game to interact with TGS version 3.
|
||||
// This will raise the minimum required security level of your game to TGS_SECURITY_TRUSTED due to it utilizing call()().
|
||||
//#define TGS_V3_API
|
||||
|
||||
// Required interfaces (fill in with your codebase equivalent):
|
||||
@@ -50,46 +51,55 @@
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef TGS_FILE2TEXT_NATIVE
|
||||
#ifdef file2text
|
||||
#error Your codebase is re-defining the BYOND proc file2text. The DMAPI requires the native version to read the result of world.Export(). You SHOULD fix this by adding "#define TGS_FILE2TEXT_NATIVE file2text" before your override of file2text to allow the DMAPI to use the native version. This will only be used for world.Export(), not regular file accesses
|
||||
#endif
|
||||
#define TGS_FILE2TEXT_NATIVE file2text
|
||||
#endif
|
||||
|
||||
// EVENT CODES
|
||||
|
||||
/// Before a reboot mode change, extras parameters are the current and new reboot mode enums
|
||||
/// Before a reboot mode change, extras parameters are the current and new reboot mode enums.
|
||||
#define TGS_EVENT_REBOOT_MODE_CHANGE -1
|
||||
/// Before a port change is about to happen, extra parameters is new port
|
||||
/// Before a port change is about to happen, extra parameters is new port.
|
||||
#define TGS_EVENT_PORT_SWAP -2
|
||||
/// Before the instance is renamed, extra parameter is the new name
|
||||
/// Before the instance is renamed, extra parameter is the new name.
|
||||
#define TGS_EVENT_INSTANCE_RENAMED -3
|
||||
/// After the watchdog reattaches to DD, extra parameter is the new [/datum/tgs_version] of the server
|
||||
/// After the watchdog reattaches to DD, extra parameter is the new [/datum/tgs_version] of the server.
|
||||
#define TGS_EVENT_WATCHDOG_REATTACH -4
|
||||
/// When the watchdog sends a health check to DD. No parameters.
|
||||
#define TGS_EVENT_HEALTH_CHECK -5
|
||||
|
||||
/// When the repository is reset to its origin reference. Parameters: Reference name, Commit SHA
|
||||
/// When the repository is reset to its origin reference. Parameters: Reference name, Commit SHA.
|
||||
#define TGS_EVENT_REPO_RESET_ORIGIN 0
|
||||
/// When the repository performs a checkout. Parameters: Checkout git object
|
||||
/// When the repository performs a checkout. Parameters: Checkout git object.
|
||||
#define TGS_EVENT_REPO_CHECKOUT 1
|
||||
/// When the repository performs a fetch operation. No parameters
|
||||
/// When the repository performs a fetch operation. No parameters.
|
||||
#define TGS_EVENT_REPO_FETCH 2
|
||||
/// When the repository test merges. Parameters: PR Number, PR Sha, (Nullable) Comment made by TGS user
|
||||
/// When the repository test merges. Parameters: PR Number, PR Sha, (Nullable) Comment made by TGS user.
|
||||
#define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3
|
||||
/// Before the repository makes a sychronize operation. Parameters: Absolute repostiory path
|
||||
/// Before the repository makes a sychronize operation. Parameters: Absolute repostiory path.
|
||||
#define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4
|
||||
/// Before a BYOND install operation begins. Parameters: [/datum/tgs_version] of the installing BYOND
|
||||
#define TGS_EVENT_BYOND_INSTALL_START 5
|
||||
/// When a BYOND install operation fails. Parameters: Error message
|
||||
#define TGS_EVENT_BYOND_INSTALL_FAIL 6
|
||||
/// When the active BYOND version changes. Parameters: (Nullable) [/datum/tgs_version] of the current BYOND, [/datum/tgs_version] of the new BYOND
|
||||
#define TGS_EVENT_BYOND_ACTIVE_VERSION_CHANGE 7
|
||||
/// When the compiler starts running. Parameters: Game directory path, origin commit SHA
|
||||
/// Before a engine install operation begins. Parameters: Version string of the installing engine.
|
||||
#define TGS_EVENT_ENGINE_INSTALL_START 5
|
||||
/// When a engine install operation fails. Parameters: Error message
|
||||
#define TGS_EVENT_ENGINE_INSTALL_FAIL 6
|
||||
/// When the active engine version changes. Parameters: (Nullable) Version string of the current engine, version string of the new engine.
|
||||
#define TGS_EVENT_ENGINE_ACTIVE_VERSION_CHANGE 7
|
||||
/// When the compiler starts running. Parameters: Game directory path, origin commit SHA.
|
||||
#define TGS_EVENT_COMPILE_START 8
|
||||
/// When a compile is cancelled. No parameters
|
||||
/// When a compile is cancelled. No parameters.
|
||||
#define TGS_EVENT_COMPILE_CANCELLED 9
|
||||
/// When a compile fails. Parameters: Game directory path, [TRUE]/[FALSE] based on if the cause for failure was DMAPI validation
|
||||
/// When a compile fails. Parameters: Game directory path, [TRUE]/[FALSE] based on if the cause for failure was DMAPI validation.
|
||||
#define TGS_EVENT_COMPILE_FAILURE 10
|
||||
/// When a compile operation completes. Note, this event fires before the new .dmb is loaded into the watchdog. Consider using the [TGS_EVENT_DEPLOYMENT_COMPLETE] instead. Parameters: Game directory path
|
||||
/// When a compile operation completes. Note, this event fires before the new .dmb is loaded into the watchdog. Consider using the [TGS_EVENT_DEPLOYMENT_COMPLETE] instead. Parameters: Game directory path.
|
||||
#define TGS_EVENT_COMPILE_COMPLETE 11
|
||||
/// When an automatic update for the current instance begins. No parameters
|
||||
/// When an automatic update for the current instance begins. No parameters.
|
||||
#define TGS_EVENT_INSTANCE_AUTO_UPDATE_START 12
|
||||
/// When the repository encounters a merge conflict: Parameters: Base SHA, target SHA, base reference, target reference
|
||||
/// When the repository encounters a merge conflict: Parameters: Base SHA, target SHA, base reference, target reference.
|
||||
#define TGS_EVENT_REPO_MERGE_CONFLICT 13
|
||||
/// When a deployment completes. No Parameters
|
||||
/// When a deployment completes. No Parameters.
|
||||
#define TGS_EVENT_DEPLOYMENT_COMPLETE 14
|
||||
/// Before the watchdog shuts down. Not sent for graceful shutdowns. No parameters.
|
||||
#define TGS_EVENT_WATCHDOG_SHUTDOWN 15
|
||||
@@ -102,6 +112,14 @@
|
||||
// #define TGS_EVENT_WORLD_REBOOT 20
|
||||
/// Watchdog event when TgsInitializationComplete() is called. No parameters.
|
||||
#define TGS_EVENT_WORLD_PRIME 21
|
||||
// DMAPI also doesnt implement this
|
||||
// #define TGS_EVENT_DREAM_DAEMON_LAUNCH 22
|
||||
/// After a single submodule update is performed. Parameters: Updated submodule name.
|
||||
#define TGS_EVENT_REPO_SUBMODULE_UPDATE 23
|
||||
/// After CodeModifications are applied, before DreamMaker is run. Parameters: Game directory path, origin commit sha, version string of the used engine.
|
||||
#define TGS_EVENT_PRE_DREAM_MAKER 24
|
||||
/// Whenever a deployment folder is deleted from disk. Parameters: Game directory path.
|
||||
#define TGS_EVENT_DEPLOYMENT_CLEANUP 25
|
||||
|
||||
// OTHER ENUMS
|
||||
|
||||
@@ -112,6 +130,7 @@
|
||||
/// The watchdog will restart on reboot.
|
||||
#define TGS_REBOOT_MODE_RESTART 2
|
||||
|
||||
// Note that security levels are currently meaningless in OpenDream
|
||||
/// DreamDaemon Trusted security level.
|
||||
#define TGS_SECURITY_TRUSTED 0
|
||||
/// DreamDaemon Safe security level.
|
||||
@@ -119,19 +138,32 @@
|
||||
/// DreamDaemon Ultrasafe security level.
|
||||
#define TGS_SECURITY_ULTRASAFE 2
|
||||
|
||||
/// DreamDaemon public visibility level.
|
||||
#define TGS_VISIBILITY_PUBLIC 0
|
||||
/// DreamDaemon private visibility level.
|
||||
#define TGS_VISIBILITY_PRIVATE 1
|
||||
/// DreamDaemon invisible visibility level.
|
||||
#define TGS_VISIBILITY_INVISIBLE 2
|
||||
|
||||
/// The Build Your Own Net Dream engine.
|
||||
#define TGS_ENGINE_TYPE_BYOND 0
|
||||
/// The OpenDream engine.
|
||||
#define TGS_ENGINE_TYPE_OPENDREAM 1
|
||||
|
||||
//REQUIRED HOOKS
|
||||
|
||||
/**
|
||||
* Call this somewhere in [/world/proc/New] that is always run. This function may sleep!
|
||||
* Consumers MUST call this somewhere in [/world/proc/New] that is always run. This function may sleep!
|
||||
*
|
||||
* * event_handler - Optional user defined [/datum/tgs_event_handler].
|
||||
* * minimum_required_security_level: The minimum required security level to run the game in which the DMAPI is integrated. Can be one of [TGS_SECURITY_ULTRASAFE], [TGS_SECURITY_SAFE], or [TGS_SECURITY_TRUSTED].
|
||||
* * http_handler - Optional user defined [/datum/tgs_http_handler].
|
||||
*/
|
||||
/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
|
||||
/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE, datum/tgs_http_handler/http_handler)
|
||||
return
|
||||
|
||||
/**
|
||||
* Call this when your initializations are complete and your game is ready to play before any player interactions happen.
|
||||
* Consumers MUST call this when world initializations are complete and the game is ready to play before any player interactions happen.
|
||||
*
|
||||
* This may use [/world/var/sleep_offline] to make this happen so ensure no changes are made to it while this call is running.
|
||||
* Afterwards, consider explicitly setting it to what you want to avoid this BYOND bug: http://www.byond.com/forum/post/2575184
|
||||
@@ -140,12 +172,10 @@
|
||||
/world/proc/TgsInitializationComplete()
|
||||
return
|
||||
|
||||
/// Put this at the start of [/world/proc/Topic].
|
||||
/// Consumers MUST run this macro at the start of [/world/proc/Topic].
|
||||
#define TGS_TOPIC var/tgs_topic_return = TgsTopic(args[1]); if(tgs_topic_return) return tgs_topic_return
|
||||
|
||||
/**
|
||||
* Call this as late as possible in [world/proc/Reboot].
|
||||
*/
|
||||
/// Consumers MUST call this as late as possible in [world/proc/Reboot] (BEFORE ..()).
|
||||
/world/proc/TgsReboot()
|
||||
return
|
||||
|
||||
@@ -156,28 +186,28 @@
|
||||
/datum/tgs_revision_information
|
||||
/// Full SHA of the commit.
|
||||
var/commit
|
||||
/// ISO 8601 timestamp of when the commit was created
|
||||
/// ISO 8601 timestamp of when the commit was created.
|
||||
var/timestamp
|
||||
/// Full sha of last known remote commit. This may be null if the TGS repository is not currently tracking a remote branch.
|
||||
var/origin_commit
|
||||
|
||||
/// Represents a version.
|
||||
/datum/tgs_version
|
||||
/// The suite/major version number
|
||||
/// The suite/major version number.
|
||||
var/suite
|
||||
|
||||
// This group of variables can be null to represent a wild card
|
||||
/// The minor version number. null for wildcards
|
||||
// This group of variables can be null to represent a wild card.
|
||||
/// The minor version number. null for wildcards.
|
||||
var/minor
|
||||
/// The patch version number. null for wildcards
|
||||
/// The patch version number. null for wildcards.
|
||||
var/patch
|
||||
|
||||
/// Legacy version number. Generally null
|
||||
/// Legacy version number. Generally null.
|
||||
var/deprecated_patch
|
||||
|
||||
/// Unparsed string value
|
||||
/// Unparsed string value.
|
||||
var/raw_parameter
|
||||
/// String value minus prefix
|
||||
/// String value minus prefix.
|
||||
var/deprefixed_parameter
|
||||
|
||||
/**
|
||||
@@ -223,47 +253,208 @@
|
||||
var/is_admin_channel
|
||||
/// [TRUE]/[FALSE] if the channel is a private message channel for a [/datum/tgs_chat_user].
|
||||
var/is_private_channel
|
||||
/// Tag string associated with the channel in TGS
|
||||
/// Tag string associated with the channel in TGS.
|
||||
var/custom_tag
|
||||
/// [TRUE]/[FALSE] if the channel supports embeds.
|
||||
var/embeds_supported
|
||||
|
||||
// Represents a chat user
|
||||
/datum/tgs_chat_user
|
||||
/// TGS internal user ID.
|
||||
var/id
|
||||
// The user's display name.
|
||||
/// The user's display name.
|
||||
var/friendly_name
|
||||
// The string to use to ping this user in a message.
|
||||
/// The string to use to ping this user in a message.
|
||||
var/mention
|
||||
/// The [/datum/tgs_chat_channel] the user was from
|
||||
/// The [/datum/tgs_chat_channel] the user was from.
|
||||
var/datum/tgs_chat_channel/channel
|
||||
|
||||
/// User definable handler for TGS events This abstract version SHOULD be overridden to be used.
|
||||
/datum/tgs_event_handler
|
||||
/// If the handler receieves [TGS_EVENT_HEALTH_CHECK] events.
|
||||
var/receive_health_checks = FALSE
|
||||
|
||||
/**
|
||||
* User definable callback for handling TGS events.
|
||||
*
|
||||
* event_code - One of the TGS_EVENT_ defines. Extra parameters will be documented in each
|
||||
* event_code - One of the TGS_EVENT_ defines. Extra parameters will be documented in each.
|
||||
*/
|
||||
/datum/tgs_event_handler/proc/HandleEvent(event_code, ...)
|
||||
set waitfor = FALSE
|
||||
return
|
||||
|
||||
/// User definable chat command
|
||||
/datum/tgs_chat_command
|
||||
/// The string to trigger this command on a chat bot. e.g `@bot name ...` or `!tgs name ...`
|
||||
var/name = ""
|
||||
/// The help text displayed for this command
|
||||
var/help_text = ""
|
||||
/// If this command should be available to game administrators only
|
||||
var/admin_only = FALSE
|
||||
/// User definable handler for HTTP calls. This abstract version MUST be overridden to be used.
|
||||
/datum/tgs_http_handler
|
||||
|
||||
/**
|
||||
* Process command activation. Should return a string to respond to the issuer with.
|
||||
* User definable callback for executing HTTP GET requests.
|
||||
* MUST perform BYOND sleeps while the request is in flight.
|
||||
* MUST return a [/datum/tgs_http_result].
|
||||
* SHOULD log its own errors
|
||||
*
|
||||
* sender - The [/datum/tgs_chat_user] who issued the command.
|
||||
* params - The trimmed string following the command `/datum/tgs_chat_command/var/name].
|
||||
* url - The full URL to execute the GET request for including query parameters.
|
||||
*/
|
||||
/datum/tgs_http_handler/proc/PerformGet(url)
|
||||
CRASH("[type]/PerformGet not implemented!")
|
||||
|
||||
/// Result of a [/datum/tgs_http_handler] call. MUST NOT be overridden.
|
||||
/datum/tgs_http_result
|
||||
/// HTTP response as text
|
||||
var/response_text
|
||||
/// Boolean request success flag. Set for any 2XX response code.
|
||||
var/success
|
||||
|
||||
/**
|
||||
* Create a [/datum/tgs_http_result].
|
||||
*
|
||||
* * response_text - HTTP response as text. Must be provided in New().
|
||||
* * success - Boolean request success flag. Set for any 2XX response code. Must be provided in New().
|
||||
*/
|
||||
/datum/tgs_http_result/New(response_text, success)
|
||||
if(response_text && !istext(response_text))
|
||||
CRASH("response_text was not text!")
|
||||
|
||||
src.response_text = response_text
|
||||
src.success = success
|
||||
|
||||
/// User definable chat command. This abstract version MUST be overridden to be used.
|
||||
/datum/tgs_chat_command
|
||||
/// The string to trigger this command on a chat bot. e.g `@bot name ...` or `!tgs name ...`.
|
||||
var/name = ""
|
||||
/// The help text displayed for this command.
|
||||
var/help_text = ""
|
||||
/// If this command should be available to game administrators only.
|
||||
var/admin_only = FALSE
|
||||
/// A subtype of [/datum/tgs_chat_command] that is ignored when enumerating available commands. Use this to create shared base /datums for commands.
|
||||
var/ignore_type
|
||||
|
||||
/**
|
||||
* Process command activation. Should return a [/datum/tgs_message_content] to respond to the issuer with.
|
||||
* MUST be implemented
|
||||
*
|
||||
* * sender - The [/datum/tgs_chat_user] who issued the command.
|
||||
* * params - The trimmed string following the command `/datum/tgs_chat_command/var/name].
|
||||
*/
|
||||
/datum/tgs_chat_command/proc/Run(datum/tgs_chat_user/sender, params)
|
||||
CRASH("[type] has no implementation for Run()")
|
||||
|
||||
/// User definable chat message. MUST NOT be overridden.
|
||||
/datum/tgs_message_content
|
||||
/// The string content of the message. Must be provided in New().
|
||||
var/text
|
||||
|
||||
/// The [/datum/tgs_chat_embed] to embed in the message. Not supported on all chat providers.
|
||||
var/datum/tgs_chat_embed/structure/embed
|
||||
|
||||
/**
|
||||
* Create a [/datum/tgs_message_content].
|
||||
*
|
||||
* * text - The string content of the message.
|
||||
*/
|
||||
/datum/tgs_message_content/New(text)
|
||||
..()
|
||||
if(!istext(text))
|
||||
TGS_ERROR_LOG("[/datum/tgs_message_content] created with no text!")
|
||||
text = null
|
||||
|
||||
src.text = text
|
||||
|
||||
/// User definable chat embed. Currently mirrors Discord chat embeds. See https://discord.com/developers/docs/resources/message#embed-object for details.
|
||||
/datum/tgs_chat_embed/structure
|
||||
var/title
|
||||
var/description
|
||||
var/url
|
||||
|
||||
/// Timestamp must be encoded as: time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss"). Use the active timezone.
|
||||
var/timestamp
|
||||
|
||||
/// Colour must be #AARRGGBB or #RRGGBB hex string.
|
||||
var/colour
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-image-structure for details.
|
||||
var/datum/tgs_chat_embed/media/image
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-thumbnail-structure for details.
|
||||
var/datum/tgs_chat_embed/media/thumbnail
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-video-structure for details.
|
||||
var/datum/tgs_chat_embed/media/video
|
||||
|
||||
var/datum/tgs_chat_embed/footer/footer
|
||||
var/datum/tgs_chat_embed/provider/provider
|
||||
var/datum/tgs_chat_embed/provider/author/author
|
||||
|
||||
var/list/datum/tgs_chat_embed/field/fields
|
||||
|
||||
/// Common datum for similar Discord embed medias.
|
||||
/datum/tgs_chat_embed/media
|
||||
/// Must be set in New().
|
||||
var/url
|
||||
var/width
|
||||
var/height
|
||||
var/proxy_url
|
||||
|
||||
/// Create a [/datum/tgs_chat_embed].
|
||||
/datum/tgs_chat_embed/media/New(url)
|
||||
..()
|
||||
if(!istext(url))
|
||||
CRASH("[/datum/tgs_chat_embed/media] created with no url!")
|
||||
|
||||
src.url = url
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-footer-structure for details.
|
||||
/datum/tgs_chat_embed/footer
|
||||
/// Must be set in New().
|
||||
var/text
|
||||
var/icon_url
|
||||
var/proxy_icon_url
|
||||
|
||||
/// Create a [/datum/tgs_chat_embed/footer].
|
||||
/datum/tgs_chat_embed/footer/New(text)
|
||||
..()
|
||||
if(!istext(text))
|
||||
CRASH("[/datum/tgs_chat_embed/footer] created with no text!")
|
||||
|
||||
src.text = text
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-provider-structure for details.
|
||||
/datum/tgs_chat_embed/provider
|
||||
var/name
|
||||
var/url
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-author-structure for details. Must have name set in New().
|
||||
/datum/tgs_chat_embed/provider/author
|
||||
var/icon_url
|
||||
var/proxy_icon_url
|
||||
|
||||
/// Create a [/datum/tgs_chat_embed/footer].
|
||||
/datum/tgs_chat_embed/provider/author/New(name)
|
||||
..()
|
||||
if(!istext(name))
|
||||
CRASH("[/datum/tgs_chat_embed/provider/author] created with no name!")
|
||||
|
||||
src.name = name
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-field-structure for details.
|
||||
/datum/tgs_chat_embed/field
|
||||
/// Must be set in New().
|
||||
var/name
|
||||
/// Must be set in New().
|
||||
var/value
|
||||
var/is_inline
|
||||
|
||||
/// Create a [/datum/tgs_chat_embed/field].
|
||||
/datum/tgs_chat_embed/field/New(name, value)
|
||||
..()
|
||||
if(!istext(name))
|
||||
CRASH("[/datum/tgs_chat_embed/field] created with no name!")
|
||||
|
||||
if(!istext(value))
|
||||
CRASH("[/datum/tgs_chat_embed/field] created with no value!")
|
||||
|
||||
src.name = name
|
||||
src.value = value
|
||||
|
||||
// API FUNCTIONS
|
||||
|
||||
/// Returns the maximum supported [/datum/tgs_version] of the DMAPI.
|
||||
@@ -283,75 +474,94 @@
|
||||
// No function below this succeeds if it TgsAvailable() returns FALSE or if TgsNew() has yet to be called.
|
||||
|
||||
/**
|
||||
* Forces a hard reboot of DreamDaemon by ending the process.
|
||||
* Forces a hard reboot of DreamDaemon by ending the process. This function may sleep!
|
||||
*
|
||||
* Unlike del(world) clients will try to reconnect.
|
||||
* If TGS has not requested a [TGS_REBOOT_MODE_SHUTDOWN] DreamDaemon will be launched again
|
||||
* If TGS has not requested a [TGS_REBOOT_MODE_SHUTDOWN] DreamDaemon will be launched again.
|
||||
*/
|
||||
/world/proc/TgsEndProcess()
|
||||
return
|
||||
|
||||
/**
|
||||
* Send a message to connected chats.
|
||||
* Send a message to connected chats. This function may sleep!
|
||||
* If TGS is offline when called, the message may be placed in a queue to be sent and this function will return immediately. Your message will be sent when TGS reconnects to the game.
|
||||
*
|
||||
* message - The string to send.
|
||||
* message - The [/datum/tgs_message_content] to send.
|
||||
* admin_only: If [TRUE], message will be sent to admin connected chats. Vice-versa applies.
|
||||
*/
|
||||
/world/proc/TgsTargetedChatBroadcast(message, admin_only = FALSE)
|
||||
/world/proc/TgsTargetedChatBroadcast(datum/tgs_message_content/message, admin_only = FALSE)
|
||||
return
|
||||
|
||||
/**
|
||||
* Send a private message to a specific user.
|
||||
* Send a private message to a specific user. This function may sleep!
|
||||
* If TGS is offline when called, the message may be placed in a queue to be sent and this function will return immediately. Your message will be sent when TGS reconnects to the game.
|
||||
*
|
||||
* message - The string to send.
|
||||
* message - The [/datum/tgs_message_content] to send.
|
||||
* user: The [/datum/tgs_chat_user] to PM.
|
||||
*/
|
||||
/world/proc/TgsChatPrivateMessage(message, datum/tgs_chat_user/user)
|
||||
/world/proc/TgsChatPrivateMessage(datum/tgs_message_content/message, datum/tgs_chat_user/user)
|
||||
return
|
||||
|
||||
// The following functions will sleep if a call to TgsNew() is sleeping
|
||||
|
||||
/**
|
||||
* Send a message to connected chats that are flagged as game-related in TGS.
|
||||
* Send a message to connected chats that are flagged as game-related in TGS. This function may sleep!
|
||||
* If TGS is offline when called, the message may be placed in a queue to be sent and this function will return immediately. Your message will be sent when TGS reconnects to the game.
|
||||
*
|
||||
* message - The string to send.
|
||||
* message - The [/datum/tgs_message_content] to send.
|
||||
* channels - Optional list of [/datum/tgs_chat_channel]s to restrict the message to.
|
||||
*/
|
||||
/world/proc/TgsChatBroadcast(message, list/channels = null)
|
||||
/world/proc/TgsChatBroadcast(datum/tgs_message_content/message, list/channels = null)
|
||||
return
|
||||
|
||||
/// Returns the current [/datum/tgs_version] of TGS if it is running the server, null otherwise.
|
||||
/// Returns the current [/datum/tgs_version] of TGS if it is running the server, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
|
||||
/world/proc/TgsVersion()
|
||||
return
|
||||
|
||||
/// Returns the current [/datum/tgs_version] of the DMAPI being used if it was activated, null otherwise.
|
||||
/// Returns the running engine type
|
||||
/world/proc/TgsEngine()
|
||||
return
|
||||
|
||||
/// Returns the current [/datum/tgs_version] of the DMAPI being used if it was activated, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
|
||||
/world/proc/TgsApiVersion()
|
||||
return
|
||||
|
||||
/// Returns the name of the TGS instance running the game if TGS is present, null otherwise.
|
||||
/// Returns the name of the TGS instance running the game if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
|
||||
/world/proc/TgsInstanceName()
|
||||
return
|
||||
|
||||
/// Return the current [/datum/tgs_revision_information] of the running server if TGS is present, null otherwise.
|
||||
/// Return the current [/datum/tgs_revision_information] of the running server if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
|
||||
/world/proc/TgsRevision()
|
||||
return
|
||||
|
||||
/// Returns the current BYOND security level as a TGS_SECURITY_ define if TGS is present, null otherwise.
|
||||
/// Returns the current BYOND security level as a TGS_SECURITY_ define if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
|
||||
/world/proc/TgsSecurityLevel()
|
||||
return
|
||||
|
||||
/// Returns a list of active [/datum/tgs_revision_information/test_merge]s if TGS is present, null otherwise.
|
||||
/// Returns the current BYOND visibility level as a TGS_VISIBILITY_ define if TGS is present, null otherwise. Requires TGS to be using interop API version 5 or higher otherwise the string "___unimplemented" wil be returned. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
|
||||
/world/proc/TgsVisibility()
|
||||
return
|
||||
|
||||
/// Returns a list of active [/datum/tgs_revision_information/test_merge]s if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
|
||||
/world/proc/TgsTestMerges()
|
||||
return
|
||||
|
||||
/// Returns a list of connected [/datum/tgs_chat_channel]s if TGS is present, null otherwise.
|
||||
/// Returns a list of connected [/datum/tgs_chat_channel]s if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
|
||||
/world/proc/TgsChatChannelInfo()
|
||||
return
|
||||
|
||||
/**
|
||||
* Trigger an event in TGS. Requires TGS version >= 6.3.0. Returns [TRUE] if the event was triggered successfully, [FALSE] otherwise. This function may sleep!
|
||||
*
|
||||
* event_name - The name of the event to trigger
|
||||
* parameters - Optional list of string parameters to pass as arguments to the event script. The first parameter passed to a script will always be the running game's directory followed by these parameters.
|
||||
* wait_for_completion - If set, this function will not return until the event has run to completion.
|
||||
*/
|
||||
/world/proc/TgsTriggerEvent(event_name, list/parameters, wait_for_completion = FALSE)
|
||||
return
|
||||
|
||||
/*
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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?")
|
||||
@@ -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]")
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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, ".")
|
||||
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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!")
|
||||
@@ -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
|
||||
LIBCALL(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
|
||||
#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)
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -226,3 +240,5 @@
|
||||
#undef SERVICE_REQUEST_API_VERSION
|
||||
|
||||
#undef SERVICE_RETURN_SUCCESS
|
||||
|
||||
#undef TGS_FILE2LIST
|
||||
|
||||
@@ -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])
|
||||
@@ -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
|
||||
|
||||
+27
-14
@@ -73,7 +73,7 @@
|
||||
if(cached_json["apiValidateOnly"])
|
||||
TGS_INFO_LOG("Validating API and exiting...")
|
||||
Export(TGS4_COMM_VALIDATE, list(TGS4_PARAMETER_DATA = "[minimum_required_security_level]"))
|
||||
del(world)
|
||||
TerminateWorld()
|
||||
|
||||
security_level = cached_json["securityLevel"]
|
||||
chat_channels_json_path = cached_json["chatChannelsJson"]
|
||||
@@ -181,14 +181,14 @@
|
||||
var/json = json_encode(data)
|
||||
|
||||
while(requesting_new_port && !override_requesting_new_port)
|
||||
sleep(1)
|
||||
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]")
|
||||
del(world)
|
||||
TerminateWorld()
|
||||
|
||||
//request a new port
|
||||
export_lock = FALSE
|
||||
@@ -196,20 +196,20 @@
|
||||
|
||||
if(!new_port_json)
|
||||
TGS_ERROR_LOG("No new port response from server![TGS4_PORT_CRITFAIL_MESSAGE]")
|
||||
del(world)
|
||||
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]")
|
||||
del(world)
|
||||
TerminateWorld()
|
||||
|
||||
if(new_port != world.port && !world.OpenPort(new_port))
|
||||
TGS_ERROR_LOG("Unable to open port [new_port]![TGS4_PORT_CRITFAIL_MESSAGE]")
|
||||
del(world)
|
||||
TerminateWorld()
|
||||
requesting_new_port = FALSE
|
||||
|
||||
while(export_lock)
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
export_lock = TRUE
|
||||
|
||||
last_interop_response = null
|
||||
@@ -217,7 +217,7 @@
|
||||
text2file(json, server_commands_json_path)
|
||||
|
||||
for(var/I = 0; I < EXPORT_TIMEOUT_DS && !last_interop_response; ++I)
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
|
||||
if(!last_interop_response)
|
||||
TGS_ERROR_LOG("Failed to get export result for: [json]")
|
||||
@@ -256,33 +256,46 @@
|
||||
/datum/tgs_api/v4/Revision()
|
||||
return cached_revision
|
||||
|
||||
/datum/tgs_api/v4/ChatBroadcast(message, list/channels)
|
||||
/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 = list("message" = message, "channelIds" = ids)
|
||||
|
||||
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(message, admin_only)
|
||||
/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 = list("message" = message, "channelIds" = channels)
|
||||
|
||||
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(message, datum/tgs_chat_user/user)
|
||||
message = list("message" = message, "channelIds" = list(user.channel.id))
|
||||
/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
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
custom_commands = list()
|
||||
for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
|
||||
var/datum/tgs_chat_command/stc = new I
|
||||
if(stc.ignore_type == I)
|
||||
continue
|
||||
|
||||
var/command_name = stc.name
|
||||
if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
|
||||
TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!")
|
||||
@@ -34,8 +37,8 @@
|
||||
|
||||
var/datum/tgs_chat_command/sc = custom_commands[command]
|
||||
if(sc)
|
||||
var/result = sc.Run(u, params)
|
||||
if(result == null)
|
||||
result = ""
|
||||
return result
|
||||
var/datum/tgs_message_content/result = sc.Run(u, params)
|
||||
result = UpgradeDeprecatedCommandResponse(result, command)
|
||||
|
||||
return result ? result.text : TRUE
|
||||
return "Unknown command: [command]!"
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
|
||||
This DMAPI implements bridge requests using HTTP GET requests to TGS. It has no security restrictions.
|
||||
|
||||
- [__interop_version.dm](./__interop_version.dm) contains the version of the API used between the DMAPI and TGS.
|
||||
- [_defines.dm](./_defines.dm) contains constant definitions.
|
||||
- [api.dm](./api.dm) contains the bulk of the API code.
|
||||
- [bridge.dm](./bridge.dm) contains functions related to making bridge requests.
|
||||
- [chunking.dm](./chunking.dm) contains common function for splitting large raw data sets into chunks BYOND can natively process.
|
||||
- [commands.dm](./commands.dm) contains functions relating to `/datum/tgs_chat_command`s.
|
||||
- [serializers.dm](./serializers.dm) contains function to help convert interop `/datum`s into a JSON encodable `list()` format.
|
||||
- [topic.dm](./topic.dm) contains functions related to processing topic requests.
|
||||
- [undefs.dm](./undefs.dm) Undoes the work of `_defines.dm`.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"5.10.0"
|
||||
@@ -4,15 +4,29 @@
|
||||
#define DMAPI5_BRIDGE_DATA "data"
|
||||
#define DMAPI5_TOPIC_DATA "tgs_data"
|
||||
|
||||
#define DMAPI5_BRIDGE_COMMAND_PORT_UPDATE 0
|
||||
#define DMAPI5_BRIDGE_REQUEST_LIMIT 8198
|
||||
#define DMAPI5_TOPIC_REQUEST_LIMIT 65528
|
||||
#define DMAPI5_TOPIC_RESPONSE_LIMIT 65529
|
||||
|
||||
#define DMAPI5_BRIDGE_COMMAND_STARTUP 1
|
||||
#define DMAPI5_BRIDGE_COMMAND_PRIME 2
|
||||
#define DMAPI5_BRIDGE_COMMAND_REBOOT 3
|
||||
#define DMAPI5_BRIDGE_COMMAND_KILL 4
|
||||
#define DMAPI5_BRIDGE_COMMAND_CHAT_SEND 5
|
||||
#define DMAPI5_BRIDGE_COMMAND_CHUNK 6
|
||||
#define DMAPI5_BRIDGE_COMMAND_EVENT 7
|
||||
|
||||
#define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier"
|
||||
#define DMAPI5_PARAMETER_CUSTOM_COMMANDS "customCommands"
|
||||
#define DMAPI5_PARAMETER_TOPIC_PORT "topicPort"
|
||||
|
||||
#define DMAPI5_CHUNK "chunk"
|
||||
#define DMAPI5_CHUNK_PAYLOAD "payload"
|
||||
#define DMAPI5_CHUNK_TOTAL "totalChunks"
|
||||
#define DMAPI5_CHUNK_SEQUENCE_ID "sequenceId"
|
||||
#define DMAPI5_CHUNK_PAYLOAD_ID "payloadId"
|
||||
|
||||
#define DMAPI5_MISSING_CHUNKS "missingChunks"
|
||||
|
||||
#define DMAPI5_RESPONSE_ERROR_MESSAGE "errorMessage"
|
||||
|
||||
@@ -21,11 +35,11 @@
|
||||
#define DMAPI5_BRIDGE_PARAMETER_VERSION "version"
|
||||
#define DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE "chatMessage"
|
||||
#define DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL "minimumSecurityLevel"
|
||||
#define DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION "eventInvocation"
|
||||
|
||||
#define DMAPI5_BRIDGE_RESPONSE_NEW_PORT "newPort"
|
||||
#define DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION "runtimeInformation"
|
||||
|
||||
#define DMAPI5_CHAT_MESSAGE_TEXT "text"
|
||||
#define DMAPI5_CHAT_MESSAGE_CHANNEL_IDS "channelIds"
|
||||
|
||||
#define DMAPI5_RUNTIME_INFORMATION_ACCESS_IDENTIFIER "accessIdentifier"
|
||||
@@ -36,6 +50,7 @@
|
||||
#define DMAPI5_RUNTIME_INFORMATION_REVISION "revision"
|
||||
#define DMAPI5_RUNTIME_INFORMATION_TEST_MERGES "testMerges"
|
||||
#define DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL "securityLevel"
|
||||
#define DMAPI5_RUNTIME_INFORMATION_VISIBILITY "visibility"
|
||||
|
||||
#define DMAPI5_CHAT_UPDATE_CHANNELS "channels"
|
||||
|
||||
@@ -63,8 +78,12 @@
|
||||
#define DMAPI5_TOPIC_COMMAND_INSTANCE_RENAMED 4
|
||||
#define DMAPI5_TOPIC_COMMAND_CHAT_CHANNELS_UPDATE 5
|
||||
#define DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE 6
|
||||
#define DMAPI5_TOPIC_COMMAND_HEARTBEAT 7
|
||||
#define DMAPI5_TOPIC_COMMAND_HEALTHCHECK 7
|
||||
#define DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH 8
|
||||
#define DMAPI5_TOPIC_COMMAND_SEND_CHUNK 9
|
||||
#define DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK 10
|
||||
#define DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST 11
|
||||
#define DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT 12
|
||||
|
||||
#define DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE "commandType"
|
||||
#define DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND "chatCommand"
|
||||
@@ -74,7 +93,10 @@
|
||||
#define DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME "newInstanceName"
|
||||
#define DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE "chatUpdate"
|
||||
#define DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION "newServerVersion"
|
||||
#define DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE "broadcastMessage"
|
||||
|
||||
#define DMAPI5_TOPIC_RESPONSE_CLIENT_COUNT "clientCount"
|
||||
#define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE "commandResponse"
|
||||
#define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE "commandResponseMessage"
|
||||
#define DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES "chatResponses"
|
||||
|
||||
@@ -93,7 +115,14 @@
|
||||
#define DMAPI5_CHAT_CHANNEL_IS_ADMIN_CHANNEL "isAdminChannel"
|
||||
#define DMAPI5_CHAT_CHANNEL_IS_PRIVATE_CHANNEL "isPrivateChannel"
|
||||
#define DMAPI5_CHAT_CHANNEL_TAG "tag"
|
||||
#define DMAPI5_CHAT_CHANNEL_EMBEDS_SUPPORTED "embedsSupported"
|
||||
|
||||
#define DMAPI5_CUSTOM_CHAT_COMMAND_NAME "name"
|
||||
#define DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT "helpText"
|
||||
#define DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY "adminOnly"
|
||||
|
||||
#define DMAPI5_EVENT_ID "eventId"
|
||||
|
||||
#define DMAPI5_EVENT_INVOCATION_NAME "eventName"
|
||||
#define DMAPI5_EVENT_INVOCATION_PARAMETERS "parameters"
|
||||
#define DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION "notifyCompletion"
|
||||
|
||||
+146
-181
@@ -4,11 +4,16 @@
|
||||
|
||||
var/instance_name
|
||||
var/security_level
|
||||
var/visibility
|
||||
|
||||
var/reboot_mode = TGS_REBOOT_MODE_NORMAL
|
||||
|
||||
/// List of chat messages list()s that attempted to be sent during a topic call. To be bundled in the result of the call
|
||||
var/list/intercepted_message_queue
|
||||
|
||||
/// List of chat messages list()s that attempted to be sent during a topic call. To be bundled in the result of the call
|
||||
var/list/offline_message_queue
|
||||
|
||||
var/list/custom_commands
|
||||
|
||||
var/list/test_merges
|
||||
@@ -16,19 +21,43 @@
|
||||
var/list/chat_channels
|
||||
|
||||
var/initialized = FALSE
|
||||
var/initial_bridge_request_received = FALSE
|
||||
var/datum/tgs_version/interop_version
|
||||
|
||||
var/chunked_requests = 0
|
||||
var/list/chunked_topics = list()
|
||||
|
||||
var/list/pending_events = list()
|
||||
|
||||
var/detached = FALSE
|
||||
|
||||
var/datum/tgs_http_handler/http_handler
|
||||
|
||||
/datum/tgs_api/v5/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version, datum/tgs_http_handler/http_handler)
|
||||
. = ..()
|
||||
interop_version = version
|
||||
src.http_handler = http_handler
|
||||
TGS_DEBUG_LOG("V5 API created: [json_encode(args)]")
|
||||
|
||||
/datum/tgs_api/v5/ApiVersion()
|
||||
return new /datum/tgs_version(
|
||||
#include "interop_version.dm"
|
||||
#include "__interop_version.dm"
|
||||
)
|
||||
|
||||
/datum/tgs_api/v5/OnWorldNew(minimum_required_security_level)
|
||||
TGS_DEBUG_LOG("OnWorldNew()")
|
||||
server_port = world.params[DMAPI5_PARAM_SERVER_PORT]
|
||||
access_identifier = world.params[DMAPI5_PARAM_ACCESS_IDENTIFIER]
|
||||
|
||||
var/datum/tgs_version/api_version = ApiVersion()
|
||||
version = null
|
||||
var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands()))
|
||||
version = null // we want this to be the TGS version, not the interop version
|
||||
|
||||
// sleep once to prevent an issue where world.Export on the first tick can hang indefinitely
|
||||
TGS_DEBUG_LOG("Starting Export bug prevention sleep tick. time:[world.time] sleep_offline:[world.sleep_offline]")
|
||||
sleep(world.tick_lag)
|
||||
TGS_DEBUG_LOG("Export bug prevention sleep complete")
|
||||
|
||||
var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands(), DMAPI5_PARAMETER_TOPIC_PORT = GetTopicPort()))
|
||||
if(!istype(bridge_response))
|
||||
TGS_ERROR_LOG("Failed initial bridge request!")
|
||||
return FALSE
|
||||
@@ -40,10 +69,12 @@
|
||||
|
||||
if(runtime_information[DMAPI5_RUNTIME_INFORMATION_API_VALIDATE_ONLY])
|
||||
TGS_INFO_LOG("DMAPI validation, exiting...")
|
||||
del(world)
|
||||
TerminateWorld()
|
||||
|
||||
version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION])
|
||||
initial_bridge_request_received = TRUE
|
||||
version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION]) // reassigning this because it can change if TGS updates
|
||||
security_level = runtime_information[DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL]
|
||||
visibility = runtime_information[DMAPI5_RUNTIME_INFORMATION_VISIBILITY]
|
||||
instance_name = runtime_information[DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME]
|
||||
|
||||
var/list/revisionData = runtime_information[DMAPI5_RUNTIME_INFORMATION_REVISION]
|
||||
@@ -90,186 +121,50 @@
|
||||
initialized = TRUE
|
||||
return TRUE
|
||||
|
||||
/datum/tgs_api/v5/proc/GetTopicPort()
|
||||
#if defined(OPENDREAM) && defined(OPENDREAM_TOPIC_PORT_EXISTS)
|
||||
return "[world.opendream_topic_port]"
|
||||
#else
|
||||
return null
|
||||
#endif
|
||||
|
||||
/datum/tgs_api/v5/proc/RequireInitialBridgeResponse()
|
||||
while(!version)
|
||||
sleep(1)
|
||||
TGS_DEBUG_LOG("RequireInitialBridgeResponse()")
|
||||
var/logged = FALSE
|
||||
while(!initial_bridge_request_received)
|
||||
if(!logged)
|
||||
TGS_DEBUG_LOG("RequireInitialBridgeResponse: Starting sleep")
|
||||
logged = TRUE
|
||||
|
||||
sleep(world.tick_lag)
|
||||
|
||||
TGS_DEBUG_LOG("RequireInitialBridgeResponse: Passed")
|
||||
|
||||
/datum/tgs_api/v5/OnInitializationComplete()
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_PRIME)
|
||||
|
||||
/datum/tgs_api/v5/proc/TopicResponse(error_message = null)
|
||||
var/list/response = list()
|
||||
response[DMAPI5_RESPONSE_ERROR_MESSAGE] = error_message
|
||||
|
||||
return json_encode(response)
|
||||
|
||||
/datum/tgs_api/v5/OnTopic(T)
|
||||
TGS_DEBUG_LOG("OnTopic()")
|
||||
RequireInitialBridgeResponse()
|
||||
TGS_DEBUG_LOG("OnTopic passed bridge request gate")
|
||||
var/list/params = params2list(T)
|
||||
var/json = params[DMAPI5_TOPIC_DATA]
|
||||
if(!json)
|
||||
TGS_DEBUG_LOG("No \"[DMAPI5_TOPIC_DATA]\" entry found, ignoring...")
|
||||
return FALSE // continue to /world/Topic
|
||||
|
||||
var/list/topic_parameters = json_decode(json)
|
||||
if(!topic_parameters)
|
||||
return TopicResponse("Invalid topic parameters json!");
|
||||
|
||||
if(!initialized)
|
||||
TGS_WARNING_LOG("Missed topic due to not being initialized: [T]")
|
||||
TGS_WARNING_LOG("Missed topic due to not being initialized: [json]")
|
||||
return TRUE // too early to handle, but it's still our responsibility
|
||||
|
||||
var/their_sCK = topic_parameters[DMAPI5_PARAMETER_ACCESS_IDENTIFIER]
|
||||
if(their_sCK != access_identifier)
|
||||
return TopicResponse("Failed to decode [DMAPI5_PARAMETER_ACCESS_IDENTIFIER] from: [json]!");
|
||||
|
||||
var/command = topic_parameters[DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE]
|
||||
if(!isnum(command))
|
||||
return TopicResponse("Failed to decode [DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE] from: [json]!")
|
||||
|
||||
switch(command)
|
||||
if(DMAPI5_TOPIC_COMMAND_CHAT_COMMAND)
|
||||
var/result = HandleCustomCommand(topic_parameters[DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND])
|
||||
if(!result)
|
||||
result = TopicResponse("Error running chat command!")
|
||||
return result
|
||||
if(DMAPI5_TOPIC_COMMAND_EVENT_NOTIFICATION)
|
||||
intercepted_message_queue = list()
|
||||
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))
|
||||
return TopicResponse("Invalid or missing [DMAPI5_EVENT_NOTIFICATION_PARAMETERS]!")
|
||||
|
||||
var/list/event_call = list(event_type)
|
||||
if(event_parameters)
|
||||
event_call += event_parameters
|
||||
|
||||
if(event_handler != null)
|
||||
event_handler.HandleEvent(arglist(event_call))
|
||||
|
||||
var/list/response = list()
|
||||
response[DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES] = intercepted_message_queue
|
||||
intercepted_message_queue = null
|
||||
return json_encode(response)
|
||||
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)
|
||||
var/list/chat_update_json = topic_parameters[DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE]
|
||||
if(!istype(chat_update_json))
|
||||
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_HEARTBEAT)
|
||||
return TopicResponse()
|
||||
if(DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH)
|
||||
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
|
||||
|
||||
return json_encode(list(DMAPI5_RESPONSE_ERROR_MESSAGE = error_message, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands()))
|
||||
|
||||
return TopicResponse("Unknown command: [command]")
|
||||
|
||||
/datum/tgs_api/v5/proc/Bridge(command, list/data)
|
||||
if(!data)
|
||||
data = list()
|
||||
|
||||
data[DMAPI5_BRIDGE_PARAMETER_COMMAND_TYPE] = command
|
||||
data[DMAPI5_PARAMETER_ACCESS_IDENTIFIER] = access_identifier
|
||||
|
||||
var/json = json_encode(data)
|
||||
var/encoded_json = url_encode(json)
|
||||
|
||||
// This is an infinite sleep until we get a response
|
||||
var/export_response = world.Export("http://127.0.0.1:[server_port]/Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]")
|
||||
if(!export_response)
|
||||
TGS_ERROR_LOG("Failed export request: [json]")
|
||||
return
|
||||
|
||||
var/response_json = file2text(export_response["CONTENT"])
|
||||
if(!response_json)
|
||||
TGS_ERROR_LOG("Failed export request, missing content!")
|
||||
return
|
||||
|
||||
var/list/bridge_response = json_decode(response_json)
|
||||
if(!bridge_response)
|
||||
TGS_ERROR_LOG("Failed export request, bad json: [response_json]")
|
||||
return
|
||||
|
||||
var/error = bridge_response[DMAPI5_RESPONSE_ERROR_MESSAGE]
|
||||
if(error)
|
||||
TGS_ERROR_LOG("Failed export request, bad request: [error]")
|
||||
return
|
||||
|
||||
return bridge_response
|
||||
return ProcessTopicJson(json, TRUE)
|
||||
|
||||
/datum/tgs_api/v5/OnReboot()
|
||||
var/list/result = Bridge(DMAPI5_BRIDGE_COMMAND_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
|
||||
//okay so the standard TGS proceedure is: right before rebooting change the port to whatever was sent to us in the above json's data parameter
|
||||
|
||||
var/port = result[DMAPI5_BRIDGE_RESPONSE_NEW_PORT]
|
||||
if(!isnum(port))
|
||||
@@ -297,7 +192,15 @@
|
||||
RequireInitialBridgeResponse()
|
||||
return revision
|
||||
|
||||
/datum/tgs_api/v5/ChatBroadcast(message, list/channels)
|
||||
// Common proc b/c it's used by the V3/V4 APIs
|
||||
/datum/tgs_api/proc/UpgradeDeprecatedChatMessage(datum/tgs_message_content/message)
|
||||
if(!istext(message))
|
||||
return message
|
||||
|
||||
TGS_WARNING_LOG("Received legacy string when a [/datum/tgs_message_content] was expected. Please audit all calls to TgsChatBroadcast, TgsChatTargetedBroadcast, and TgsChatPrivateMessage to ensure they use the new /datum.")
|
||||
return new /datum/tgs_message_content(message)
|
||||
|
||||
/datum/tgs_api/v5/ChatBroadcast(datum/tgs_message_content/message2, list/channels)
|
||||
if(!length(channels))
|
||||
channels = ChatChannelInfo()
|
||||
|
||||
@@ -306,36 +209,93 @@
|
||||
var/datum/tgs_chat_channel/channel = I
|
||||
ids += channel.id
|
||||
|
||||
message = list(DMAPI5_CHAT_MESSAGE_TEXT = message, DMAPI5_CHAT_MESSAGE_CHANNEL_IDS = ids)
|
||||
if(intercepted_message_queue)
|
||||
intercepted_message_queue += list(message)
|
||||
else
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message))
|
||||
SendChatMessageRaw(message2, ids)
|
||||
|
||||
/datum/tgs_api/v5/ChatTargetedBroadcast(message, admin_only)
|
||||
/datum/tgs_api/v5/ChatTargetedBroadcast(datum/tgs_message_content/message2, 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 = list(DMAPI5_CHAT_MESSAGE_TEXT = message, DMAPI5_CHAT_MESSAGE_CHANNEL_IDS = channels)
|
||||
if(intercepted_message_queue)
|
||||
intercepted_message_queue += list(message)
|
||||
else
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message))
|
||||
|
||||
/datum/tgs_api/v5/ChatPrivateMessage(message, datum/tgs_chat_user/user)
|
||||
message = list(DMAPI5_CHAT_MESSAGE_TEXT = message, DMAPI5_CHAT_MESSAGE_CHANNEL_IDS = list(user.channel.id))
|
||||
SendChatMessageRaw(message2, channels)
|
||||
|
||||
/datum/tgs_api/v5/ChatPrivateMessage(datum/tgs_message_content/message2, datum/tgs_chat_user/user)
|
||||
SendChatMessageRaw(message2, list(user.channel.id))
|
||||
|
||||
/datum/tgs_api/v5/proc/SendChatMessageRaw(datum/tgs_message_content/message2, list/channel_ids)
|
||||
message2 = UpgradeDeprecatedChatMessage(message2)
|
||||
|
||||
if (!length(channel_ids))
|
||||
return
|
||||
|
||||
var/list/data = message2._interop_serialize()
|
||||
data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = channel_ids
|
||||
if(intercepted_message_queue)
|
||||
intercepted_message_queue += list(message)
|
||||
intercepted_message_queue += list(data)
|
||||
return
|
||||
|
||||
if(offline_message_queue)
|
||||
offline_message_queue += list(data)
|
||||
return
|
||||
|
||||
if(detached)
|
||||
offline_message_queue = list(data)
|
||||
|
||||
WaitForReattach(FALSE)
|
||||
|
||||
data = offline_message_queue
|
||||
offline_message_queue = null
|
||||
|
||||
for(var/queued_message in data)
|
||||
SendChatDataRaw(queued_message)
|
||||
else
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message))
|
||||
SendChatDataRaw(data)
|
||||
|
||||
/datum/tgs_api/v5/proc/SendChatDataRaw(list/data)
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
|
||||
|
||||
/datum/tgs_api/v5/ChatChannelInfo()
|
||||
RequireInitialBridgeResponse()
|
||||
WaitForReattach(TRUE)
|
||||
return chat_channels.Copy()
|
||||
|
||||
/datum/tgs_api/v5/TriggerEvent(event_name, list/parameters, wait_for_completion)
|
||||
RequireInitialBridgeResponse()
|
||||
WaitForReattach(TRUE)
|
||||
|
||||
if(interop_version.minor < 9)
|
||||
TGS_WARNING_LOG("Interop version too low for custom events!")
|
||||
return FALSE
|
||||
|
||||
var/str_parameters = list()
|
||||
for(var/i in parameters)
|
||||
str_parameters += "[i]"
|
||||
|
||||
var/list/response = Bridge(DMAPI5_BRIDGE_COMMAND_EVENT, list(DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION = list(DMAPI5_EVENT_INVOCATION_NAME = event_name, DMAPI5_EVENT_INVOCATION_PARAMETERS = str_parameters, DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION = wait_for_completion)))
|
||||
if(!response)
|
||||
return FALSE
|
||||
|
||||
var/event_id = response[DMAPI5_EVENT_ID]
|
||||
if(!event_id)
|
||||
return FALSE
|
||||
|
||||
TGS_DEBUG_LOG("Created event ID: [event_id]")
|
||||
if(!wait_for_completion)
|
||||
return TRUE
|
||||
|
||||
TGS_DEBUG_LOG("Waiting for completion of event ID: [event_id]")
|
||||
|
||||
while(!pending_events[event_id])
|
||||
sleep(world.tick_lag)
|
||||
|
||||
TGS_DEBUG_LOG("Completed wait on event ID: [event_id]")
|
||||
pending_events -= event_id
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/tgs_api/v5/proc/DecodeChannels(chat_update_json)
|
||||
TGS_DEBUG_LOG("DecodeChannels()")
|
||||
var/list/chat_channels_json = chat_update_json[DMAPI5_CHAT_UPDATE_CHANNELS]
|
||||
if(istype(chat_channels_json))
|
||||
chat_channels.Cut()
|
||||
@@ -354,8 +314,13 @@
|
||||
channel.is_admin_channel = channel_json[DMAPI5_CHAT_CHANNEL_IS_ADMIN_CHANNEL]
|
||||
channel.is_private_channel = channel_json[DMAPI5_CHAT_CHANNEL_IS_PRIVATE_CHANNEL]
|
||||
channel.custom_tag = channel_json[DMAPI5_CHAT_CHANNEL_TAG]
|
||||
channel.embeds_supported = channel_json[DMAPI5_CHAT_CHANNEL_EMBEDS_SUPPORTED]
|
||||
return channel
|
||||
|
||||
/datum/tgs_api/v5/SecurityLevel()
|
||||
RequireInitialBridgeResponse()
|
||||
return security_level
|
||||
|
||||
/datum/tgs_api/v5/Visibility()
|
||||
RequireInitialBridgeResponse()
|
||||
return visibility
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/datum/tgs_api/v5/proc/Bridge(command, list/data)
|
||||
if(!data)
|
||||
data = list()
|
||||
|
||||
var/single_bridge_request = CreateBridgeRequest(command, data)
|
||||
if(length(single_bridge_request) <= DMAPI5_BRIDGE_REQUEST_LIMIT)
|
||||
return PerformBridgeRequest(single_bridge_request)
|
||||
|
||||
// chunking required
|
||||
var/payload_id = ++chunked_requests
|
||||
|
||||
var/raw_data = CreateBridgeData(command, data, FALSE)
|
||||
|
||||
var/list/chunk_requests = GenerateChunks(raw_data, TRUE)
|
||||
|
||||
var/list/response
|
||||
for(var/bridge_request in chunk_requests)
|
||||
response = PerformBridgeRequest(bridge_request)
|
||||
if(!response)
|
||||
// Abort
|
||||
return
|
||||
|
||||
var/list/missing_sequence_ids = response[DMAPI5_MISSING_CHUNKS]
|
||||
if(length(missing_sequence_ids))
|
||||
do
|
||||
TGS_WARNING_LOG("Server is still missing some chunks of bridge P[payload_id]! Sending missing chunks...")
|
||||
if(!istype(missing_sequence_ids))
|
||||
TGS_ERROR_LOG("Did not receive a list() for [DMAPI5_MISSING_CHUNKS]!")
|
||||
return
|
||||
|
||||
for(var/missing_sequence_id in missing_sequence_ids)
|
||||
if(!isnum(missing_sequence_id))
|
||||
TGS_ERROR_LOG("Did not receive a num in [DMAPI5_MISSING_CHUNKS]!")
|
||||
return
|
||||
|
||||
var/missing_chunk_request = chunk_requests[missing_sequence_id + 1]
|
||||
response = PerformBridgeRequest(missing_chunk_request)
|
||||
if(!response)
|
||||
// Abort
|
||||
return
|
||||
|
||||
missing_sequence_ids = response[DMAPI5_MISSING_CHUNKS]
|
||||
while(length(missing_sequence_ids))
|
||||
|
||||
return response
|
||||
|
||||
/datum/tgs_api/v5/proc/CreateBridgeRequest(command, list/data)
|
||||
var/json = CreateBridgeData(command, data, TRUE)
|
||||
var/encoded_json = url_encode(json)
|
||||
|
||||
var/api_prefix = interop_version.minor >= 8 ? "api/" : ""
|
||||
|
||||
var/url = "http://127.0.0.1:[server_port]/[api_prefix]Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]"
|
||||
return url
|
||||
|
||||
/datum/tgs_api/v5/proc/CreateBridgeData(command, list/data, needs_auth)
|
||||
data[DMAPI5_BRIDGE_PARAMETER_COMMAND_TYPE] = command
|
||||
if(needs_auth)
|
||||
data[DMAPI5_PARAMETER_ACCESS_IDENTIFIER] = access_identifier
|
||||
|
||||
var/json = json_encode(data)
|
||||
return json
|
||||
|
||||
/datum/tgs_api/v5/proc/WaitForReattach(require_channels = FALSE)
|
||||
if(detached)
|
||||
// Wait up to one minute
|
||||
for(var/i in 1 to 600)
|
||||
sleep(world.tick_lag)
|
||||
if(!detached && (!require_channels || length(chat_channels)))
|
||||
break
|
||||
|
||||
// dad went out for milk and cigarettes 20 years ago...
|
||||
// yes, this affects all other waiters, intentional
|
||||
if(i == 600)
|
||||
detached = FALSE
|
||||
|
||||
/datum/tgs_api/v5/proc/PerformBridgeRequest(bridge_request)
|
||||
WaitForReattach(FALSE)
|
||||
|
||||
TGS_DEBUG_LOG("Bridge request start")
|
||||
var/datum/tgs_http_result/result = http_handler.PerformGet(bridge_request)
|
||||
TGS_DEBUG_LOG("Bridge request complete")
|
||||
|
||||
if(isnull(result))
|
||||
TGS_ERROR_LOG("Failed bridge request, handler returned null!")
|
||||
return
|
||||
|
||||
if(!istype(result) || result.type != /datum/tgs_http_result)
|
||||
TGS_ERROR_LOG("Failed bridge request, handler returned non-[/datum/tgs_http_result]!")
|
||||
return
|
||||
|
||||
if(!result.success)
|
||||
TGS_DEBUG_LOG("Failed bridge request, HTTP request failed!")
|
||||
return
|
||||
|
||||
var/list/bridge_response = json_decode(result.response_text)
|
||||
if(!bridge_response)
|
||||
TGS_ERROR_LOG("Failed bridge request, bad json: [result.response_text]")
|
||||
return
|
||||
|
||||
var/error = bridge_response[DMAPI5_RESPONSE_ERROR_MESSAGE]
|
||||
if(error)
|
||||
TGS_ERROR_LOG("Failed bridge request, bad request: [error]")
|
||||
return
|
||||
|
||||
return bridge_response
|
||||
@@ -0,0 +1,43 @@
|
||||
/datum/tgs_api/v5/proc/GenerateChunks(payload, bridge)
|
||||
var/limit = bridge ? DMAPI5_BRIDGE_REQUEST_LIMIT : DMAPI5_TOPIC_RESPONSE_LIMIT
|
||||
|
||||
var/payload_id = ++chunked_requests
|
||||
var/data_length = length(payload)
|
||||
|
||||
var/chunk_count
|
||||
var/list/chunk_requests
|
||||
for(chunk_count = 2; !chunk_requests; ++chunk_count)
|
||||
var/max_chunk_size = -round(-(data_length / chunk_count))
|
||||
if(max_chunk_size > limit)
|
||||
continue
|
||||
|
||||
chunk_requests = list()
|
||||
for(var/i in 1 to chunk_count)
|
||||
var/start_index = 1 + ((i - 1) * max_chunk_size)
|
||||
if (start_index > data_length)
|
||||
break
|
||||
|
||||
var/end_index = min(1 + (i * max_chunk_size), data_length + 1)
|
||||
|
||||
var/chunk_payload = copytext(payload, start_index, end_index)
|
||||
|
||||
// sequence IDs in interop chunking are always zero indexed
|
||||
var/list/chunk = list(DMAPI5_CHUNK_PAYLOAD_ID = payload_id, DMAPI5_CHUNK_SEQUENCE_ID = (i - 1), DMAPI5_CHUNK_TOTAL = chunk_count, DMAPI5_CHUNK_PAYLOAD = chunk_payload)
|
||||
|
||||
var/chunk_request = list(DMAPI5_CHUNK = chunk)
|
||||
var/chunk_length
|
||||
if(bridge)
|
||||
chunk_request = CreateBridgeRequest(DMAPI5_BRIDGE_COMMAND_CHUNK, chunk_request)
|
||||
chunk_length = length(chunk_request)
|
||||
else
|
||||
chunk_request = list(chunk_request) // wrap for adding to list
|
||||
chunk_length = length(json_encode(chunk_request))
|
||||
|
||||
if(chunk_length > limit)
|
||||
// Screwed by encoding, no way to preempt it though
|
||||
chunk_requests = null
|
||||
break
|
||||
|
||||
chunk_requests += chunk_request
|
||||
|
||||
return chunk_requests
|
||||
@@ -3,14 +3,17 @@
|
||||
custom_commands = list()
|
||||
for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
|
||||
var/datum/tgs_chat_command/stc = new I
|
||||
if(stc.ignore_type == I)
|
||||
continue
|
||||
|
||||
var/command_name = stc.name
|
||||
if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
|
||||
TGS_WARNING_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!")
|
||||
TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!")
|
||||
continue
|
||||
|
||||
if(results[command_name])
|
||||
var/datum/other = custom_commands[command_name]
|
||||
TGS_WARNING_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!")
|
||||
TGS_ERROR_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!")
|
||||
continue
|
||||
results += list(list(DMAPI5_CUSTOM_CHAT_COMMAND_NAME = command_name, DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT = stc.help_text, DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY = stc.admin_only))
|
||||
custom_commands[command_name] = stc
|
||||
@@ -30,11 +33,28 @@
|
||||
|
||||
var/datum/tgs_chat_command/sc = custom_commands[command]
|
||||
if(sc)
|
||||
var/text_response = sc.Run(u, params)
|
||||
var/list/topic_response = list()
|
||||
if(!istext(text_response))
|
||||
TGS_ERROR_LOG("Custom command [command] should return a string! Got: \"[text_response]\"")
|
||||
text_response = null
|
||||
topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE] = text_response
|
||||
return json_encode(topic_response)
|
||||
var/datum/tgs_message_content/response = sc.Run(u, params)
|
||||
response = UpgradeDeprecatedCommandResponse(response, command)
|
||||
|
||||
var/list/topic_response = TopicResponse()
|
||||
topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE] = response ? response.text : null
|
||||
topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE] = response ? response._interop_serialize() : null
|
||||
return topic_response
|
||||
return TopicResponse("Unknown custom chat command: [command]!")
|
||||
|
||||
// Common proc b/c it's used by the V3/V4 APIs
|
||||
/datum/tgs_api/proc/UpgradeDeprecatedCommandResponse(datum/tgs_message_content/response, command)
|
||||
// Backwards compatibility, used to return a string
|
||||
if(istext(response))
|
||||
warned_deprecated_command_runs = warned_deprecated_command_runs || list()
|
||||
if(!warned_deprecated_command_runs[command])
|
||||
TGS_WARNING_LOG("Custom chat command \"[command]\" is still returning a string. This behaviour is deprecated, please upgrade it to return a [/datum/tgs_message_content].")
|
||||
warned_deprecated_command_runs[command] = TRUE
|
||||
|
||||
return new /datum/tgs_message_content(response)
|
||||
|
||||
if(!istype(response))
|
||||
TGS_ERROR_LOG("Custom chat command \"[command]\" should return a [/datum/tgs_message_content]! Got: \"[response]\"")
|
||||
return null
|
||||
|
||||
return response
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
"5.3.0"
|
||||
@@ -0,0 +1,59 @@
|
||||
/datum/tgs_message_content/proc/_interop_serialize()
|
||||
return list("text" = text, "embed" = embed ? embed._interop_serialize() : null)
|
||||
|
||||
/datum/tgs_chat_embed/proc/_interop_serialize()
|
||||
CRASH("Base /proc/interop_serialize called on [type]!")
|
||||
|
||||
/datum/tgs_chat_embed/structure/_interop_serialize()
|
||||
var/list/serialized_fields
|
||||
if(istype(fields, /list))
|
||||
serialized_fields = list()
|
||||
for(var/datum/tgs_chat_embed/field/field as anything in fields)
|
||||
serialized_fields += list(field._interop_serialize())
|
||||
return list(
|
||||
"title" = title,
|
||||
"description" = description,
|
||||
"url" = url,
|
||||
"timestamp" = timestamp,
|
||||
"colour" = colour,
|
||||
"image" = src.image ? src.image._interop_serialize() : null,
|
||||
"thumbnail" = thumbnail ? thumbnail._interop_serialize() : null,
|
||||
"video" = video ? video._interop_serialize() : null,
|
||||
"footer" = footer ? footer._interop_serialize() : null,
|
||||
"provider" = provider ? provider._interop_serialize() : null,
|
||||
"author" = author ? author._interop_serialize() : null,
|
||||
"fields" = serialized_fields
|
||||
)
|
||||
|
||||
/datum/tgs_chat_embed/media/_interop_serialize()
|
||||
return list(
|
||||
"url" = url,
|
||||
"width" = width,
|
||||
"height" = height,
|
||||
"proxyUrl" = proxy_url
|
||||
)
|
||||
|
||||
/datum/tgs_chat_embed/provider/_interop_serialize()
|
||||
return list(
|
||||
"url" = url,
|
||||
"name" = name
|
||||
)
|
||||
|
||||
/datum/tgs_chat_embed/provider/author/_interop_serialize()
|
||||
. = ..()
|
||||
.["iconUrl"] = icon_url
|
||||
.["proxyIconUrl"] = proxy_icon_url
|
||||
|
||||
/datum/tgs_chat_embed/footer/_interop_serialize()
|
||||
return list(
|
||||
"text" = text,
|
||||
"iconUrl" = icon_url,
|
||||
"proxyIconUrl" = proxy_icon_url
|
||||
)
|
||||
|
||||
/datum/tgs_chat_embed/field/_interop_serialize()
|
||||
return list(
|
||||
"name" = name,
|
||||
"value" = value,
|
||||
"isInline" = is_inline
|
||||
)
|
||||
@@ -0,0 +1,298 @@
|
||||
/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
|
||||
|
||||
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)
|
||||
@@ -4,15 +4,29 @@
|
||||
#undef DMAPI5_BRIDGE_DATA
|
||||
#undef DMAPI5_TOPIC_DATA
|
||||
|
||||
#undef DMAPI5_BRIDGE_COMMAND_PORT_UPDATE
|
||||
#undef DMAPI5_BRIDGE_REQUEST_LIMIT
|
||||
#undef DMAPI5_TOPIC_REQUEST_LIMIT
|
||||
#undef DMAPI5_TOPIC_RESPONSE_LIMIT
|
||||
|
||||
#undef DMAPI5_BRIDGE_COMMAND_STARTUP
|
||||
#undef DMAPI5_BRIDGE_COMMAND_PRIME
|
||||
#undef DMAPI5_BRIDGE_COMMAND_REBOOT
|
||||
#undef DMAPI5_BRIDGE_COMMAND_KILL
|
||||
#undef DMAPI5_BRIDGE_COMMAND_CHAT_SEND
|
||||
#undef DMAPI5_BRIDGE_COMMAND_CHUNK
|
||||
#undef DMAPI5_BRIDGE_COMMAND_EVENT
|
||||
|
||||
#undef DMAPI5_PARAMETER_ACCESS_IDENTIFIER
|
||||
#undef DMAPI5_PARAMETER_CUSTOM_COMMANDS
|
||||
#undef DMAPI5_PARAMETER_TOPIC_PORT
|
||||
|
||||
#undef DMAPI5_CHUNK
|
||||
#undef DMAPI5_CHUNK_PAYLOAD
|
||||
#undef DMAPI5_CHUNK_TOTAL
|
||||
#undef DMAPI5_CHUNK_SEQUENCE_ID
|
||||
#undef DMAPI5_CHUNK_PAYLOAD_ID
|
||||
|
||||
#undef DMAPI5_MISSING_CHUNKS
|
||||
|
||||
#undef DMAPI5_RESPONSE_ERROR_MESSAGE
|
||||
|
||||
@@ -21,11 +35,11 @@
|
||||
#undef DMAPI5_BRIDGE_PARAMETER_VERSION
|
||||
#undef DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE
|
||||
#undef DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL
|
||||
#undef DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION
|
||||
|
||||
#undef DMAPI5_BRIDGE_RESPONSE_NEW_PORT
|
||||
#undef DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION
|
||||
|
||||
#undef DMAPI5_CHAT_MESSAGE_TEXT
|
||||
#undef DMAPI5_CHAT_MESSAGE_CHANNEL_IDS
|
||||
|
||||
#undef DMAPI5_RUNTIME_INFORMATION_ACCESS_IDENTIFIER
|
||||
@@ -36,6 +50,7 @@
|
||||
#undef DMAPI5_RUNTIME_INFORMATION_REVISION
|
||||
#undef DMAPI5_RUNTIME_INFORMATION_TEST_MERGES
|
||||
#undef DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL
|
||||
#undef DMAPI5_RUNTIME_INFORMATION_VISIBILITY
|
||||
|
||||
#undef DMAPI5_CHAT_UPDATE_CHANNELS
|
||||
|
||||
@@ -63,8 +78,12 @@
|
||||
#undef DMAPI5_TOPIC_COMMAND_INSTANCE_RENAMED
|
||||
#undef DMAPI5_TOPIC_COMMAND_CHAT_CHANNELS_UPDATE
|
||||
#undef DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE
|
||||
#undef DMAPI5_TOPIC_COMMAND_HEARTBEAT
|
||||
#undef DMAPI5_TOPIC_COMMAND_HEALTHCHECK
|
||||
#undef DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH
|
||||
#undef DMAPI5_TOPIC_COMMAND_SEND_CHUNK
|
||||
#undef DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK
|
||||
#undef DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST
|
||||
#undef DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT
|
||||
|
||||
#undef DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE
|
||||
#undef DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND
|
||||
@@ -74,7 +93,10 @@
|
||||
#undef DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME
|
||||
#undef DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE
|
||||
#undef DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION
|
||||
#undef DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE
|
||||
|
||||
#undef DMAPI5_TOPIC_RESPONSE_CLIENT_COUNT
|
||||
#undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE
|
||||
#undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE
|
||||
#undef DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES
|
||||
|
||||
@@ -93,7 +115,14 @@
|
||||
#undef DMAPI5_CHAT_CHANNEL_IS_ADMIN_CHANNEL
|
||||
#undef DMAPI5_CHAT_CHANNEL_IS_PRIVATE_CHANNEL
|
||||
#undef DMAPI5_CHAT_CHANNEL_TAG
|
||||
#undef DMAPI5_CHAT_CHANNEL_EMBEDS_SUPPORTED
|
||||
|
||||
#undef DMAPI5_CUSTOM_CHAT_COMMAND_NAME
|
||||
#undef DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT
|
||||
#undef DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY
|
||||
|
||||
#undef DMAPI5_EVENT_ID
|
||||
|
||||
#undef DMAPI5_EVENT_INVOCATION_NAME
|
||||
#undef DMAPI5_EVENT_INVOCATION_PARAMETERS
|
||||
#undef DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION
|
||||
|
||||
Reference in New Issue
Block a user