From 77848bd90f6f8b50b0370a82cc6b412319f06da3 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 8 May 2020 04:30:51 -0700 Subject: [PATCH 01/62] tgs folder --- code/modules/tgs/core/core.dm | 17 +- code/modules/tgs/core/datum.dm | 2 +- code/modules/tgs/core/tgs_version.dm | 14 +- code/modules/tgs/includes.dm | 34 +++ code/modules/tgs/v3210/api.dm | 8 +- code/modules/tgs/v3210/commands.dm | 32 +-- code/modules/tgs/v4/api.dm | 2 +- code/modules/tgs/v4/commands.dm | 30 +-- code/modules/tgs/v5/_defines.dm | 121 +++++++++ code/modules/tgs/v5/api.dm | 352 +++++++++++++++++++++++++++ code/modules/tgs/v5/commands.dm | 68 ++++++ code/modules/tgs/v5/undef.dm | 121 +++++++++ 12 files changed, 756 insertions(+), 45 deletions(-) create mode 100644 code/modules/tgs/v5/_defines.dm create mode 100644 code/modules/tgs/v5/api.dm create mode 100644 code/modules/tgs/v5/commands.dm create mode 100644 code/modules/tgs/v5/undef.dm diff --git a/code/modules/tgs/core/core.dm b/code/modules/tgs/core/core.dm index 70252cfb49..144f33926f 100644 --- a/code/modules/tgs/core/core.dm +++ b/code/modules/tgs/core/core.dm @@ -22,17 +22,19 @@ #ifndef TGS_V3_API TGS_ERROR_LOG("Detected V3 API but TGS_V3_API isn't defined!") #else - switch(version.major) + switch(version.minor) if(2) api_datum = /datum/tgs_api/v3210 #endif if(4) - switch(version.major) + switch(version.minor) if(0) api_datum = /datum/tgs_api/v4 + if(5) + api_datum = /datum/tgs_api/v5 var/datum/tgs_version/max_api_version = TgsMaximumAPIVersion(); - if(version.suite != null && version.major != null && version.minor != null && version.patch != null && version.deprefixed_parameter > max_api_version.deprefixed_parameter) + 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.") api_datum = /datum/tgs_api/latest @@ -51,10 +53,10 @@ TGS_ERROR_LOG("Failed to activate API!") /world/TgsMaximumAPIVersion() - return new /datum/tgs_version("4.0.x.x") + return new /datum/tgs_version("5.x.x") /world/TgsMinimumAPIVersion() - return new /datum/tgs_version("3.2.0.0") + return new /datum/tgs_version("3.2.x") /world/TgsInitializationComplete() var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) @@ -88,6 +90,11 @@ if(api) return api.version +/world/TgsApiVersion() + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + return api.ApiVersion() + /world/TgsInstanceName() var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) if(api) diff --git a/code/modules/tgs/core/datum.dm b/code/modules/tgs/core/datum.dm index fb2508059a..3adecbb556 100644 --- a/code/modules/tgs/core/datum.dm +++ b/code/modules/tgs/core/datum.dm @@ -8,7 +8,7 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null) src.version = version /datum/tgs_api/latest - parent_type = /datum/tgs_api/v4 + parent_type = /datum/tgs_api/v5 TGS_PROTECT_DATUM(/datum/tgs_api) diff --git a/code/modules/tgs/core/tgs_version.dm b/code/modules/tgs/core/tgs_version.dm index b97745611f..a5dae1241a 100644 --- a/code/modules/tgs/core/tgs_version.dm +++ b/code/modules/tgs/core/tgs_version.dm @@ -5,11 +5,11 @@ suite = text2num(version_bits[1]) if(version_bits.len > 1) - major = text2num(version_bits[2]) + minor = text2num(version_bits[2]) if(version_bits.len > 2) - minor = text2num(version_bits[3]) + patch = text2num(version_bits[3]) if(version_bits.len == 4) - patch = text2num(version_bits[4]) + deprecated_patch = text2num(version_bits[4]) /datum/tgs_version/proc/Valid(allow_wildcards = FALSE) if(suite == null) @@ -19,4 +19,10 @@ return !Wildcard() /datum/tgs_version/Wildcard() - return major == null || minor == null || patch == null + return minor == null || patch == null + +/datum/tgs_version/Equals(datum/tgs_version/other_version) + if(!istype(other_version)) + return FALSE + + return suite == other_version.suite && minor == other_version.minor && patch == other_version.patch && deprecated_patch == other_version.deprecated_patch diff --git a/code/modules/tgs/includes.dm b/code/modules/tgs/includes.dm index 247f1fda5d..b3c1728f34 100644 --- a/code/modules/tgs/includes.dm +++ b/code/modules/tgs/includes.dm @@ -2,9 +2,43 @@ #include "core\core.dm" #include "core\datum.dm" #include "core\tgs_version.dm" + #ifdef TGS_V3_API #include "v3210\api.dm" #include "v3210\commands.dm" #endif + #include "v4\api.dm" #include "v4\commands.dm" + +#include "v5\_defines.dm" +#include "v5\api.dm" +#include "v5\commands.dm" +#include "v5\undef.dm" + +/* +The MIT License + +Copyright (c) 2017 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/code/modules/tgs/v3210/api.dm b/code/modules/tgs/v3210/api.dm index c536995b04..5327b9276b 100644 --- a/code/modules/tgs/v3210/api.dm +++ b/code/modules/tgs/v3210/api.dm @@ -39,7 +39,7 @@ var/warned_custom_commands = FALSE /datum/tgs_api/v3210/ApiVersion() - return "3.2.1.0" + return new /datum/tgs_version("3.2.1.0") /datum/tgs_api/v3210/proc/trim_left(text) for (var/i = 1 to length(text)) @@ -76,7 +76,8 @@ TGS_ERROR_LOG("This API version is only supported on Windows. Not running on Windows. Aborting initialization!") return ListServiceCustomCommands(TRUE) - ExportService("[SERVICE_REQUEST_API_VERSION] [ApiVersion()]", TRUE) + var/datum/tgs_version/api_version = ApiVersion() + ExportService("[SERVICE_REQUEST_API_VERSION] [api_version.deprefixed_parameter]", TRUE) return TRUE //nothing to do for v3 @@ -166,7 +167,8 @@ /datum/tgs_api/v3210/Revision() if(!warned_revison) - TGS_ERROR_LOG("Use of TgsRevision on [ApiVersion()] origin_commit only points to master!") + var/datum/tgs_version/api_version = ApiVersion() + TGS_ERROR_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 diff --git a/code/modules/tgs/v3210/commands.dm b/code/modules/tgs/v3210/commands.dm index e5bc6f4895..5046631981 100644 --- a/code/modules/tgs/v3210/commands.dm +++ b/code/modules/tgs/v3210/commands.dm @@ -19,7 +19,7 @@ TGS_ERROR_LOG("Custom command [command_name] can't be used as it is empty or contains illegal characters!") warned_command_names[command_name] = TRUE continue - + if(command_name_types[command_name]) if(warnings_only) TGS_ERROR_LOG("Custom commands [command_name_types[command_name]] and [stc] have the same name, only [command_name_types[command_name]] will be available!") @@ -55,24 +55,24 @@ The MIT License Copyright (c) 2017 Jordan Brown -Permission is hereby granted, free of charge, -to any person obtaining a copy of this software and -associated documentation files (the "Software"), to -deal in the Software without restriction, including -without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom -the Software is furnished to do so, +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR -ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ diff --git a/code/modules/tgs/v4/api.dm b/code/modules/tgs/v4/api.dm index b61cddbeba..5c98a1a7a7 100644 --- a/code/modules/tgs/v4/api.dm +++ b/code/modules/tgs/v4/api.dm @@ -49,7 +49,7 @@ var/list/last_interop_response /datum/tgs_api/v4/ApiVersion() - return "4.0.0.0" + return new /datum/tgs_version("4.0.0.0") /datum/tgs_api/v4/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level) json_path = world.params[TGS4_PARAM_INFO_JSON] diff --git a/code/modules/tgs/v4/commands.dm b/code/modules/tgs/v4/commands.dm index af21a86fb6..1d9951bc04 100644 --- a/code/modules/tgs/v4/commands.dm +++ b/code/modules/tgs/v4/commands.dm @@ -46,24 +46,24 @@ The MIT License Copyright (c) 2017 Jordan Brown -Permission is hereby granted, free of charge, -to any person obtaining a copy of this software and -associated documentation files (the "Software"), to -deal in the Software without restriction, including -without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom -the Software is furnished to do so, +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR -ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ diff --git a/code/modules/tgs/v5/_defines.dm b/code/modules/tgs/v5/_defines.dm new file mode 100644 index 0000000000..1117a1df50 --- /dev/null +++ b/code/modules/tgs/v5/_defines.dm @@ -0,0 +1,121 @@ +#define DMAPI5_PARAM_SERVER_PORT "tgs_port" +#define DMAPI5_PARAM_ACCESS_IDENTIFIER "tgs_key" + +#define DMAPI5_BRIDGE_DATA "data" +#define DMAPI5_TOPIC_DATA "tgs_data" + +#define DMAPI5_BRIDGE_COMMAND_PORT_UPDATE 0 +#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_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier" +#define DMAPI5_RESPONSE_ERROR_MESSAGE "errorMessage" + +#define DMAPI5_BRIDGE_PARAMETER_COMMAND_TYPE "commandType" +#define DMAPI5_BRIDGE_PARAMETER_CURRENT_PORT "currentPort" +#define DMAPI5_BRIDGE_PARAMETER_VERSION "version" +#define DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE "chatMessage" +#define DMAPI5_BRIDGE_PARAMETER_CUSTOM_COMMANDS "customCommands" +#define DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL "minimumSecurityLevel" + +#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" +#define DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION "serverVersion" +#define DMAPI5_RUNTIME_INFORMATION_SERVER_PORT "serverPort" +#define DMAPI5_RUNTIME_INFORMATION_API_VALIDATE_ONLY "apiValidateOnly" +#define DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME "instanceName" +#define DMAPI5_RUNTIME_INFORMATION_REVISION "revision" +#define DMAPI5_RUNTIME_INFORMATION_TEST_MERGES "testMerges" +#define DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL "securityLevel" + +#define DMAPI5_CHAT_UPDATE_CHANNELS "channels" + +#define DMAPI5_TEST_MERGE_TIME_MERGED "timeMerged" +#define DMAPI5_TEST_MERGE_REVISION "revision" +#define DMAPI5_TEST_MERGE_TITLE_AT_MERGE "titleAtMerge" +#define DMAPI5_TEST_MERGE_BODY_AT_MERGE "bodyAtMerge" +#define DMAPI5_TEST_MERGE_URL "url" +#define DMAPI5_TEST_MERGE_AUTHOR "author" +#define DMAPI5_TEST_MERGE_NUMBER "number" +#define DMAPI5_TEST_MERGE_PULL_REQUEST_REVISION "pullRequestRevision" +#define DMAPI5_TEST_MERGE_COMMENT "comment" + +#define DMAPI5_CHAT_COMMAND_NAME "name" +#define DMAPI5_CHAT_COMMAND_PARAMS "params" +#define DMAPI5_CHAT_COMMAND_USER "user" + +#define DMAPI5_EVENT_NOTIFICATION_TYPE "type" +#define DMAPI5_EVENT_NOTIFICATION_PARAMETERS "parameters" + +#define DMAPI5_TOPIC_COMMAND_CHAT_COMMAND 0 +#define DMAPI5_TOPIC_COMMAND_EVENT_NOTIFICATION 1 +#define DMAPI5_TOPIC_COMMAND_CHANGE_PORT 2 +#define DMAPI5_TOPIC_COMMAND_CHANGE_REBOOT_STATE 3 +#define DMAPI5_TOPIC_COMMAND_INSTANCE_RENAMED 4 +#define DMAPI5_TOPIC_COMMAND_CHAT_CHANNELS_UPDATE 4 +#define DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE 5 + +#define DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE "commandType" +#define DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND "chatCommand" +#define DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION "eventNotification" +#define DMAPI5_TOPIC_PARAMETER_NEW_PORT "newPort" +#define DMAPI5_TOPIC_PARAMETER_NEW_REBOOT_STATE "newRebootState" +#define DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME "newInstanceName" +#define DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE "chatUpdate" + +#define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE "commandResponseMessage" +#define DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES "chatResponses" + +#define DMAPI5_REVISION_INFORMATION_COMMIT_SHA "commitSha" +#define DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA "originCommitSha" + +#define DMAPI5_CHAT_USER_ID "id" +#define DMAPI5_CHAT_USER_FRIENDLY_NAME "friendlyName" +#define DMAPI5_CHAT_USER_MENTION "mention" +#define DMAPI5_CHAT_USER_CHANNEL "channel" + +#define DMAPI5_CHAT_CHANNEL_ID "id" +#define DMAPI5_CHAT_CHANNEL_FRIENDLY_NAME "friendlyName" +#define DMAPI5_CHAT_CHANNEL_CONNECTION_NAME "connectionName" +#define DMAPI5_CHAT_CHANNEL_IS_ADMIN_CHANNEL "isAdminChannel" +#define DMAPI5_CHAT_CHANNEL_IS_PRIVATE_CHANNEL "isPrivateChannel" +#define DMAPI5_CHAT_CHANNEL_TAG "tag" + +#define DMAPI5_CUSTOM_CHAT_COMMAND_NAME "name" +#define DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT "helpText" +#define DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY "adminOnly" + +/* +The MIT License + +Copyright (c) 2020 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/code/modules/tgs/v5/api.dm b/code/modules/tgs/v5/api.dm new file mode 100644 index 0000000000..32f80c0ae4 --- /dev/null +++ b/code/modules/tgs/v5/api.dm @@ -0,0 +1,352 @@ +/datum/tgs_api/v5 + var/server_port + var/access_identifier + + var/instance_name + var/security_level + + var/reboot_mode = TGS_REBOOT_MODE_NORMAL + + var/list/intercepted_message_queue + + var/list/custom_commands + + var/list/test_merges + var/datum/tgs_revision_information/revision + var/list/chat_channels + + var/datum/tgs_event_handler/event_handler + +/datum/tgs_api/v5/ApiVersion() + return new /datum/tgs_version("5.0.0") + +/datum/tgs_api/v5/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level) + src.event_handler = event_handler + + server_port = world.params[DMAPI5_PARAM_SERVER_PORT] + access_identifier = world.params[DMAPI5_PARAM_ACCESS_IDENTIFIER] + + var/datum/tgs_version/api_version = ApiVersion() + 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_BRIDGE_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands())) + if(!istype(bridge_response)) + TGS_ERROR_LOG("Failed initial bridge request!") + return FALSE + + var/list/runtime_information = bridge_response[DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION] + if(!istype(runtime_information)) + TGS_ERROR_LOG("Failed to decode runtime information from bridge response: [json_encode(bridge_response)]!") + return FALSE + + if(runtime_information[DMAPI5_RUNTIME_INFORMATION_API_VALIDATE_ONLY]) + TGS_INFO_LOG("DMAPI validation, exiting...") + del(world) + + security_level = runtime_information[DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL] + instance_name = runtime_information[DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME] + version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION]) + + var/list/revisionData = runtime_information[DMAPI5_RUNTIME_INFORMATION_REVISION] + if(istype(revisionData)) + revision = new + revision.commit = revisionData[DMAPI5_REVISION_INFORMATION_COMMIT_SHA] + revision.origin_commit = revisionData[DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA] + else + TGS_ERROR_LOG("Failed to decode [DMAPI5_RUNTIME_INFORMATION_REVISION] from runtime information!") + + test_merges = list() + var/list/test_merge_json = runtime_information[DMAPI5_RUNTIME_INFORMATION_TEST_MERGES] + if(istype(test_merge_json)) + for(var/entry in test_merge_json) + var/datum/tgs_revision_information/test_merge/tm = new + tm.number = entry[DMAPI5_TEST_MERGE_NUMBER] + + var/list/revInfo = entry[DMAPI5_TEST_MERGE_REVISION] + if(revInfo) + tm.commit = revisionData[DMAPI5_REVISION_INFORMATION_COMMIT_SHA] + tm.origin_commit = revisionData[DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA] + else + TGS_WARNING_LOG("Failed to decode [DMAPI5_TEST_MERGE_REVISION] from test merge #[tm.number]!") + + tm.time_merged = text2num(entry[DMAPI5_TEST_MERGE_TIME_MERGED]) + tm.title = entry[DMAPI5_TEST_MERGE_TITLE_AT_MERGE] + tm.body = entry[DMAPI5_TEST_MERGE_BODY_AT_MERGE] + tm.url = entry[DMAPI5_TEST_MERGE_URL] + tm.author = entry[DMAPI5_TEST_MERGE_AUTHOR] + tm.pull_request_commit = entry[DMAPI5_TEST_MERGE_PULL_REQUEST_REVISION] + tm.comment = entry[DMAPI5_TEST_MERGE_COMMENT] + + test_merges += tm + else + TGS_WARNING_LOG("Failed to decode [DMAPI5_RUNTIME_INFORMATION_TEST_MERGES] from runtime information!") + + chat_channels = list() + DecodeChannels(runtime_information) + + return TRUE + +/datum/tgs_api/v5/OnInitializationComplete() + Bridge(DMAPI5_BRIDGE_COMMAND_PRIME) + + var/tgs4_secret_sleep_offline_sauce = 29051994 + var/old_sleep_offline = world.sleep_offline + world.sleep_offline = tgs4_secret_sleep_offline_sauce + sleep(1) + if(world.sleep_offline == tgs4_secret_sleep_offline_sauce) //if not someone changed it + world.sleep_offline = old_sleep_offline + +/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) + var/list/params = params2list(T) + var/json = params[DMAPI5_TOPIC_DATA] + if(!json) + return FALSE //continue world/Topic + + var/list/topic_parameters = json_decode(json) + if(!topic_parameters) + return TopicResponse("Invalid topic parameters json!"); + + 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() + + 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 + +/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 + + var/port = result[DMAPI5_BRIDGE_RESPONSE_NEW_PORT] + if(!isnum(port)) + return //this is valid, server may just want use to reboot + + if(port == 0) + //to byond 0 means any port and "none" means close vOv + port = "none" + + if(!world.OpenPort(port)) + TGS_ERROR_LOG("Unable to set port to [port]!") + +/datum/tgs_api/v5/InstanceName() + return instance_name + +/datum/tgs_api/v5/TestMerges() + return test_merges + +/datum/tgs_api/v5/EndProcess() + Bridge(DMAPI5_BRIDGE_COMMAND_KILL) + +/datum/tgs_api/v5/Revision() + return revision + +/datum/tgs_api/v5/ChatBroadcast(message, list/channels) + if(!length(channels)) + channels = ChatChannelInfo() + + var/list/ids = list() + for(var/I in channels) + 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)) + +/datum/tgs_api/v5/ChatTargetedBroadcast(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(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)) + 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/ChatChannelInfo() + return chat_channels + +/datum/tgs_api/v5/proc/DecodeChannels(chat_update_json) + var/list/chat_channels_json = chat_update_json[DMAPI5_CHAT_UPDATE_CHANNELS] + if(istype(chat_channels_json)) + chat_channels.Cut() + for(var/channel_json in chat_channels_json) + var/datum/tgs_chat_channel/channel = DecodeChannel(channel_json) + if(channel) + chat_channels += channel + else + TGS_WARNING_LOG("Failed to decode [DMAPI5_CHAT_UPDATE_CHANNELS] from channel update!") + +/datum/tgs_api/v5/proc/DecodeChannel(channel_json) + var/datum/tgs_chat_channel/channel = new + channel.id = channel_json[DMAPI5_CHAT_CHANNEL_ID] + channel.friendly_name = channel_json[DMAPI5_CHAT_CHANNEL_FRIENDLY_NAME] + channel.connection_name = channel_json[DMAPI5_CHAT_CHANNEL_CONNECTION_NAME] + 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] + return channel + +/datum/tgs_api/v5/SecurityLevel() + return security_level + +/* +The MIT License + +Copyright (c) 2020 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/code/modules/tgs/v5/commands.dm b/code/modules/tgs/v5/commands.dm new file mode 100644 index 0000000000..2775157656 --- /dev/null +++ b/code/modules/tgs/v5/commands.dm @@ -0,0 +1,68 @@ +/datum/tgs_api/v5/proc/ListCustomCommands() + var/results = list() + custom_commands = list() + for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command) + var/datum/tgs_chat_command/stc = new I + 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!") + 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!") + 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 + + return results + +/datum/tgs_api/v5/proc/HandleCustomCommand(list/command_json) + var/command = command_json[DMAPI5_CHAT_COMMAND_NAME] + var/user = command_json[DMAPI5_CHAT_COMMAND_USER] + var/params = command_json[DMAPI5_CHAT_COMMAND_PARAMS] + + var/datum/tgs_chat_user/u = new + u.id = user[DMAPI5_CHAT_USER_ID] + u.friendly_name = user[DMAPI5_CHAT_USER_FRIENDLY_NAME] + u.mention = user[DMAPI5_CHAT_USER_MENTION] + u.channel = DecodeChannel(user[DMAPI5_CHAT_USER_CHANNEL]) + + 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) + return TopicResponse("Unknown custom chat command: [command]!") + +/* + +The MIT License + +Copyright (c) 2020 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/code/modules/tgs/v5/undef.dm b/code/modules/tgs/v5/undef.dm new file mode 100644 index 0000000000..72d673b9b1 --- /dev/null +++ b/code/modules/tgs/v5/undef.dm @@ -0,0 +1,121 @@ +#undef DMAPI5_PARAM_SERVER_PORT +#undef DMAPI5_PARAM_ACCESS_IDENTIFIER + +#undef DMAPI5_BRIDGE_DATA +#undef DMAPI5_TOPIC_DATA + +#undef DMAPI5_BRIDGE_COMMAND_PORT_UPDATE +#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_PARAMETER_ACCESS_IDENTIFIER +#undef DMAPI5_RESPONSE_ERROR_MESSAGE + +#undef DMAPI5_BRIDGE_PARAMETER_COMMAND_TYPE +#undef DMAPI5_BRIDGE_PARAMETER_CURRENT_PORT +#undef DMAPI5_BRIDGE_PARAMETER_VERSION +#undef DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE +#undef DMAPI5_BRIDGE_PARAMETER_CUSTOM_COMMANDS +#undef DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL + +#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 +#undef DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION +#undef DMAPI5_RUNTIME_INFORMATION_SERVER_PORT +#undef DMAPI5_RUNTIME_INFORMATION_API_VALIDATE_ONLY +#undef DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME +#undef DMAPI5_RUNTIME_INFORMATION_REVISION +#undef DMAPI5_RUNTIME_INFORMATION_TEST_MERGES +#undef DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL + +#undef DMAPI5_CHAT_UPDATE_CHANNELS + +#undef DMAPI5_TEST_MERGE_TIME_MERGED +#undef DMAPI5_TEST_MERGE_REVISION +#undef DMAPI5_TEST_MERGE_TITLE_AT_MERGE +#undef DMAPI5_TEST_MERGE_BODY_AT_MERGE +#undef DMAPI5_TEST_MERGE_URL +#undef DMAPI5_TEST_MERGE_AUTHOR +#undef DMAPI5_TEST_MERGE_NUMBER +#undef DMAPI5_TEST_MERGE_PULL_REQUEST_REVISION +#undef DMAPI5_TEST_MERGE_COMMENT + +#undef DMAPI5_CHAT_COMMAND_NAME +#undef DMAPI5_CHAT_COMMAND_PARAMS +#undef DMAPI5_CHAT_COMMAND_USER + +#undef DMAPI5_EVENT_NOTIFICATION_TYPE +#undef DMAPI5_EVENT_NOTIFICATION_PARAMETERS + +#undef DMAPI5_TOPIC_COMMAND_CHAT_COMMAND +#undef DMAPI5_TOPIC_COMMAND_EVENT_NOTIFICATION +#undef DMAPI5_TOPIC_COMMAND_CHANGE_PORT +#undef DMAPI5_TOPIC_COMMAND_CHANGE_REBOOT_STATE +#undef DMAPI5_TOPIC_COMMAND_INSTANCE_RENAMED +#undef DMAPI5_TOPIC_COMMAND_CHAT_CHANNELS_UPDATE +#undef DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE + +#undef DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE +#undef DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND +#undef DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION +#undef DMAPI5_TOPIC_PARAMETER_NEW_PORT +#undef DMAPI5_TOPIC_PARAMETER_NEW_REBOOT_STATE +#undef DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME +#undef DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE + +#undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE +#undef DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES + +#undef DMAPI5_REVISION_INFORMATION_COMMIT_SHA +#undef DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA + +#undef DMAPI5_CHAT_USER_ID +#undef DMAPI5_CHAT_USER_FRIENDLY_NAME +#undef DMAPI5_CHAT_USER_MENTION +#undef DMAPI5_CHAT_USER_CHANNEL + +#undef DMAPI5_CHAT_CHANNEL_ID +#undef DMAPI5_CHAT_CHANNEL_FRIENDLY_NAME +#undef DMAPI5_CHAT_CHANNEL_CONNECTION_NAME +#undef DMAPI5_CHAT_CHANNEL_IS_ADMIN_CHANNEL +#undef DMAPI5_CHAT_CHANNEL_IS_PRIVATE_CHANNEL +#undef DMAPI5_CHAT_CHANNEL_TAG + +#undef DMAPI5_CUSTOM_CHAT_COMMAND_NAME +#undef DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT +#undef DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY + +/* +The MIT License + +Copyright (c) 2020 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ From ee97ac81328e5408f7d238a9a57bffd8d473aeb8 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 8 May 2020 04:32:57 -0700 Subject: [PATCH 02/62] ok --- code/__DEFINES/tgs.config.dm | 5 +++-- code/__DEFINES/tgs.dm | 39 +++++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/code/__DEFINES/tgs.config.dm b/code/__DEFINES/tgs.config.dm index 9f4f63a1fc..a2fcc7855a 100644 --- a/code/__DEFINES/tgs.config.dm +++ b/code/__DEFINES/tgs.config.dm @@ -4,8 +4,9 @@ #define TGS_READ_GLOBAL(Name) GLOB.##Name #define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value #define TGS_WORLD_ANNOUNCE(message) to_chat(world, "[html_encode(##message)]") -#define TGS_INFO_LOG(message) log_world("TGS: Info: [##message]") -#define TGS_ERROR_LOG(message) log_world("TGS: Error: [##message]") +#define TGS_INFO_LOG(message) log_world("TGS Info: [##message]") +#define TGS_WARNING_LOG(message) log_world("TGS Warn: [##message]") +#define TGS_ERROR_LOG(message) log_world("TGS Error: [##message]") #define TGS_NOTIFY_ADMINS(event) message_admins(##event) #define TGS_CLIENT_COUNT GLOB.clients.len #define TGS_PROTECT_DATUM(Path) GENERAL_PROTECT_DATUM(##Path) diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm index dcccfc9295..4e152ad3cf 100644 --- a/code/__DEFINES/tgs.dm +++ b/code/__DEFINES/tgs.dm @@ -1,5 +1,7 @@ //tgstation-server DMAPI +#define TGS_DMAPI_VERSION "5.0.0" + //All functions and datums outside this document are subject to change with any version and should not be relied on //CONFIGURATION @@ -17,7 +19,6 @@ //Required interfaces (fill in with your codebase equivalent): //create a global variable named `Name` and set it to `Value` -//These globals must not be modifiable from anywhere outside of the server tools #define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) //Read the value in the global variable `Name` @@ -26,10 +27,10 @@ //Set the value in the global variable `Name` to `Value` #define TGS_WRITE_GLOBAL(Name, Value) -//Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game priveledge escalation +//Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game use of DD -> TGS capabilities #define TGS_PROTECT_DATUM(Path) -//display an announcement `message` from the server to all players +//Display an announcement `message` from the server to all players #define TGS_WORLD_ANNOUNCE(message) //Notify current in-game administrators of a string `event` @@ -38,6 +39,9 @@ //Write an info `message` to a server log #define TGS_INFO_LOG(message) +//Write an warning `message` to a server log +#define TGS_WARNING_LOG(message) + //Write an error `message` to a server log #define TGS_ERROR_LOG(message) @@ -48,10 +52,11 @@ //EVENT CODES -#define TGS_EVENT_PORT_SWAP -2 //before a port change is about to happen, extra parameter is new port -#define TGS_EVENT_REBOOT_MODE_CHANGE -1 //before a reboot mode change, extras parameters are the current and new reboot mode enums +#define TGS_EVENT_REBOOT_MODE_CHANGE -1 //Before a reboot mode change, extras parameters are the current and new reboot mode enums +#define TGS_EVENT_PORT_SWAP -2 //Before a port change is about to happen, extra parameters is new port +#define TGS_EVENT_INSTANCE_RENAMED -3 //Before the instance is renamed, extra prameter is the new name -//See the descriptions for these codes here: https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/EventType.cs +//See the descriptions for the parameters of these codes here: https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/EventType.cs #define TGS_EVENT_REPO_RESET_ORIGIN 0 #define TGS_EVENT_REPO_CHECKOUT 1 #define TGS_EVENT_REPO_FETCH 2 @@ -63,9 +68,9 @@ #define TGS_EVENT_COMPILE_START 8 #define TGS_EVENT_COMPILE_CANCELLED 9 #define TGS_EVENT_COMPILE_FAILURE 10 -#define TGS_EVENT_COMPILE_COMPLETE 11 +#define TGS_EVENT_COMPILE_COMPLETE 11 // Note, this event fires before the new .dmb is loaded into the watchdog. Consider using the TGS_EVENT_DEPLOYMENT_COMPLETE instead #define TGS_EVENT_INSTANCE_AUTO_UPDATE_START 12 -#define TGS_EVENT_REPO_MERGE_CONFLICT 13 +#define TGS_EVENT_DEPLOYMENT_COMPLETE 13 //OTHER ENUMS @@ -80,6 +85,7 @@ //REQUIRED HOOKS //Call this somewhere in /world/New() that is always run +//IMPORTANT: This function may sleep! Other TGS functions will not succeed until it completes //event_handler: optional user defined event handler. The default behaviour is to broadcast the event in english to all connected admin channels //minimum_required_security_level: The minimum required security level to run the game in which the DMAPI is integrated /world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE) @@ -109,12 +115,12 @@ //represents a version of tgstation-server /datum/tgs_version - var/suite //The suite version, can be >=3 + var/suite //The suite/major version, can be >=3 //this group of variables can be null to represent a wild card - var/major //The major version var/minor //The minor version var/patch //The patch version + var/deprecated_patch //The legacy version var/raw_parameter //The unparsed parameter var/deprefixed_parameter //The version only bit of raw_parameter @@ -123,6 +129,10 @@ /datum/tgs_version/proc/Wildcard() return +//if the tgs_version equals some other_version +/datum/tgs_version/proc/Equals(datum/tgs_version/other_version) + return + //represents a merge of a GitHub pull request /datum/tgs_revision_information/test_merge var/number //pull request number @@ -179,7 +189,7 @@ return //Returns TRUE if the world was launched under the server tools and the API matches, FALSE otherwise -//No function below this succeeds if it returns FALSE +//No function below this succeeds if it returns FALSE or if TgsNew() has yet to be called /world/proc/TgsAvailable() return @@ -187,6 +197,11 @@ /world/proc/TgsVersion() return +//Gets the current /datum/tgs_version of the DMAPI being used +/world/proc/TgsApiVersion() + return + +//Gets the name of the TGS instance running the game /world/proc/TgsInstanceName() return @@ -255,4 +270,4 @@ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ \ No newline at end of file +*/ From 2b598c692c4f5f1e9f44c32c5ce5a077e995f51f Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sun, 10 May 2020 15:01:30 -0700 Subject: [PATCH 03/62] ok --- code/__HELPERS/unsorted.dm | 7 ++++++- code/datums/tgs_event_handler.dm | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 code/datums/tgs_event_handler.dm diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 13ac820778..830e933518 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1571,8 +1571,13 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) //empty string - use TgsTargetBroadcast with admin_only = FALSE //other string - use TgsChatBroadcast with the tag that matches config_setting, only works with TGS4, if using TGS3 the above method is used /proc/send2chat(message, config_setting) - if(config_setting == null || !world.TgsAvailable()) + if(config_setting == null) return + + UNTIL(GLOB.tgs_initialized) + if(!world.TgsAvailable()) + return + var/datum/tgs_version/version = world.TgsVersion() if(config_setting == "" || version.suite == 3) world.TgsTargetedChatBroadcast(message, FALSE) diff --git a/code/datums/tgs_event_handler.dm b/code/datums/tgs_event_handler.dm new file mode 100644 index 0000000000..a3324121bf --- /dev/null +++ b/code/datums/tgs_event_handler.dm @@ -0,0 +1,20 @@ +/datum/tgs_event_handler/impl/HandleEvent(event_code, ...) + switch(event_code) + if(TGS_EVENT_REBOOT_MODE_CHANGE) + var/list/reboot_mode_lookup = list ("[TGS_REBOOT_MODE_NORMAL]" = "be normal", "[TGS_REBOOT_MODE_SHUTDOWN]" = "shutdown the server", "[TGS_REBOOT_MODE_RESTART]" = "hard restart the server") + var old_reboot_mode = args[2] + var new_reboot_mode = args[3] + message_admins("TGS: Reboot will no longer [reboot_mode_lookup["[old_reboot_mode]"]], it will instead [reboot_mode_lookup["[new_reboot_mode]"]]") + if(TGS_EVENT_PORT_SWAP) + message_admins("TGS: Changing port from [world.port] to [args[2]]") + if(TGS_EVENT_INSTANCE_RENAMED) + message_admins("TGS: Instance renamed to from [world.TgsInstanceName()] to [args[2]]") + if(TGS_EVENT_COMPILE_START) + message_admins("TGS: Deployment started, new game version incoming...") + if(TGS_EVENT_COMPILE_CANCELLED) + message_admins("TGS: Deployment cancelled!") + if(TGS_EVENT_COMPILE_FAILURE) + message_admins("TGS: Deployment failed!") + if(TGS_EVENT_DEPLOYMENT_COMPLETE) + message_admins("TGS: Deployment complete!") + to_chat(world, "Server updated, changes will be applied on the next round...") From 452b360450c991ba787b9c2991faf0da66da8c5d Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sun, 10 May 2020 15:05:46 -0700 Subject: [PATCH 04/62] ok --- code/datums/helper_datums/getrev.dm | 18 +++++++++++------- code/game/world.dm | 15 ++++++++++++--- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm index 0b0fed407c..91b8e32566 100644 --- a/code/datums/helper_datums/getrev.dm +++ b/code/datums/helper_datums/getrev.dm @@ -5,16 +5,18 @@ var/list/testmerge = list() /datum/getrev/New() + commit = rustg_git_revparse("HEAD") + if(commit) + date = rustg_git_commit_date(commit) + originmastercommit = rustg_git_revparse("origin/master") + +/datum/getrev/proc/load_tgs_info() testmerge = world.TgsTestMerges() var/datum/tgs_revision_information/revinfo = world.TgsRevision() if(revinfo) commit = revinfo.commit originmastercommit = revinfo.origin_commit - else - commit = rustg_git_revparse("HEAD") - if(commit) - date = rustg_git_commit_date(commit) - originmastercommit = rustg_git_revparse("origin/master") + date = rustg_git_commit_date(commit) // goes to DD log and config_error.txt log_world(get_log_message()) @@ -77,7 +79,9 @@ msg += "No commit information" if(world.TgsAvailable()) var/datum/tgs_version/version = world.TgsVersion() - msg += "Server tools version: [version.raw_parameter]" + msg += "TGS version: [version.raw_parameter]" + var/datum/tgs_version/api_version = world.TgsApiVersion() + msg += "DMAPI version: [api_version.raw_parameter]" // Game mode odds msg += "
Current Informational Settings:" @@ -121,4 +125,4 @@ if(probabilities[ctag] > 0) var/percentage = round(probabilities[ctag] / sum * 100, 0.1) msg += "[ctag] [percentage]%" - to_chat(src, msg.Join("
")) \ No newline at end of file + to_chat(src, msg.Join("
")) diff --git a/code/game/world.dm b/code/game/world.dm index 10431e4af9..45ed035e58 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -23,10 +23,11 @@ GLOBAL_LIST(topic_status_cache) make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once) - TgsNew(minimum_required_security_level = TGS_SECURITY_TRUSTED) GLOB.revdata = new + InitTgs() + config.Load(params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER]) //SetupLogs depends on the RoundID, so lets check @@ -37,6 +38,9 @@ GLOBAL_LIST(topic_status_cache) #ifndef USE_CUSTOM_ERROR_HANDLER world.log = file("[GLOB.log_directory]/dd.log") +#else + if (TgsAvailable()) + world.log = file("[GLOB.log_directory]/dd.log") //not all runtimes trigger world/Error, so this is the only way to ensure we can see all of them. #endif load_admins() @@ -62,6 +66,10 @@ GLOBAL_LIST(topic_status_cache) if(TEST_RUN_PARAMETER in params) HandleTestRun() +/world/proc/InitTgs() + TgsNew(new /datum/tgs_event_handler/impl, TGS_SECURITY_TRUSTED) + GLOB.revdata.load_tgs_info() + /world/proc/HandleTestRun() //trigger things to run the whole process Master.sleep_offline_after_initializations = FALSE @@ -223,16 +231,17 @@ GLOBAL_LIST(topic_status_cache) qdel(src) //shut it down /world/Reboot(reason = 0, fast_track = FALSE) - TgsReboot() if (reason || fast_track) //special reboot, do none of the normal stuff if (usr) log_admin("[key_name(usr)] Has requested an immediate world restart via client side debugging tools") message_admins("[key_name_admin(usr)] Has requested an immediate world restart via client side debugging tools") - to_chat(world, "Rebooting World immediately due to host request") + to_chat(world, "Rebooting World immediately due to host request.") else to_chat(world, "Rebooting world...") Master.Shutdown() //run SS shutdowns + TgsReboot() + if(TEST_RUN_PARAMETER in params) FinishTestRun() return From 5aea034d881280eeef65d4268d5e856271fb1e04 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sun, 10 May 2020 15:12:13 -0700 Subject: [PATCH 05/62] k --- code/__HELPERS/unsorted.dm | 2 +- code/game/world.dm | 9 ++++++--- tgstation.dme | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 830e933518..e6663be5b9 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1573,7 +1573,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) /proc/send2chat(message, config_setting) if(config_setting == null) return - + UNTIL(GLOB.tgs_initialized) if(!world.TgsAvailable()) return diff --git a/code/game/world.dm b/code/game/world.dm index 45ed035e58..bd56ffdbf4 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -1,6 +1,7 @@ #define RESTART_COUNTER_PATH "data/round_counter.txt" GLOBAL_VAR(restart_counter) +GLOBAL_VAR_INIT(tgs_initialized, FALSE) GLOBAL_VAR(topic_status_lastcache) GLOBAL_LIST(topic_status_cache) @@ -38,9 +39,6 @@ GLOBAL_LIST(topic_status_cache) #ifndef USE_CUSTOM_ERROR_HANDLER world.log = file("[GLOB.log_directory]/dd.log") -#else - if (TgsAvailable()) - world.log = file("[GLOB.log_directory]/dd.log") //not all runtimes trigger world/Error, so this is the only way to ensure we can see all of them. #endif load_admins() @@ -69,6 +67,11 @@ GLOBAL_LIST(topic_status_cache) /world/proc/InitTgs() TgsNew(new /datum/tgs_event_handler/impl, TGS_SECURITY_TRUSTED) GLOB.revdata.load_tgs_info() +#ifdef USE_CUSTOM_ERROR_HANDLER + if (TgsAvailable()) + world.log = file("[GLOB.log_directory]/dd.log") //not all runtimes trigger world/Error, so this is the only way to ensure we can see all of them. +#endif + GLOB.tgs_initialized = TRUE /world/proc/HandleTestRun() //trigger things to run the whole process diff --git a/tgstation.dme b/tgstation.dme index d61a1c4ef9..34cf0e354a 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -366,6 +366,7 @@ #include "code\datums\shuttles.dm" #include "code\datums\soullink.dm" #include "code\datums\spawners_menu.dm" +#include "code\datums\tgs_event_handler.dm" #include "code\datums\verbs.dm" #include "code\datums\weakrefs.dm" #include "code\datums\world_topic.dm" From 07299f4cf029fc66856c135b1571f005463c33df Mon Sep 17 00:00:00 2001 From: DeltaFire Date: Thu, 14 May 2020 20:06:03 +0200 Subject: [PATCH 06/62] ... why do people not proofread their code Fixes 1. Neovgre recharging near sigils 2. Neovgre healing on brass floor 3. ... the weapon firing sound --- code/game/mecha/combat/neovgre.dm | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/code/game/mecha/combat/neovgre.dm b/code/game/mecha/combat/neovgre.dm index c678912a21..a660142962 100644 --- a/code/game/mecha/combat/neovgre.dm +++ b/code/game/mecha/combat/neovgre.dm @@ -62,18 +62,21 @@ /obj/mecha/combat/neovgre/process() ..() - if(GLOB.ratvar_awakens) // At this point only timley intervention by lord singulo could hople to stop the superweapon + if(GLOB.ratvar_awakens) // At this point only timley intervention by lord singulo could hope to stop the superweapon cell.charge = INFINITY max_integrity = INFINITY obj_integrity = max_integrity CHECK_TICK //Just to be on the safe side lag wise - else if(cell.charge < cell.maxcharge) - for(var/obj/effect/clockwork/sigil/transmission/T in range(SIGIL_ACCESS_RANGE, src)) - var/delta = min(recharge_rate, cell.maxcharge - cell.charge) - if (get_clockwork_power() <= delta) - cell.charge += delta - adjust_clockwork_power(-delta) - CHECK_TICK + else + if(cell.charge < cell.maxcharge) + for(var/obj/effect/clockwork/sigil/transmission/T in range(SIGIL_ACCESS_RANGE, src)) + var/delta = min(recharge_rate, cell.maxcharge - cell.charge) + if (get_clockwork_power() >= delta) + cell.charge += delta + adjust_clockwork_power(-delta) + if(obj_integrity < max_integrity && istype(loc, /turf/open/floor/clockwork)) + obj_integrity += min(max_integrity - obj_integrity, max_integrity / 100) + CHECK_TICK /obj/mecha/combat/neovgre/Initialize() .=..() @@ -90,7 +93,7 @@ energy_drain = 30 name = "Aribter Laser Cannon" desc = "Please re-attach this to neovgre and stop asking questions about why it looks like a normal Nanotrasen issue Solaris laser cannon - Nezbere" - fire_sound = "sound/weapons/neovgre_laser.ogg" + fire_sound = 'sound/weapons/neovgre_laser.ogg' /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser/heavy/neovgre/can_attach(obj/mecha/combat/neovgre/M) if(istype(M)) From 87492b1a3590db8aae4a5db5683d8ef2a6455c37 Mon Sep 17 00:00:00 2001 From: DeltaFire Date: Thu, 14 May 2020 20:29:30 +0200 Subject: [PATCH 07/62] Changes repair value 0.5% per process tick ~ 1sec, so ~200sec to fully heal it without outside interference if on clockie floor. --- code/game/mecha/combat/neovgre.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/mecha/combat/neovgre.dm b/code/game/mecha/combat/neovgre.dm index a660142962..fdcc4df151 100644 --- a/code/game/mecha/combat/neovgre.dm +++ b/code/game/mecha/combat/neovgre.dm @@ -75,7 +75,7 @@ cell.charge += delta adjust_clockwork_power(-delta) if(obj_integrity < max_integrity && istype(loc, /turf/open/floor/clockwork)) - obj_integrity += min(max_integrity - obj_integrity, max_integrity / 100) + obj_integrity += min(max_integrity - obj_integrity, max_integrity / 200) CHECK_TICK /obj/mecha/combat/neovgre/Initialize() From 7d75166b7bf680dadc5b98f5a84d134f5a32f1b5 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 15 May 2020 23:20:34 -0700 Subject: [PATCH 08/62] tgs 5.1.1 --- code/modules/tgs/core/core.dm | 22 ++++++++++++++++----- code/modules/tgs/core/datum.dm | 4 +++- code/modules/tgs/v3210/api.dm | 2 +- code/modules/tgs/v4/api.dm | 5 +---- code/modules/tgs/v5/_defines.dm | 7 +++++-- code/modules/tgs/v5/api.dm | 34 ++++++++++++++++++++++++++------- code/modules/tgs/v5/undef.dm | 3 +++ 7 files changed, 57 insertions(+), 20 deletions(-) diff --git a/code/modules/tgs/core/core.dm b/code/modules/tgs/core/core.dm index 144f33926f..a74c9bbab2 100644 --- a/code/modules/tgs/core/core.dm +++ b/code/modules/tgs/core/core.dm @@ -1,11 +1,17 @@ /world/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE) var/current_api = TGS_READ_GLOBAL(tgs) if(current_api) - TGS_ERROR_LOG("TgsNew(): TGS API datum already set ([current_api])! Was TgsNew() called more than once?") + TGS_ERROR_LOG("API datum already set (\ref[current_api] ([current_api]))! Was TgsNew() called more than once?") + return + + if(!(minimum_required_security_level in list(TGS_SECURITY_ULTRASAFE, TGS_SECURITY_SAFE, TGS_SECURITY_TRUSTED))) + TGS_ERROR_LOG("Invalid minimum_required_security_level: [minimum_required_security_level]!") return #ifdef TGS_V3_API - minimum_required_security_level = TGS_SECURITY_TRUSTED + if(minimum_required_security_level != TGS_SECURITY_TRUSTED) + TGS_WARNING_LOG("V3 DMAPI requires trusted security!") + minimum_required_security_level = TGS_SECURITY_TRUSTED #endif var/raw_parameter = world.params[TGS_VERSION_PARAMETER] if(!raw_parameter) @@ -13,7 +19,7 @@ var/datum/tgs_version/version = new(raw_parameter) if(!version.Valid(FALSE)) - TGS_ERROR_LOG("Failed to validate TGS version parameter: [raw_parameter]!") + TGS_ERROR_LOG("Failed to validate DMAPI version parameter: [raw_parameter]!") return var/api_datum @@ -21,6 +27,7 @@ if(3) #ifndef TGS_V3_API TGS_ERROR_LOG("Detected V3 API but TGS_V3_API isn't defined!") + return #else switch(version.minor) if(2) @@ -43,11 +50,16 @@ return TGS_INFO_LOG("Activating API for version [version.deprefixed_parameter]") - var/datum/tgs_api/new_api = new api_datum(version) + + if(event_handler && !istype(event_handler)) + 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) TGS_WRITE_GLOBAL(tgs, new_api) - var/result = new_api.OnWorldNew(event_handler, minimum_required_security_level) + var/result = new_api.OnWorldNew(minimum_required_security_level) if(!result || result == TGS_UNIMPLEMENTED) TGS_WRITE_GLOBAL(tgs, null) TGS_ERROR_LOG("Failed to activate API!") diff --git a/code/modules/tgs/core/datum.dm b/code/modules/tgs/core/datum.dm index 3adecbb556..ccc75fe344 100644 --- a/code/modules/tgs/core/datum.dm +++ b/code/modules/tgs/core/datum.dm @@ -2,9 +2,11 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null) /datum/tgs_api var/datum/tgs_version/version + var/datum/tgs_event_handler/event_handler -/datum/tgs_api/New(datum/tgs_version/version) +/datum/tgs_api/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version) . = ..() + src.event_handler = event_handler src.version = version /datum/tgs_api/latest diff --git a/code/modules/tgs/v3210/api.dm b/code/modules/tgs/v3210/api.dm index 5327b9276b..0fc0076c8b 100644 --- a/code/modules/tgs/v3210/api.dm +++ b/code/modules/tgs/v3210/api.dm @@ -56,7 +56,7 @@ /datum/tgs_api/v3210/proc/file2list(filename) return splittext(trim_left(trim_right(file2text(filename))), "\n") -/datum/tgs_api/v3210/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level) //don't use event handling in this version +/datum/tgs_api/v3210/OnWorldNew(minimum_required_security_level) . = FALSE comms_key = world.params[SERVICE_WORLD_PARAM] diff --git a/code/modules/tgs/v4/api.dm b/code/modules/tgs/v4/api.dm index 5c98a1a7a7..ba18aab688 100644 --- a/code/modules/tgs/v4/api.dm +++ b/code/modules/tgs/v4/api.dm @@ -43,15 +43,13 @@ var/list/cached_test_merges var/datum/tgs_revision_information/cached_revision - var/datum/tgs_event_handler/event_handler - var/export_lock = FALSE var/list/last_interop_response /datum/tgs_api/v4/ApiVersion() return new /datum/tgs_version("4.0.0.0") -/datum/tgs_api/v4/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level) +/datum/tgs_api/v4/OnWorldNew(minimum_required_security_level) json_path = world.params[TGS4_PARAM_INFO_JSON] if(!json_path) TGS_ERROR_LOG("Missing [TGS4_PARAM_INFO_JSON] world parameter!") @@ -76,7 +74,6 @@ security_level = cached_json["securityLevel"] chat_channels_json_path = cached_json["chatChannelsJson"] chat_commands_json_path = cached_json["chatCommandsJson"] - src.event_handler = event_handler instance_name = cached_json["instanceName"] ListCustomCommands() diff --git a/code/modules/tgs/v5/_defines.dm b/code/modules/tgs/v5/_defines.dm index 1117a1df50..2baf3e12d7 100644 --- a/code/modules/tgs/v5/_defines.dm +++ b/code/modules/tgs/v5/_defines.dm @@ -60,8 +60,10 @@ #define DMAPI5_TOPIC_COMMAND_CHANGE_PORT 2 #define DMAPI5_TOPIC_COMMAND_CHANGE_REBOOT_STATE 3 #define DMAPI5_TOPIC_COMMAND_INSTANCE_RENAMED 4 -#define DMAPI5_TOPIC_COMMAND_CHAT_CHANNELS_UPDATE 4 -#define DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE 5 +#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_WATCHDOG_REATTACH 8 #define DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE "commandType" #define DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND "chatCommand" @@ -70,6 +72,7 @@ #define DMAPI5_TOPIC_PARAMETER_NEW_REBOOT_STATE "newRebootState" #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_RESPONSE_COMMAND_RESPONSE_MESSAGE "commandResponseMessage" #define DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES "chatResponses" diff --git a/code/modules/tgs/v5/api.dm b/code/modules/tgs/v5/api.dm index 32f80c0ae4..eedefb2877 100644 --- a/code/modules/tgs/v5/api.dm +++ b/code/modules/tgs/v5/api.dm @@ -15,18 +15,15 @@ var/datum/tgs_revision_information/revision var/list/chat_channels - var/datum/tgs_event_handler/event_handler - /datum/tgs_api/v5/ApiVersion() - return new /datum/tgs_version("5.0.0") - -/datum/tgs_api/v5/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level) - src.event_handler = event_handler + return new /datum/tgs_version("5.1.1") +/datum/tgs_api/v5/OnWorldNew(minimum_required_security_level) 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_BRIDGE_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands())) if(!istype(bridge_response)) TGS_ERROR_LOG("Failed initial bridge request!") @@ -41,9 +38,9 @@ TGS_INFO_LOG("DMAPI validation, exiting...") del(world) + version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION]) security_level = runtime_information[DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL] instance_name = runtime_information[DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME] - version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION]) var/list/revisionData = runtime_information[DMAPI5_RUNTIME_INFORMATION_REVISION] if(istype(revisionData)) @@ -84,6 +81,10 @@ return TRUE +/datum/tgs_api/v5/proc/RequireInitialBridgeResponse() + while(!version) + sleep(1) + /datum/tgs_api/v5/OnInitializationComplete() Bridge(DMAPI5_BRIDGE_COMMAND_PRIME) @@ -197,6 +198,20 @@ server_port = new_port return TopicResponse() + if(DMAPI5_TOPIC_COMMAND_HEARTBEAT) + return TopicResponse() + if(DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH) + var/new_version_string = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION] + if (!istext(new_version_string)) + return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]]") + + 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 TopicResponse() return TopicResponse("Unknown command: [command]") @@ -252,15 +267,18 @@ TGS_ERROR_LOG("Unable to set port to [port]!") /datum/tgs_api/v5/InstanceName() + RequireInitialBridgeResponse() return instance_name /datum/tgs_api/v5/TestMerges() + RequireInitialBridgeResponse() return test_merges /datum/tgs_api/v5/EndProcess() Bridge(DMAPI5_BRIDGE_COMMAND_KILL) /datum/tgs_api/v5/Revision() + RequireInitialBridgeResponse() return revision /datum/tgs_api/v5/ChatBroadcast(message, list/channels) @@ -298,6 +316,7 @@ Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message)) /datum/tgs_api/v5/ChatChannelInfo() + RequireInitialBridgeResponse() return chat_channels /datum/tgs_api/v5/proc/DecodeChannels(chat_update_json) @@ -322,6 +341,7 @@ return channel /datum/tgs_api/v5/SecurityLevel() + RequireInitialBridgeResponse() return security_level /* diff --git a/code/modules/tgs/v5/undef.dm b/code/modules/tgs/v5/undef.dm index 72d673b9b1..b27e3abe95 100644 --- a/code/modules/tgs/v5/undef.dm +++ b/code/modules/tgs/v5/undef.dm @@ -62,6 +62,8 @@ #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_WATCHDOG_REATTACH #undef DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE #undef DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND @@ -70,6 +72,7 @@ #undef DMAPI5_TOPIC_PARAMETER_NEW_REBOOT_STATE #undef DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME #undef DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE +#undef DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION #undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE #undef DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES From 1d6ff8380f0d763eff3e030326a340051a5a0296 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Fri, 15 May 2020 23:22:13 -0700 Subject: [PATCH 09/62] update --- code/__DEFINES/tgs.dm | 62 ++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm index 4e152ad3cf..e164a4ec96 100644 --- a/code/__DEFINES/tgs.dm +++ b/code/__DEFINES/tgs.dm @@ -1,6 +1,6 @@ //tgstation-server DMAPI -#define TGS_DMAPI_VERSION "5.0.0" +#define TGS_DMAPI_VERSION "5.1.1" //All functions and datums outside this document are subject to change with any version and should not be relied on @@ -54,7 +54,8 @@ #define TGS_EVENT_REBOOT_MODE_CHANGE -1 //Before a reboot mode change, extras parameters are the current and new reboot mode enums #define TGS_EVENT_PORT_SWAP -2 //Before a port change is about to happen, extra parameters is new port -#define TGS_EVENT_INSTANCE_RENAMED -3 //Before the instance is renamed, extra prameter is the new name +#define TGS_EVENT_INSTANCE_RENAMED -3 //Before the instance is renamed, extra parameter is the new name +#define TGS_EVENT_WATCHDOG_REATTACH -4 //After the watchdog reattaches to DD, extra parameter is the new /datum/tgs_version of the server //See the descriptions for the parameters of these codes here: https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/EventType.cs #define TGS_EVENT_REPO_RESET_ORIGIN 0 @@ -70,7 +71,10 @@ #define TGS_EVENT_COMPILE_FAILURE 10 #define TGS_EVENT_COMPILE_COMPLETE 11 // Note, this event fires before the new .dmb is loaded into the watchdog. Consider using the TGS_EVENT_DEPLOYMENT_COMPLETE instead #define TGS_EVENT_INSTANCE_AUTO_UPDATE_START 12 -#define TGS_EVENT_DEPLOYMENT_COMPLETE 13 +#define TGS_EVENT_REPO_MERGE_CONFLICT 13 +#define TGS_EVENT_DEPLOYMENT_COMPLETE 14 +#define TGS_EVENT_WATCHDOG_SHUTDOWN 15 +#define TGS_EVENT_WATCHDOG_DETACH 16 //OTHER ENUMS @@ -85,7 +89,7 @@ //REQUIRED HOOKS //Call this somewhere in /world/New() that is always run -//IMPORTANT: This function may sleep! Other TGS functions will not succeed until it completes +//IMPORTANT: This function may sleep! //event_handler: optional user defined event handler. The default behaviour is to broadcast the event in english to all connected admin channels //minimum_required_security_level: The minimum required security level to run the game in which the DMAPI is integrated /world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE) @@ -193,6 +197,32 @@ /world/proc/TgsAvailable() return +//Forces a hard reboot of BYOND by ending the process +//unlike del(world) clients will try to reconnect +//If the service has not requested a shutdown, the next server will take over +/world/proc/TgsEndProcess() + return + +//Send a message to non-admin connected chats +//message: The message to send +//admin_only: If TRUE, message will instead be sent to only admin connected chats +/world/proc/TgsTargetedChatBroadcast(message, admin_only) + return + +//Send a private message to a specific user +//message: The message to send +//user: The /datum/tgs_chat_user to send to +/world/proc/TgsChatPrivateMessage(message, datum/tgs_chat_user/user) + return + +//The following functions will sleep if a call to TgsNew() is sleeping + +//Sends a message to connected game chats +//message: The message to send +//channels: optional channels to limit the broadcast to +/world/proc/TgsChatBroadcast(message, list/channels) + return + //Gets the current /datum/tgs_version of the server tools running the server /world/proc/TgsVersion() return @@ -217,34 +247,10 @@ /world/proc/TgsTestMerges() return -//Forces a hard reboot of BYOND by ending the process -//unlike del(world) clients will try to reconnect -//If the service has not requested a shutdown, the next server will take over -/world/proc/TgsEndProcess() - return - //Gets a list of connected tgs_chat_channel /world/proc/TgsChatChannelInfo() return -//Sends a message to connected game chats -//message: The message to send -//channels: optional channels to limit the broadcast to -/world/proc/TgsChatBroadcast(message, list/channels) - return - -//Send a message to non-admin connected chats -//message: The message to send -//admin_only: If TRUE, message will instead be sent to only admin connected chats -/world/proc/TgsTargetedChatBroadcast(message, admin_only) - return - -//Send a private message to a specific user -//message: The message to send -//user: The /datum/tgs_chat_user to send to -/world/proc/TgsChatPrivateMessage(message, datum/tgs_chat_user/user) - return - /* The MIT License From 2eb3089ccb974bad4760548d52c1d17536e79715 Mon Sep 17 00:00:00 2001 From: Ghommie <42542238+Ghommie@users.noreply.github.com> Date: Sun, 17 May 2020 23:38:26 +0200 Subject: [PATCH 10/62] Certain packs (mostly armory/security) can not be privately bought anymore. --- code/modules/cargo/console.dm | 11 ++++++---- code/modules/cargo/packs.dm | 1 + code/modules/cargo/packs/armory.dm | 1 + code/modules/cargo/packs/engine.dm | 2 +- code/modules/cargo/packs/medical.dm | 3 ++- code/modules/cargo/packs/science.dm | 1 + code/modules/cargo/packs/security.dm | 21 +++++++++++++++++++ code/modules/cargo/packs/vending.dm | 1 + tgui-next/packages/tgui/interfaces/Cargo.js | 4 ++++ tgui-next/packages/tgui/public/tgui.bundle.js | 4 ++-- 10 files changed, 41 insertions(+), 8 deletions(-) diff --git a/code/modules/cargo/console.dm b/code/modules/cargo/console.dm index d1d895ca69..6968a5ccd8 100644 --- a/code/modules/cargo/console.dm +++ b/code/modules/cargo/console.dm @@ -119,6 +119,7 @@ var/list/data = list() data["requestonly"] = requestonly data["supplies"] = list() + data["emagged"] = obj_flags & EMAGGED for(var/pack in SSshuttle.supply_packs) var/datum/supply_pack/P = SSshuttle.supply_packs[pack] if(!data["supplies"][P.group]) @@ -133,7 +134,8 @@ "cost" = P.cost, "id" = pack, "desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name. - "access" = P.access + "access" = P.access, + "can_private_buy" = P.can_private_buy )) return data @@ -195,9 +197,10 @@ rank = "Silicon" var/datum/bank_account/account - if(self_paid && ishuman(usr)) - var/mob/living/carbon/human/H = usr - var/obj/item/card/id/id_card = H.get_idcard(TRUE) + if(self_paid) + if(!pack.can_private_buy && !(obj_flags & EMAGGED)) + return + var/obj/item/card/id/id_card = usr.get_idcard(TRUE) if(!istype(id_card)) say("No ID card detected.") return diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index 96557d58ef..f82e16ad5c 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -15,6 +15,7 @@ var/special_enabled = FALSE var/DropPodOnly = FALSE //only usable by the Bluespace Drop Pod via the express cargo console var/admin_spawned = FALSE //Can only an admin spawn this crate? + var/can_private_buy = TRUE //Can it be purchased privately by each crewmember? /datum/supply_pack/proc/generate(atom/A, datum/bank_account/paying_account) var/obj/structure/closet/crate/C diff --git a/code/modules/cargo/packs/armory.dm b/code/modules/cargo/packs/armory.dm index bcfbc83001..3a3357cc42 100644 --- a/code/modules/cargo/packs/armory.dm +++ b/code/modules/cargo/packs/armory.dm @@ -10,6 +10,7 @@ group = "Armory" access = ACCESS_ARMORY crate_type = /obj/structure/closet/crate/secure/weapon + can_private_buy = FALSE /datum/supply_pack/security/armory/bulletarmor name = "Bulletproof Armor Crate" diff --git a/code/modules/cargo/packs/engine.dm b/code/modules/cargo/packs/engine.dm index 89775c07ee..499881a110 100644 --- a/code/modules/cargo/packs/engine.dm +++ b/code/modules/cargo/packs/engine.dm @@ -148,7 +148,7 @@ crate_name = "supermatter shard crate" crate_type = /obj/structure/closet/crate/secure/engineering dangerous = TRUE - + /datum/supply_pack/engine/tesla_coils name = "Tesla Coil Crate" desc = "Whether it's high-voltage executions, creating research points, or just plain old power generation: This pack of four Tesla coils can do it all!" diff --git a/code/modules/cargo/packs/medical.dm b/code/modules/cargo/packs/medical.dm index 5d02bbe60f..ab188f235b 100644 --- a/code/modules/cargo/packs/medical.dm +++ b/code/modules/cargo/packs/medical.dm @@ -252,6 +252,7 @@ crate_name = "virus crate" crate_type = /obj/structure/closet/crate/secure/plasma dangerous = TRUE + can_private_buy = FALSE /datum/supply_pack/medical/anitvirus name = "Virus Containment Crate" @@ -271,4 +272,4 @@ /obj/item/storage/box/syringes, /obj/item/storage/box/beakers) crate_name = "virus containment unit crate" - crate_type = /obj/structure/closet/crate/secure/plasma \ No newline at end of file + crate_type = /obj/structure/closet/crate/secure/plasma diff --git a/code/modules/cargo/packs/science.dm b/code/modules/cargo/packs/science.dm index 51bb2b728f..fd6fee362d 100644 --- a/code/modules/cargo/packs/science.dm +++ b/code/modules/cargo/packs/science.dm @@ -17,6 +17,7 @@ cost = 2500 contains = list (/obj/item/reagent_containers/food/snacks/cube/ape) crate_name = "ape cube crate" + can_private_buy = FALSE /datum/supply_pack/science/beakers name = "Chemistry Beakers Crate" diff --git a/code/modules/cargo/packs/security.dm b/code/modules/cargo/packs/security.dm index d134d7bab8..a48874e974 100644 --- a/code/modules/cargo/packs/security.dm +++ b/code/modules/cargo/packs/security.dm @@ -10,6 +10,7 @@ group = "Security" access = ACCESS_SECURITY crate_type = /obj/structure/closet/crate/secure/gear + can_private_buy = FALSE /datum/supply_pack/security/ammo name = "Ammo Crate - General Purpose" @@ -57,6 +58,7 @@ /obj/item/toy/crayon/white, /obj/item/clothing/head/fedora/det_hat) crate_name = "forensics crate" + can_private_buy = TRUE /datum/supply_pack/security/helmets name = "Helmets Crate" @@ -134,6 +136,7 @@ /obj/item/grenade/barrier) cost = 2000 crate_name = "security barriers crate" + can_private_buy = TRUE /datum/supply_pack/security/securityclothes name = "Security Clothing Crate" @@ -152,6 +155,7 @@ /obj/item/clothing/suit/armor/hos/navyblue, /obj/item/clothing/head/beret/sec/navyhos) crate_name = "security clothing crate" + can_private_buy = TRUE /datum/supply_pack/security/supplies name = "Security Supplies Crate" @@ -179,6 +183,7 @@ contains = list(/obj/item/clothing/head/helmet/justice, /obj/item/clothing/mask/gas/sechailer) crate_name = "security clothing crate" + can_private_buy = TRUE /datum/supply_pack/security/baton name = "Stun Batons Crate" @@ -207,3 +212,19 @@ /obj/item/storage/box/wall_flash, /obj/item/storage/box/wall_flash) crate_name = "wall-mounted flash crate" + +/datum/supply_pack/security/hunting + name = "Hunting Gear" + desc = "Even in space, we can find prey to hunt, this crate contains everthing a fine hunter needs to have a sporting time. This crate needs armory access to open. A true huntter only needs a fine bottle of cognac, a nice coat, some good o' cigars, and of cource a hunting shotgun. " + cost = 3500 + contraband = TRUE + contains = list(/obj/item/clothing/head/flatcap, + /obj/item/clothing/suit/hooded/wintercoat/captain, + /obj/item/reagent_containers/food/drinks/bottle/cognac, + /obj/item/storage/fancy/cigarettes/cigars/havana, + /obj/item/clothing/gloves/color/white, + /obj/item/clothing/under/rank/civilian/curator, + /obj/item/gun/ballistic/shotgun/lethal) + access = ACCESS_ARMORY + crate_name = "sporting crate" + crate_type = /obj/structure/closet/crate/secure // Would have liked a wooden crate but access >:( diff --git a/code/modules/cargo/packs/vending.dm b/code/modules/cargo/packs/vending.dm index 344d19f6c9..810cfd8d6e 100644 --- a/code/modules/cargo/packs/vending.dm +++ b/code/modules/cargo/packs/vending.dm @@ -99,6 +99,7 @@ contains = list(/obj/machinery/vending/security) crate_name = "SecTech supply crate" crate_type = /obj/structure/closet/crate/secure/gear + can_private_buy = FALSE /datum/supply_pack/vending/snack name = "Snack Supply Crate" diff --git a/tgui-next/packages/tgui/interfaces/Cargo.js b/tgui-next/packages/tgui/interfaces/Cargo.js index b31a25c611..133b1b00ab 100644 --- a/tgui-next/packages/tgui/interfaces/Cargo.js +++ b/tgui-next/packages/tgui/interfaces/Cargo.js @@ -154,6 +154,10 @@ const Catalog = props => {