diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 82252adb17..4439daea55 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -468,11 +468,11 @@ jobs: sleep 10 set +e test_output=$(dotnet test -c ${{ matrix.configuration }} --no-build --filter TestCategory=RequiresDatabase --logger "GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true" --collect:"XPlat Code Coverage" --settings ../../build/ci.runsettings --results-directory ../../TestResults) - succeeded=$? + retval=$? set -e cd ../.. echo $test_output > ./test_output.txt - if [[ $retVal -ne 0 ]]; then + if [[ $retval -ne 0 ]]; then echo "succeeded=NO" >> $GITHUB_OUTPUT else echo "succeeded=YES" >> $GITHUB_OUTPUT diff --git a/build/Dockerfile b/build/Dockerfile index 5c7c7bd05a..a65df7defe 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -24,7 +24,7 @@ WORKDIR /repo/build COPY build/Common.props Common.props COPY build/NugetCommon.props NugetCommon.props COPY build/Version.props Version.props -COPY build/ControlPanelVersion.props ControlPanelVersion.props +COPY build/WebpanelVersion.props WebpanelVersion.props COPY build/SrcCommon.props SrcCommon.props WORKDIR /repo/src/Tgstation.Server.Host diff --git a/build/TestCommon.props b/build/TestCommon.props index 5562510f72..234e06735a 100644 --- a/build/TestCommon.props +++ b/build/TestCommon.props @@ -13,7 +13,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/build/Version.props b/build/Version.props index 0f6cb5b904..3478f0fc6f 100644 --- a/build/Version.props +++ b/build/Version.props @@ -1,16 +1,16 @@ - + - 5.17.3 + 5.18.0 4.7.1 - 9.13.0 + 9.14.0 7.0.0 - 12.0.1 - 14.1.0 - 6.6.2 - 5.6.2 + 12.2.0 + 14.2.0 + 6.7.0 + 5.7.0 1.4.0 1.2.1 1.0.2 diff --git a/build/ControlPanelVersion.props b/build/WebpanelVersion.props similarity index 80% rename from build/ControlPanelVersion.props rename to build/WebpanelVersion.props index 02d75e7142..3e3a8aa08c 100644 --- a/build/ControlPanelVersion.props +++ b/build/WebpanelVersion.props @@ -1,6 +1,6 @@ - 4.26.2 + 5.1.0 diff --git a/docs/Features.dox b/docs/Features.dox index 725b26d947..64eca5a3f7 100644 --- a/docs/Features.dox +++ b/docs/Features.dox @@ -87,4 +87,5 @@ tgstation-server is a BYOND server managment suite. It includes all the followin - Functions with all 3 DreamDaemon security levels - Provides notifications of TGS side events - Allows specifying the .dmb's minimum required security level + - Broadcast messages to clients */ diff --git a/src/DMAPI/tgs.dm b/src/DMAPI/tgs.dm index 0cc106ec9c..b0e97e05e9 100644 --- a/src/DMAPI/tgs.dm +++ b/src/DMAPI/tgs.dm @@ -1,6 +1,6 @@ // tgstation-server DMAPI -#define TGS_DMAPI_VERSION "6.6.2" +#define TGS_DMAPI_VERSION "6.7.0" // All functions and datums outside this document are subject to change with any version and should not be relied on. diff --git a/src/DMAPI/tgs/v5/__interop_version.dm b/src/DMAPI/tgs/v5/__interop_version.dm index 1b52b31d6a..83420d130a 100644 --- a/src/DMAPI/tgs/v5/__interop_version.dm +++ b/src/DMAPI/tgs/v5/__interop_version.dm @@ -1 +1 @@ -"5.6.2" +"5.7.0" diff --git a/src/DMAPI/tgs/v5/_defines.dm b/src/DMAPI/tgs/v5/_defines.dm index bdcd4e4dd5..48969c0c7d 100644 --- a/src/DMAPI/tgs/v5/_defines.dm +++ b/src/DMAPI/tgs/v5/_defines.dm @@ -80,6 +80,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_PARAMETER_COMMAND_TYPE "commandType" #define DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND "chatCommand" @@ -89,6 +90,7 @@ #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_COMMAND_RESPONSE "commandResponse" #define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE "commandResponseMessage" diff --git a/src/DMAPI/tgs/v5/topic.dm b/src/DMAPI/tgs/v5/topic.dm index d7d4712138..2ef0c70a97 100644 --- a/src/DMAPI/tgs/v5/topic.dm +++ b/src/DMAPI/tgs/v5/topic.dm @@ -94,7 +94,7 @@ 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]]") + return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]") if(event_handler != null) event_handler.HandleEvent(TGS_EVENT_PORT_SWAP, new_port) @@ -141,7 +141,7 @@ 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]]") + return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]") server_port = new_port return TopicResponse() @@ -157,7 +157,7 @@ var/error_message = null if (new_port != null) if (!isnum(new_port) || !(new_port > 0)) - error_message = "Invalid [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]" + error_message = "Invalid [DMAPI5_TOPIC_PARAMETER_NEW_PORT]" else server_port = new_port @@ -165,7 +165,7 @@ if (!istext(new_version_string)) if(error_message != null) error_message += ", " - error_message += "Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]]" + 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) @@ -267,4 +267,16 @@ 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() + return TopicResponse("Unknown command: [command]") + +/datum/tgs_api/v5/proc/WorldBroadcast(message) + set waitfor = FALSE + TGS_WORLD_ANNOUNCE(message) diff --git a/src/DMAPI/tgs/v5/undefs.dm b/src/DMAPI/tgs/v5/undefs.dm index f163adaaaf..fd1ed7e4cf 100644 --- a/src/DMAPI/tgs/v5/undefs.dm +++ b/src/DMAPI/tgs/v5/undefs.dm @@ -78,6 +78,9 @@ #undef DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE #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_PARAMETER_COMMAND_TYPE #undef DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND @@ -87,6 +90,7 @@ #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_COMMAND_RESPONSE #undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE diff --git a/src/Tgstation.Server.Api/Models/ErrorCode.cs b/src/Tgstation.Server.Api/Models/ErrorCode.cs index ae2dd3341c..69f4d8dbed 100644 --- a/src/Tgstation.Server.Api/Models/ErrorCode.cs +++ b/src/Tgstation.Server.Api/Models/ErrorCode.cs @@ -629,6 +629,10 @@ namespace Tgstation.Server.Api.Models [Description("The deployment took longer than the configured timeout!")] DeploymentTimeout, - // This comment is here to remind you that there is one more unused error code above and you should use it first + /// + /// Sending a broadcast message failed. + /// + [Description("Could not send broadcast to the DMAPI. This can happen either due to there being an insufficient DMAPI version, a communication failure, or the server being offline.")] + BroadcastFailure, } } diff --git a/src/Tgstation.Server.Api/Models/Request/DreamDaemonRequest.cs b/src/Tgstation.Server.Api/Models/Request/DreamDaemonRequest.cs index 0398d69935..0e4c51e993 100644 --- a/src/Tgstation.Server.Api/Models/Request/DreamDaemonRequest.cs +++ b/src/Tgstation.Server.Api/Models/Request/DreamDaemonRequest.cs @@ -7,5 +7,9 @@ namespace Tgstation.Server.Api.Models.Request /// public sealed class DreamDaemonRequest : DreamDaemonApiBase { + /// + /// A to send to the running server's DMAPI for broadcasting. How this is displayed is up to how the DMAPI is integrated in the codebase. Requires interop version >=5.7.0. + /// + public string? BroadcastMessage { get; set; } } } diff --git a/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs b/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs index b13002bf9c..378783bf1e 100644 --- a/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs +++ b/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs @@ -112,5 +112,10 @@ namespace Tgstation.Server.Api.Rights /// User can change . /// SetMapThreads = 1 << 19, + + /// + /// User can use . + /// + BroadcastMessage = 1 << 20, } } diff --git a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicCommandType.cs b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicCommandType.cs index 29be2c9a10..286c605d07 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicCommandType.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicCommandType.cs @@ -62,5 +62,10 @@ namespace Tgstation.Server.Host.Components.Interop.Topic /// Receive additional data for a previous response. /// ReceiveChunk, + + /// + /// Sending a broadcast message. + /// + Broadcast, } } diff --git a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs index 3da7e1c34c..fff5453b0d 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs @@ -42,6 +42,11 @@ namespace Tgstation.Server.Host.Components.Interop.Topic /// public string NewInstanceName { get; } + /// + /// The message to broadcast for requests. + /// + public string BroadcastMessage { get; } + /// /// The for requests. /// @@ -68,6 +73,7 @@ namespace Tgstation.Server.Host.Components.Interop.Topic or TopicCommandType.ChangeRebootState or TopicCommandType.InstanceRenamed or TopicCommandType.ChatChannelsUpdate + or TopicCommandType.Broadcast or TopicCommandType.ServerRestarted => true, TopicCommandType.ChatCommand or TopicCommandType.HealthCheck @@ -76,6 +82,26 @@ namespace Tgstation.Server.Host.Components.Interop.Topic _ => throw new InvalidOperationException($"Invalid value for {nameof(CommandType)}: {CommandType}"), }; + /// + /// Initializes a new instance of the class. + /// + /// The value of . + /// The created . + public static TopicParameters CreateInstanceRenamedTopicParameters(string newInstanceName) + => new ( + newInstanceName ?? throw new ArgumentNullException(nameof(newInstanceName)), + TopicCommandType.InstanceRenamed); + + /// + /// Initializes a new instance of the class. + /// + /// The value of . + /// The created . + public static TopicParameters CreateBroadcastParameters(string broadcastMessage) + => new ( + broadcastMessage ?? throw new ArgumentNullException(nameof(broadcastMessage)), + TopicCommandType.Broadcast); + /// /// Initializes a new instance of the class. /// @@ -116,16 +142,6 @@ namespace Tgstation.Server.Host.Components.Interop.Topic NewRebootState = newRebootState; } - /// - /// Initializes a new instance of the class. - /// - /// The value of . - public TopicParameters(string newInstanceName) - : this(TopicCommandType.InstanceRenamed) - { - NewInstanceName = newInstanceName ?? throw new ArgumentNullException(nameof(newInstanceName)); - } - /// /// Initializes a new instance of the class. /// @@ -175,5 +191,28 @@ namespace Tgstation.Server.Host.Components.Interop.Topic { CommandType = commandType; } + + /// + /// Initializes a new instance of the class. + /// + /// The parameter for the property designated by . + /// The value of . + TopicParameters(string stringCommand, TopicCommandType stringCommandType) + : this(stringCommandType) + { +#pragma warning disable IDE0010 // Add missing cases + switch (stringCommandType) + { + case TopicCommandType.InstanceRenamed: + NewInstanceName = stringCommand; + break; + case TopicCommandType.Broadcast: + BroadcastMessage = stringCommand; + break; + default: + throw new InvalidOperationException($"Invalid string TopicCommandType: {stringCommandType}"); + } +#pragma warning restore IDE0010 // Add missing cases + } } } diff --git a/src/Tgstation.Server.Host/Components/Session/SessionController.cs b/src/Tgstation.Server.Host/Components/Session/SessionController.cs index bf0d22fac7..93e05417a3 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionController.cs @@ -583,7 +583,9 @@ namespace Tgstation.Server.Host.Components.Session public async ValueTask InstanceRenamed(string newInstanceName, CancellationToken cancellationToken) { ReattachInformation.RuntimeInformation.InstanceName = newInstanceName; - await SendCommand(new TopicParameters(newInstanceName), cancellationToken); + await SendCommand( + TopicParameters.CreateInstanceRenamedTopicParameters(newInstanceName), + cancellationToken); } /// diff --git a/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs index 360d3fcfc3..2d8d0f5d24 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs @@ -88,5 +88,13 @@ namespace Tgstation.Server.Host.Components.Watchdog /// The for the operation. /// A representing the running operation. ValueTask CreateDump(CancellationToken cancellationToken); + + /// + /// Send a broadcast to the DMAPI. + /// + /// The message to broadcast. + /// The for the operation. + /// A resulting in if the broadcast succeeded., otherwise. + ValueTask Broadcast(string message, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs index 025f3f27a8..a77496bae2 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs @@ -457,6 +457,43 @@ namespace Tgstation.Server.Host.Components.Watchdog await session.CreateDump(dumpFileName, cancellationToken); } + /// + public async ValueTask Broadcast(string message, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(message); + + var activeServer = GetActiveController(); + if (activeServer == null) + { + Logger.LogInformation("Attempted broadcast failed, no active server!"); + return false; + } + + if (!activeServer.DMApiAvailable) + { + Logger.LogInformation("Attempted broadcast failed, no DMAPI!"); + return false; + } + + var minimumRequiredVersion = new Version(5, 7, 0); + if (activeServer.DMApiVersion < minimumRequiredVersion) + { + Logger.LogInformation( + "Attempted broadcast failed, insufficient interop version: {interopVersion}. Requires {minimumRequiredVersion}!", + activeServer.DMApiVersion, + minimumRequiredVersion); + return false; + } + + Logger.LogInformation("Broadcasting: {message}", message); + + var response = await activeServer.SendCommand( + TopicParameters.CreateBroadcastParameters(message), + cancellationToken); + + return response != null && response.ErrorMessage == null; + } + /// async ValueTask IEventConsumer.HandleEvent(EventType eventType, IEnumerable parameters, bool deploymentPipeline, CancellationToken cancellationToken) { diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs index 097e0377d3..8e57d022c5 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs @@ -148,7 +148,8 @@ namespace Tgstation.Server.Host.Controllers | DreamDaemonRights.SetVisibility | DreamDaemonRights.SetProfiler | DreamDaemonRights.SetLogOutput - | DreamDaemonRights.SetMapThreads)] + | DreamDaemonRights.SetMapThreads + | DreamDaemonRights.BroadcastMessage)] [ProducesResponseType(typeof(DreamDaemonResponse), 200)] [ProducesResponseType(typeof(ErrorMessageResponse), 410)] #pragma warning disable CA1502 // TODO: Decomplexify @@ -208,36 +209,41 @@ namespace Tgstation.Server.Host.Controllers return false; } + if (CheckModified(x => x.AllowWebClient, DreamDaemonRights.SetWebClient) + || CheckModified(x => x.AutoStart, DreamDaemonRights.SetAutoStart) + || CheckModified(x => x.Port, DreamDaemonRights.SetPort) + || CheckModified(x => x.SecurityLevel, DreamDaemonRights.SetSecurity) + || CheckModified(x => x.Visibility, DreamDaemonRights.SetVisibility) + || (model.SoftRestart.HasValue && !AuthenticationContext.InstancePermissionSet.DreamDaemonRights.Value.HasFlag(DreamDaemonRights.SoftRestart)) + || (model.SoftShutdown.HasValue && !AuthenticationContext.InstancePermissionSet.DreamDaemonRights.Value.HasFlag(DreamDaemonRights.SoftShutdown)) + || (!String.IsNullOrWhiteSpace(model.BroadcastMessage) && !AuthenticationContext.InstancePermissionSet.DreamDaemonRights.Value.HasFlag(DreamDaemonRights.BroadcastMessage)) + || CheckModified(x => x.StartupTimeout, DreamDaemonRights.SetStartupTimeout) + || CheckModified(x => x.HealthCheckSeconds, DreamDaemonRights.SetHealthCheckInterval) + || CheckModified(x => x.DumpOnHealthCheckRestart, DreamDaemonRights.CreateDump) + || CheckModified(x => x.TopicRequestTimeout, DreamDaemonRights.SetTopicTimeout) + || CheckModified(x => x.AdditionalParameters, DreamDaemonRights.SetAdditionalParameters) + || CheckModified(x => x.StartProfiler, DreamDaemonRights.SetProfiler) + || CheckModified(x => x.LogOutput, DreamDaemonRights.SetLogOutput) + || CheckModified(x => x.MapThreads, DreamDaemonRights.SetMapThreads)) + return Forbid(); + return await WithComponentInstance( async instance => { var watchdog = instance.Watchdog; - var rebootState = watchdog.RebootState; - var oldSoftRestart = rebootState == RebootState.Restart; - var oldSoftShutdown = rebootState == RebootState.Shutdown; - - if (CheckModified(x => x.AllowWebClient, DreamDaemonRights.SetWebClient) - || CheckModified(x => x.AutoStart, DreamDaemonRights.SetAutoStart) - || CheckModified(x => x.Port, DreamDaemonRights.SetPort) - || CheckModified(x => x.SecurityLevel, DreamDaemonRights.SetSecurity) - || CheckModified(x => x.Visibility, DreamDaemonRights.SetVisibility) - || (model.SoftRestart.HasValue && !AuthenticationContext.InstancePermissionSet.DreamDaemonRights.Value.HasFlag(DreamDaemonRights.SoftRestart)) - || (model.SoftShutdown.HasValue && !AuthenticationContext.InstancePermissionSet.DreamDaemonRights.Value.HasFlag(DreamDaemonRights.SoftShutdown)) - || CheckModified(x => x.StartupTimeout, DreamDaemonRights.SetStartupTimeout) - || CheckModified(x => x.HealthCheckSeconds, DreamDaemonRights.SetHealthCheckInterval) - || CheckModified(x => x.DumpOnHealthCheckRestart, DreamDaemonRights.CreateDump) - || CheckModified(x => x.TopicRequestTimeout, DreamDaemonRights.SetTopicTimeout) - || CheckModified(x => x.AdditionalParameters, DreamDaemonRights.SetAdditionalParameters) - || CheckModified(x => x.StartProfiler, DreamDaemonRights.SetProfiler) - || CheckModified(x => x.LogOutput, DreamDaemonRights.SetLogOutput) - || CheckModified(x => x.MapThreads, DreamDaemonRights.SetMapThreads)) - return Forbid(); + if (!String.IsNullOrWhiteSpace(model.BroadcastMessage) + && !await watchdog.Broadcast(model.BroadcastMessage, cancellationToken)) + return Conflict(new ErrorMessageResponse(ErrorCode.BroadcastFailure)); await DatabaseContext.Save(cancellationToken); // run this second because current may be modified by it + // slight race condition with request cancellation, but I CANNOT be assed right now await watchdog.ChangeSettings(current, cancellationToken); + var rebootState = watchdog.RebootState; + var oldSoftRestart = rebootState == RebootState.Restart; + var oldSoftShutdown = rebootState == RebootState.Shutdown; if (!oldSoftRestart && model.SoftRestart == true && watchdog.Status == WatchdogStatus.Online) await watchdog.Restart(true, cancellationToken); else if (!oldSoftShutdown && model.SoftShutdown == true) diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index a1cdabec96..41effa0094 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -558,7 +558,7 @@ namespace Tgstation.Server.Host.Core logger.LogTrace("Configuration version: {configVersion}", GeneralConfiguration.CurrentConfigVersion); logger.LogTrace("DMAPI Interop version: {interopVersion}", DMApiConstants.InteropVersion); if (controlPanelConfiguration.Enable) - logger.LogTrace("Web control panel version: {webCPVersion}", MasterVersionsAttribute.Instance.RawControlPanelVersion); + logger.LogTrace("Webpanel version: {webCPVersion}", MasterVersionsAttribute.Instance.RawWebpanelVersion); logger.LogDebug("Starting hosting on port {httpApiPort}...", serverPortProvider.HttpApiPort); } diff --git a/src/Tgstation.Server.Host/Properties/MasterVersionsAttribute.cs b/src/Tgstation.Server.Host/Properties/MasterVersionsAttribute.cs index c9bfed3eca..d6461713bb 100644 --- a/src/Tgstation.Server.Host/Properties/MasterVersionsAttribute.cs +++ b/src/Tgstation.Server.Host/Properties/MasterVersionsAttribute.cs @@ -29,7 +29,7 @@ namespace Tgstation.Server.Host.Properties /// /// The of the control panel version built. /// - public string RawControlPanelVersion { get; } + public string RawWebpanelVersion { get; } /// /// The of the control panel version built. @@ -46,19 +46,19 @@ namespace Tgstation.Server.Host.Properties /// /// The value of . /// The value of . - /// The value of . + /// The value of . /// The value of . /// The value of . public MasterVersionsAttribute( string rawConfigurationVersion, string rawInteropVersion, - string rawControlPanelVersion, + string rawWebpanelVersion, string rawHostWatchdogVersion, string rawMariaDBRedistVersion) { RawConfigurationVersion = rawConfigurationVersion ?? throw new ArgumentNullException(nameof(rawConfigurationVersion)); RawInteropVersion = rawInteropVersion ?? throw new ArgumentNullException(nameof(rawInteropVersion)); - RawControlPanelVersion = rawControlPanelVersion ?? throw new ArgumentNullException(nameof(rawControlPanelVersion)); + RawWebpanelVersion = rawWebpanelVersion ?? throw new ArgumentNullException(nameof(rawWebpanelVersion)); RawHostWatchdogVersion = rawHostWatchdogVersion ?? throw new ArgumentNullException(nameof(rawHostWatchdogVersion)); RawMariaDBRedistVersion = rawMariaDBRedistVersion ?? throw new ArgumentNullException(nameof(rawMariaDBRedistVersion)); } diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 40e00b73a7..bcca6637c3 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -22,17 +22,17 @@ $(DefineConstants);NO_WEBPANEL - + - + - + @@ -47,7 +47,7 @@ <_Parameter1>$(TgsConfigVersion) <_Parameter2>$(TgsInteropVersion) - <_Parameter3>$(TgsControlPanelVersion) + <_Parameter3>$(TgsWebpanelVersion) <_Parameter4>$(TgsHostWatchdogVersion) <_Parameter5>$(TgsMariaDBRedistVersion) @@ -107,7 +107,7 @@ - + diff --git a/tests/DMAPI/LongRunning/Test.dm b/tests/DMAPI/LongRunning/Test.dm index c2e274f4d4..57b23805bc 100644 --- a/tests/DMAPI/LongRunning/Test.dm +++ b/tests/DMAPI/LongRunning/Test.dm @@ -174,6 +174,10 @@ var/run_bridge_test if(tactics8) return received_health_check ? "received health check" : "did not receive health check" + var/tactics_broadcast = data["tgs_integration_test_tactics_broadcast"] + if(tactics_broadcast) + return last_tgs_broadcast || "!!NULL!!" + var/legalize_nuclear_bombs = data["shadow_wizard_money_gang"] if(legalize_nuclear_bombs) text2file("I expect this to remain here for a while", "kajigger.txt") diff --git a/tests/DMAPI/test_prelude.dm b/tests/DMAPI/test_prelude.dm index efb84d46ae..6cb666e372 100644 --- a/tests/DMAPI/test_prelude.dm +++ b/tests/DMAPI/test_prelude.dm @@ -3,7 +3,8 @@ #define TGS_READ_GLOBAL(Name) global.##Name #define TGS_WRITE_GLOBAL(Name, Value) global.##Name = ##Value #define TGS_PROTECT_DATUM(Path) -#define TGS_WORLD_ANNOUNCE(message) world << ##message +var/last_tgs_broadcast +#define TGS_WORLD_ANNOUNCE(message) if(TRUE) { var/__tgs_announce_message_local = ##message; world << __tgs_announce_message_local; last_tgs_broadcast = __tgs_announce_message_local; } #define TGS_WARNING_LOG(message) world.log << "Warn: [##message]" #define TGS_NOTIFY_ADMINS(event) #define TGS_CLIENT_COUNT 0 diff --git a/tests/Tgstation.Server.Host.Tests.Signals/Tgstation.Server.Host.Tests.Signals.csproj b/tests/Tgstation.Server.Host.Tests.Signals/Tgstation.Server.Host.Tests.Signals.csproj index d515505048..7755d96945 100644 --- a/tests/Tgstation.Server.Host.Tests.Signals/Tgstation.Server.Host.Tests.Signals.csproj +++ b/tests/Tgstation.Server.Host.Tests.Signals/Tgstation.Server.Host.Tests.Signals.csproj @@ -1,16 +1,11 @@ - + $(TgsFrameworkVersion) Exe - - - - - diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index 0a67dc8c02..b352bd28d6 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -211,12 +211,46 @@ namespace Tgstation.Server.Tests.Live.Instance await RunTest(false); } + async ValueTask BroadcastTest(CancellationToken cancellationToken) + { + var topicRequestResult = await topicClient.SendTopic( + IPAddress.Loopback, + $"tgs_integration_test_tactics_broadcast=1", + ddPort, + cancellationToken); + + Assert.IsNotNull(topicRequestResult); + Assert.AreEqual("!!NULL!!", topicRequestResult.StringData); + + const string TestBroadcastMessage = "TGS: THIS IS A TEST OF THE EMERGENCY BROADCAST SYSTEM!"; + await instanceClient.DreamDaemon.Update(new DreamDaemonRequest + { + BroadcastMessage = TestBroadcastMessage, + }, cancellationToken); + + topicRequestResult = await topicClient.SendTopic( + IPAddress.Loopback, + $"tgs_integration_test_tactics_broadcast=1", + ddPort, + cancellationToken); + + Assert.IsNotNull(topicRequestResult); + Assert.AreEqual(TestBroadcastMessage, topicRequestResult.StringData); + } + async Task InteropTestsForLongRunningDme(CancellationToken cancellationToken) { await RegressionTest1686(cancellationToken); + await ApiAssert.ThrowsException(() => instanceClient.DreamDaemon.Update(new DreamDaemonRequest + { + BroadcastMessage = "ksjfdksjf", + }, cancellationToken), ErrorCode.BroadcastFailure); + await StartAndLeaveRunning(cancellationToken); + await BroadcastTest(cancellationToken); + await RegressionTest1550(cancellationToken); var deleteJobTask = TestDeleteByondInstallErrorCasesAndQueing(cancellationToken); diff --git a/tests/Tgstation.Server.Tests/TestVersions.cs b/tests/Tgstation.Server.Tests/TestVersions.cs index 41c48a1196..a87b47b2b1 100644 --- a/tests/Tgstation.Server.Tests/TestVersions.cs +++ b/tests/Tgstation.Server.Tests/TestVersions.cs @@ -284,11 +284,11 @@ namespace Tgstation.Server.Tests [TestMethod] public void TestControlPanelVersion() { - var doc = XDocument.Load("../../../../../build/ControlPanelVersion.props"); + var doc = XDocument.Load("../../../../../build/WebpanelVersion.props"); var project = doc.Root; var controlPanelXmlNamespace = project.GetDefaultNamespace(); var controlPanelVersionsPropertyGroup = project.Elements().First(x => x.Name == controlPanelXmlNamespace + "PropertyGroup"); - var versionString = controlPanelVersionsPropertyGroup.Element(controlPanelXmlNamespace + "TgsControlPanelVersion").Value; + var versionString = controlPanelVersionsPropertyGroup.Element(controlPanelXmlNamespace + "TgsWebpanelVersion").Value; Assert.IsNotNull(versionString); Assert.IsTrue(Version.TryParse(versionString, out var expected)); diff --git a/tgstation-server.sln b/tgstation-server.sln index 36d9027e9d..ce57c2ea30 100644 --- a/tgstation-server.sln +++ b/tgstation-server.sln @@ -26,7 +26,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{6FF654E6 build\BuildDox.ps1 = build\BuildDox.ps1 build\ci.runsettings = build\ci.runsettings build\Common.props = build\Common.props - build\ControlPanelVersion.props = build\ControlPanelVersion.props build\Dockerfile = build\Dockerfile build\GenerateMigrations.sh = build\GenerateMigrations.sh build\logo.svg = build\logo.svg @@ -39,6 +38,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{6FF654E6 build\tgstation-server.service = build\tgstation-server.service build\uac_elevation_manifest.xml = build\uac_elevation_manifest.xml build\Version.props = build\Version.props + build\WebpanelVersion.props = build\WebpanelVersion.props EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Api", "src\Tgstation.Server.Api\Tgstation.Server.Api.csproj", "{8B4A208D-A48A-4A5D-8B94-E2661138865D}" diff --git a/tools/Tgstation.Server.ReleaseNotes/Program.cs b/tools/Tgstation.Server.ReleaseNotes/Program.cs index 21bdbc1bfa..79b594e505 100644 --- a/tools/Tgstation.Server.ReleaseNotes/Program.cs +++ b/tools/Tgstation.Server.ReleaseNotes/Program.cs @@ -223,7 +223,7 @@ namespace Tgstation.Server.ReleaseNotes string prefix; const string PropsPath = "build/Version.props"; - const string ControlPanelPropsPath = "build/ControlPanelVersion.props"; + const string ControlPanelPropsPath = "build/WebpanelVersion.props"; var doc = XDocument.Load(PropsPath); var project = doc.Root; @@ -246,7 +246,7 @@ namespace Tgstation.Server.ReleaseNotes var configVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsConfigVersion").Value); var dmApiVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsDmapiVersion").Value); var interopVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsInteropVersion").Value); - var webControlVersion = Version.Parse(controlPanelVersionsPropertyGroup.Element(controlPanelXmlNamespace + "TgsControlPanelVersion").Value); + var webControlVersion = Version.Parse(controlPanelVersionsPropertyGroup.Element(controlPanelXmlNamespace + "TgsWebpanelVersion").Value); var hostWatchdogVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsHostWatchdogVersion").Value); if (webControlVersion.Major == 0) @@ -608,14 +608,14 @@ namespace Tgstation.Server.ReleaseNotes dict.Add(Component.NugetApi, Parse("TgsApiLibraryVersion")); dict.Add(Component.NugetClient, Parse("TgsClientVersion")); - var webVersion = Parse("TgsControlPanelVersion"); + var webVersion = Parse("TgsWebpanelVersion"); if (webVersion != null) { dict.Add(Component.WebControlPanel, webVersion); } else { - var controlPanelVersionBytes = await RLR(() => client.Repository.Content.GetRawContentByRef(RepoOwner, RepoName, "build/ControlPanelVersion.props", mergeCommit)); + var controlPanelVersionBytes = await RLR(() => client.Repository.Content.GetRawContentByRef(RepoOwner, RepoName, "build/WebpanelVersion.props", mergeCommit)); using (var ms = new MemoryStream(controlPanelVersionBytes)) doc = XDocument.Load(ms); @@ -623,7 +623,7 @@ namespace Tgstation.Server.ReleaseNotes project = doc.Root; var controlPanelXmlNamespace = project.GetDefaultNamespace(); var controlPanelVersionsPropertyGroup = project.Elements().First(x => x.Name == controlPanelXmlNamespace + "PropertyGroup"); - dict.Add(Component.WebControlPanel, Version.Parse(controlPanelVersionsPropertyGroup.Element(controlPanelXmlNamespace + "TgsControlPanelVersion").Value)); + dict.Add(Component.WebControlPanel, Version.Parse(controlPanelVersionsPropertyGroup.Element(controlPanelXmlNamespace + "TgsWebpanelVersion").Value)); } } diff --git a/tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj b/tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj index 35312949ce..4c87963970 100644 --- a/tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj +++ b/tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj @@ -8,7 +8,7 @@ - +