From d4b3583ff3b606b035408f3b41aedd0991e562a5 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 14 Oct 2023 00:21:45 -0400 Subject: [PATCH] More OpenDream Integration - Make use of `world.opendream_topic_port` in DMAPI if present. Send in initial bridge request. - Implement compiler and server arguments for `OpenDreamInstallation`. - Deprecate bridge port update requests. - Prevent symlink swapping if not running a BYOND engine. --- build/Version.props | 2 +- src/DMAPI/tgs/v5/__interop_version.dm | 2 +- src/DMAPI/tgs/v5/_defines.dm | 2 +- src/DMAPI/tgs/v5/api.dm | 9 +- src/DMAPI/tgs/v5/topic.dm | 1 + src/DMAPI/tgs/v5/undefs.dm | 2 +- .../Internal/DreamDaemonLaunchParameters.cs | 10 +-- .../Components/Deployment/DmbProvider.cs | 15 ++-- .../Components/Deployment/DmbProviderBase.cs | 37 ++++++++ .../Components/Deployment/DreamMaker.cs | 6 -- .../Deployment/TemporaryDmbProvider.cs | 19 ++-- .../Engine/OpenDreamInstallation.cs | 41 +++++++-- .../Components/Engine/OpenDreamInstaller.cs | 72 ++++++++++++--- .../Interop/Bridge/BridgeCommandType.cs | 2 +- .../Interop/Bridge/BridgeParameters.cs | 7 +- .../Components/Session/ISessionController.cs | 17 ++-- .../Components/Session/SessionController.cs | 90 +++---------------- .../Components/Watchdog/BasicWatchdog.cs | 5 ++ .../Components/Watchdog/WindowsWatchdog.cs | 11 +++ 19 files changed, 205 insertions(+), 145 deletions(-) create mode 100644 src/Tgstation.Server.Host/Components/Deployment/DmbProviderBase.cs diff --git a/build/Version.props b/build/Version.props index 3d8d79e696..05c5365352 100644 --- a/build/Version.props +++ b/build/Version.props @@ -10,7 +10,7 @@ 12.0.0 13.0.0 6.6.0 - 5.6.1 + 5.7.0 1.4.0 1.2.1 2.0.0 diff --git a/src/DMAPI/tgs/v5/__interop_version.dm b/src/DMAPI/tgs/v5/__interop_version.dm index 5d3d491a73..83420d130a 100644 --- a/src/DMAPI/tgs/v5/__interop_version.dm +++ b/src/DMAPI/tgs/v5/__interop_version.dm @@ -1 +1 @@ -"5.6.1" +"5.7.0" diff --git a/src/DMAPI/tgs/v5/_defines.dm b/src/DMAPI/tgs/v5/_defines.dm index f973338daa..b4557c562c 100644 --- a/src/DMAPI/tgs/v5/_defines.dm +++ b/src/DMAPI/tgs/v5/_defines.dm @@ -8,7 +8,6 @@ #define DMAPI5_TOPIC_REQUEST_LIMIT 65528 #define DMAPI5_TOPIC_RESPONSE_LIMIT 65529 -#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 @@ -18,6 +17,7 @@ #define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier" #define DMAPI5_PARAMETER_CUSTOM_COMMANDS "customCommands" +#define DMAPI5_PARAMETER_TOPIC_PORT "topicPort" #define DMAPI5_CHUNK "chunk" #define DMAPI5_CHUNK_PAYLOAD "payload" diff --git a/src/DMAPI/tgs/v5/api.dm b/src/DMAPI/tgs/v5/api.dm index 34cc43f876..cb8a2f11f7 100644 --- a/src/DMAPI/tgs/v5/api.dm +++ b/src/DMAPI/tgs/v5/api.dm @@ -38,7 +38,7 @@ var/datum/tgs_version/api_version = ApiVersion() version = null - var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands())) + var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands(), DMAPI5_PARAMETER_TOPIC_PORT = GetTopicPort())) if(!istype(bridge_response)) TGS_ERROR_LOG("Failed initial bridge request!") return FALSE @@ -100,6 +100,13 @@ initialized = TRUE return TRUE +/datum/tgs_api/v5/proc/GetTopicPort() +#if defined(OPENDREAM) && defined(OPENDREAM_TOPIC_PORT_EXISTS) + return "[world.opendream_topic_port]" +#else + return null +#endif + /datum/tgs_api/v5/proc/RequireInitialBridgeResponse() TGS_DEBUG_LOG("RequireInitialBridgeResponse()") var/logged = FALSE diff --git a/src/DMAPI/tgs/v5/topic.dm b/src/DMAPI/tgs/v5/topic.dm index d7d4712138..40ab80e465 100644 --- a/src/DMAPI/tgs/v5/topic.dm +++ b/src/DMAPI/tgs/v5/topic.dm @@ -175,6 +175,7 @@ var/list/reattach_response = TopicResponse(error_message) reattach_response[DMAPI5_PARAMETER_CUSTOM_COMMANDS] = ListCustomCommands() + reattach_response[DMAPI5_PARAMETER_TOPIC_PORT] = GetTopicPort() return reattach_response if(DMAPI5_TOPIC_COMMAND_SEND_CHUNK) diff --git a/src/DMAPI/tgs/v5/undefs.dm b/src/DMAPI/tgs/v5/undefs.dm index c679737dfc..f579cbefb7 100644 --- a/src/DMAPI/tgs/v5/undefs.dm +++ b/src/DMAPI/tgs/v5/undefs.dm @@ -8,7 +8,6 @@ #undef DMAPI5_TOPIC_REQUEST_LIMIT #undef DMAPI5_TOPIC_RESPONSE_LIMIT -#undef DMAPI5_BRIDGE_COMMAND_PORT_UPDATE #undef DMAPI5_BRIDGE_COMMAND_STARTUP #undef DMAPI5_BRIDGE_COMMAND_PRIME #undef DMAPI5_BRIDGE_COMMAND_REBOOT @@ -18,6 +17,7 @@ #undef DMAPI5_PARAMETER_ACCESS_IDENTIFIER #undef DMAPI5_PARAMETER_CUSTOM_COMMANDS +#undef DMAPI5_PARAMETER_TOPIC_PORT #undef DMAPI5_CHUNK #undef DMAPI5_CHUNK_PAYLOAD diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs index b563d95585..b21ce258ff 100644 --- a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs +++ b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs @@ -9,21 +9,21 @@ namespace Tgstation.Server.Api.Models.Internal public class DreamDaemonLaunchParameters { /// - /// If the BYOND web client can be used to connect to the game server. + /// If the BYOND web client can be used to connect to the game server. No-op for . /// [Required] [ResponseOptions] public bool? AllowWebClient { get; set; } /// - /// If -profile is passed in on the DreamDaemon command line. + /// If -profile is passed in on the DreamDaemon command line. No-op for . /// [Required] [ResponseOptions] public bool? StartProfiler { get; set; } /// - /// The level of DreamDaemon. + /// The level of DreamDaemon. No-op for . /// [Required] [ResponseOptions] @@ -31,7 +31,7 @@ namespace Tgstation.Server.Api.Models.Internal public DreamDaemonVisibility? Visibility { get; set; } /// - /// The level of DreamDaemon. + /// The level of DreamDaemon. No-op for . /// [Required] [ResponseOptions] @@ -92,7 +92,7 @@ namespace Tgstation.Server.Api.Models.Internal public bool? LogOutput { get; set; } /// - /// If DreamDaemon supports it, the value added as the -map-threads parameter. 0 uses the default BYOND value. + /// If DreamDaemon supports it, the value added as the -map-threads parameter. 0 uses the default BYOND value. No-op for . /// [Required] [ResponseOptions] diff --git a/src/Tgstation.Server.Host/Components/Deployment/DmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/DmbProvider.cs index 554cc28cdd..2b58ba17c0 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DmbProvider.cs @@ -6,19 +6,16 @@ using Tgstation.Server.Host.IO; namespace Tgstation.Server.Host.Components.Deployment { /// - sealed class DmbProvider : IDmbProvider + sealed class DmbProvider : DmbProviderBase, IDmbProvider { /// - public string DmbName => String.Concat(CompileJob.DmeName, DreamMaker.DmbExtension); + public override string Directory => ioManager.ResolvePath(CompileJob.DirectoryName.ToString() + directoryAppend); /// - public string Directory => ioManager.ResolvePath(CompileJob.DirectoryName.ToString() + directoryAppend); + public override Models.CompileJob CompileJob { get; } /// - public Models.CompileJob CompileJob { get; } - - /// - public ByondVersion ByondVersion { get; } + public override ByondVersion ByondVersion { get; } /// /// The for the . @@ -53,9 +50,9 @@ namespace Tgstation.Server.Host.Components.Deployment } /// - public void Dispose() => onDispose?.Invoke(); + public override void Dispose() => onDispose?.Invoke(); /// - public void KeepAlive() => onDispose = null; + public override void KeepAlive() => onDispose = null; } } diff --git a/src/Tgstation.Server.Host/Components/Deployment/DmbProviderBase.cs b/src/Tgstation.Server.Host/Components/Deployment/DmbProviderBase.cs new file mode 100644 index 0000000000..ea792bd23d --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Deployment/DmbProviderBase.cs @@ -0,0 +1,37 @@ +using System; + +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Components.Deployment +{ + /// + abstract class DmbProviderBase : IDmbProvider + { + /// + public string DmbName => String.Concat( + CompileJob.DmeName, + ByondVersion.Engine.Value switch + { + EngineType.Byond => ".dmb", + EngineType.OpenDream => ".json", + _ => throw new InvalidOperationException($"Invalid EngineType: {ByondVersion.Engine.Value}"), + }); + + /// + public abstract string Directory { get; } + + /// + public abstract Models.CompileJob CompileJob { get; } + + /// + public abstract ByondVersion ByondVersion { get; } + + /// + public abstract void Dispose(); + + /// + public abstract void KeepAlive(); + } +} diff --git a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs index 8dd925f812..cc7c03b001 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs @@ -30,11 +30,6 @@ namespace Tgstation.Server.Host.Components.Deployment /// sealed class DreamMaker : IDreamMaker { - /// - /// Extension for .dmbs. - /// - public const string DmbExtension = ".dmb"; - /// /// Extension for .dmes. /// @@ -805,7 +800,6 @@ namespace Tgstation.Server.Host.Components.Deployment ApiValidationStatus validationStatus; using (var provider = new TemporaryDmbProvider( ioManager.ResolvePath(job.DirectoryName.ToString()), - String.Concat(job.DmeName, DmbExtension), job, byondLock.Version)) await using (var controller = await sessionControllerFactory.LaunchNew(provider, byondLock, launchParameters, true, cancellationToken)) diff --git a/src/Tgstation.Server.Host/Components/Deployment/TemporaryDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/TemporaryDmbProvider.cs index 5ecc6b9670..a94efe35ed 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/TemporaryDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/TemporaryDmbProvider.cs @@ -7,41 +7,36 @@ namespace Tgstation.Server.Host.Components.Deployment /// /// Temporary . /// - sealed class TemporaryDmbProvider : IDmbProvider + sealed class TemporaryDmbProvider : DmbProviderBase { /// - public string DmbName { get; } + public override string Directory { get; } /// - public string Directory { get; } + public override Models.CompileJob CompileJob { get; } /// - public Models.CompileJob CompileJob { get; } - - /// - public ByondVersion ByondVersion { get; } + public override ByondVersion ByondVersion { get; } /// /// Initializes a new instance of the class. /// /// The value of . - /// The value of . /// The value of . /// The value of . - public TemporaryDmbProvider(string directory, string dmb, Models.CompileJob compileJob, ByondVersion byondVersion) + public TemporaryDmbProvider(string directory, Models.CompileJob compileJob, ByondVersion byondVersion) { - DmbName = dmb ?? throw new ArgumentNullException(nameof(dmb)); Directory = directory ?? throw new ArgumentNullException(nameof(directory)); CompileJob = compileJob ?? throw new ArgumentNullException(nameof(compileJob)); ByondVersion = byondVersion ?? throw new ArgumentNullException(nameof(byondVersion)); } /// - public void Dispose() + public override void Dispose() { } /// - public void KeepAlive() => throw new NotSupportedException(); + public override void KeepAlive() => throw new NotSupportedException(); } } diff --git a/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstallation.cs b/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstallation.cs index 5a3d04d6d2..6d1fc3377a 100644 --- a/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstallation.cs +++ b/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstallation.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using System.Web; using Tgstation.Server.Api.Models; using Tgstation.Server.Api.Models.Internal; @@ -34,21 +36,23 @@ namespace Tgstation.Server.Host.Components.Engine /// /// Initializes a new instance of the class. /// + /// The value of . + /// The value of . /// The value of . /// The value of . public OpenDreamInstallation( + string serverExePath, + string compilerExePath, Task installationTask, ByondVersion version) { + ServerExePath = serverExePath ?? throw new ArgumentNullException(nameof(serverExePath)); + CompilerExePath = compilerExePath ?? throw new ArgumentNullException(nameof(compilerExePath)); InstallationTask = installationTask ?? throw new ArgumentNullException(nameof(installationTask)); ArgumentNullException.ThrowIfNull(version); if (version.Engine.Value != EngineType.OpenDream) throw new ArgumentException($"Invalid EngineType: {version.Engine.Value}", nameof(version)); - - Version = version ?? throw new ArgumentNullException(nameof(version)); - - throw new NotImplementedException(); } /// @@ -58,14 +62,33 @@ namespace Tgstation.Server.Host.Components.Engine ArgumentNullException.ThrowIfNull(parameters); ArgumentNullException.ThrowIfNull(launchParameters); - throw new NotImplementedException(); + var parametersString = String.Join(';', parameters.Select(kvp => $"{kvp.Key}={kvp.Value}")); + + if (!String.IsNullOrEmpty(launchParameters.AdditionalParameters)) + { + // TGS and BYOND expect url encoded params, OD takes unencoded + var unencodedAdditionalParams = String.Join( + ';', + launchParameters + .AdditionalParameters + .Split('&') + .Select( + singleParam => String.Join( + '=', + singleParam + .Split('=') + .Select( + encodedParam => HttpUtility.UrlDecode(encodedParam))))); + + parametersString = $"{parametersString};{unencodedAdditionalParams}"; + } + + var arguments = $"--cvar net.port={launchParameters.Port.Value} --cvar opendream.topic_port=0 --cvar opendream.world_params=\"{parametersString}\" \"{dmbProvider.DmbName}\""; + return arguments; } /// public string FormatCompilerArguments(string dmePath) - { - ArgumentNullException.ThrowIfNull(dmePath); - throw new NotImplementedException(); - } + => $"--suppress-unimplemented --verbose --notices-enabled {dmePath ?? throw new ArgumentNullException(nameof(dmePath))}"; } } diff --git a/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstaller.cs b/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstaller.cs index 9eb60f0dfe..3021238614 100644 --- a/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstaller.cs +++ b/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstaller.cs @@ -23,10 +23,15 @@ namespace Tgstation.Server.Host.Components.Engine /// sealed class OpenDreamInstaller : EngineInstallerBase { + /// + /// The name of the subdirectory used to store the server and compiler binaries. + /// + const string InstallationBinDirectory = "bin"; + /// /// The name of the subdirectory used for the 's copy. /// - private const string InstallationRepositorySubDirectory = "SourceRepo"; + const string InstallationSourceSubDirectory = "TgsSourceSubdir"; /// protected override EngineType TargetEngineType => EngineType.OpenDream; @@ -82,7 +87,21 @@ namespace Tgstation.Server.Host.Components.Engine public override IEngineInstallation CreateInstallation(ByondVersion version, Task installationTask) { CheckVersionValidity(version); - return new OpenDreamInstallation(installationTask, version); + var binPathForVersion = IOManager.ConcatPath(version.ToString(), InstallationBinDirectory); + + var exeExtension = platformIdentifier.IsWindows + ? ".exe" + : String.Empty; + + return new OpenDreamInstallation( + IOManager.ConcatPath( + binPathForVersion, + $"OpenDreamServer{exeExtension}"), + IOManager.ConcatPath( + binPathForVersion, + $"DMCompiler{exeExtension}"), + installationTask, + version); } /// @@ -135,7 +154,7 @@ namespace Tgstation.Server.Host.Components.Engine version.SourceCommittish = repo.Head; - return new RepositoryEngineInstallationData(IOManager, repo, InstallationRepositorySubDirectory); + return new RepositoryEngineInstallationData(IOManager, repo, InstallationSourceSubDirectory); } catch { @@ -149,6 +168,40 @@ namespace Tgstation.Server.Host.Components.Engine { CheckVersionValidity(version); ArgumentNullException.ThrowIfNull(installPath); + var sourcePath = IOManager.ConcatPath(installPath, InstallationSourceSubDirectory); + + if (!await IOManager.DirectoryExists(sourcePath, cancellationToken)) + { + // a zip install that didn't come from us? + // we want to use the bin dir, so put everything where we expect + Logger.LogDebug("Correcting extraction location..."); + var dirsTask = IOManager.GetDirectories(installPath, cancellationToken); + var filesTask = IOManager.GetFiles(installPath, cancellationToken); + var dirCreateTask = IOManager.CreateDirectory(sourcePath, cancellationToken); + + await Task.WhenAll(dirsTask, filesTask, dirCreateTask); + + var dirsMoveTasks = dirsTask + .Result + .Select( + dirPath => IOManager.MoveDirectory( + dirPath, + IOManager.ConcatPath( + sourcePath, + IOManager.GetFileName(sourcePath)), + cancellationToken)); + var filesMoveTask = filesTask + .Result + .Select( + filePath => IOManager.MoveFile( + filePath, + IOManager.ConcatPath( + sourcePath, + IOManager.GetFileName(sourcePath)), + cancellationToken)); + + await Task.WhenAll(dirsMoveTasks.Concat(filesMoveTask)); + } var dotnetPaths = DotnetHelper.GetPotentialDotnetPaths(platformIdentifier.IsWindows) .ToList(); @@ -165,11 +218,9 @@ namespace Tgstation.Server.Host.Components.Engine var dotnetPath = dotnetPaths[selectedPathIndex]; - var repositoryPath = IOManager.ConcatPath(installPath, InstallationRepositorySubDirectory); - await using (var buildProcess = processExecutor.LaunchProcess( dotnetPath, - repositoryPath, + sourcePath, "build -c Release", null, true, @@ -180,18 +231,17 @@ namespace Tgstation.Server.Host.Components.Engine throw new JobException("OpenDream build failed!"); } - const string BinDirectory = "bin"; await IOManager.MoveDirectory( IOManager.ConcatPath( - repositoryPath, - BinDirectory, + sourcePath, + InstallationBinDirectory, "Content.Server"), IOManager.ConcatPath( installPath, - BinDirectory), + InstallationBinDirectory), cancellationToken); - await IOManager.DeleteDirectory(repositoryPath, cancellationToken); + await IOManager.DeleteDirectory(sourcePath, cancellationToken); } /// diff --git a/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeCommandType.cs b/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeCommandType.cs index ad3c227356..627d55d994 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeCommandType.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeCommandType.cs @@ -8,7 +8,7 @@ /// /// DreamDaemon notifying us of its current port and requesting a change if necessary. /// - PortUpdate, + DeprecatedPortUpdate, /// /// DreamDaemon notifying it is starting. diff --git a/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeParameters.cs b/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeParameters.cs index 6bd13cb72d..c1c9213063 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeParameters.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeParameters.cs @@ -17,7 +17,7 @@ namespace Tgstation.Server.Host.Components.Interop.Bridge public BridgeCommandType? CommandType { get; set; } /// - /// The current port for requests. + /// The current port for requests. /// public ushort? CurrentPort { get; set; } @@ -45,5 +45,10 @@ namespace Tgstation.Server.Host.Components.Interop.Bridge /// The for requests. /// public ChunkData Chunk { get; set; } + + /// + /// The port that should be used to send world topics, if not the default. + /// + public ushort? TopicPort { get; set; } } } diff --git a/src/Tgstation.Server.Host/Components/Session/ISessionController.cs b/src/Tgstation.Server.Host/Components/Session/ISessionController.cs index a42186c66b..ba894c941c 100644 --- a/src/Tgstation.Server.Host/Components/Session/ISessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/ISessionController.cs @@ -2,9 +2,9 @@ using System.Threading; using System.Threading.Tasks; +using Tgstation.Server.Api.Models.Internal; using Tgstation.Server.Host.Components.Deployment; using Tgstation.Server.Host.Components.Interop.Topic; -using Tgstation.Server.Host.Models; using Tgstation.Server.Host.System; namespace Tgstation.Server.Host.Components.Session @@ -37,7 +37,12 @@ namespace Tgstation.Server.Host.Components.Session /// /// Gets the associated with the . /// - CompileJob CompileJob { get; } + Models.CompileJob CompileJob { get; } + + /// + /// Gets the associated with the . + /// + ByondVersion ByondVersion { get; } /// /// Gets the associated with the . @@ -98,14 +103,6 @@ namespace Tgstation.Server.Host.Components.Session /// A resulting in the of /world/Topic(). ValueTask SendCommand(TopicParameters parameters, CancellationToken cancellationToken); - /// - /// Causes the world to start listening on a . - /// - /// The port to change to. - /// The for the operation. - /// A resulting in if the operation succeeded, otherwise. - Task SetPort(ushort newPort, CancellationToken cancellatonToken); - /// /// Attempts to change the current to . /// diff --git a/src/Tgstation.Server.Host/Components/Session/SessionController.cs b/src/Tgstation.Server.Host/Components/Session/SessionController.cs index f8a9a928f9..3802387b61 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionController.cs @@ -14,6 +14,7 @@ using Newtonsoft.Json; using Serilog.Context; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Internal; using Tgstation.Server.Common.Extensions; using Tgstation.Server.Host.Components.Chat; using Tgstation.Server.Host.Components.Deployment; @@ -51,6 +52,9 @@ namespace Tgstation.Server.Host.Components.Session /// public Models.CompileJob CompileJob => ReattachInformation.Dmb.CompileJob; + /// + public ByondVersion ByondVersion => ReattachInformation.Dmb.ByondVersion; + /// public RebootState RebootState => ReattachInformation.RebootState; @@ -167,11 +171,6 @@ namespace Tgstation.Server.Host.Components.Session /// readonly object synchronizationLock; - /// - /// The waits on when DreamDaemon currently has it's ports closed. - /// - TaskCompletionSource portAssignmentTcs; - /// /// The that completes when DD sends a valid startup bridge request. /// @@ -197,11 +196,6 @@ namespace Tgstation.Server.Host.Components.Session /// volatile uint rebootBridgeRequestsProcessing; - /// - /// The port to assign DreamDaemon when it queries for it. - /// - ushort? nextPort; - /// /// The for the . /// @@ -484,40 +478,6 @@ namespace Tgstation.Server.Host.Components.Session return fullResponse; } - /// - public Task SetPort(ushort port, CancellationToken cancellationToken) - { - CheckDisposed(); - - if (port == 0) - throw new ArgumentOutOfRangeException(nameof(port), port, "port must not be zero!"); - - async Task ImmediateTopicPortChange() - { - var commandResult = await SendCommand( - new TopicParameters(port), - cancellationToken); - - if (commandResult?.ErrorMessage != null) - return false; - - ReattachInformation.Port = port; - return true; - } - - lock (synchronizationLock) - if (portClosedForReboot) - { - if (portAssignmentTcs != null) - throw new InvalidOperationException("A port change operation is already in progress!"); - nextPort = port; - portAssignmentTcs = new TaskCompletionSource(); - return portAssignmentTcs.Task; - } - else - return ImmediateTopicPortChange(); - } - /// public async ValueTask SetRebootState(RebootState newRebootState, CancellationToken cancellationToken) { @@ -702,37 +662,8 @@ namespace Tgstation.Server.Host.Components.Session TerminationWasRequested = true; process.Terminate(); break; - case BridgeCommandType.PortUpdate: - lock (synchronizationLock) - { - if (!parameters.CurrentPort.HasValue) - { - /////UHHHH - Logger.LogWarning("DreamDaemon sent new port command without providing it's own!"); - return BridgeError("Missing stringified port as data parameter!"); - } - - var currentPort = parameters.CurrentPort.Value; - if (!nextPort.HasValue) - ReattachInformation.Port = parameters.CurrentPort.Value; // not ready yet, so what we'll do is accept the random port DD opened on for now and change it later when we decide to - else - { - // nextPort is ready, tell DD to switch to that - // if it fails it'll kill itself - response.NewPort = nextPort.Value; - ReattachInformation.Port = nextPort.Value; - nextPort = null; - - // we'll also get here from SetPort so complete that task - var tmpTcs = portAssignmentTcs; - portAssignmentTcs = null; - tmpTcs.SetResult(true); - } - - portClosedForReboot = false; - } - - break; + case BridgeCommandType.DeprecatedPortUpdate: + return BridgeError("Port switching is no longer supported!"); case BridgeCommandType.Startup: apiValidationStatus = ApiValidationStatus.BadValidationRequest; if (parameters.Version == null) @@ -774,6 +705,13 @@ namespace Tgstation.Server.Host.Components.Session ReattachInformation.RuntimeInformation.ServerPort, ReattachInformation.RuntimeInformation.ApiValidateOnly); + if (parameters.TopicPort.HasValue) + { + var newTopicPort = parameters.TopicPort.Value; + Logger.LogInformation("Server is requesting use of port {topicPort} for topic communications", newTopicPort); + ReattachInformation.Port = newTopicPort; + } + // Load custom commands chatTrackingContext.CustomCommands = parameters.CustomCommands; Interlocked.Exchange(ref startupTcs, new TaskCompletionSource()).SetResult(); @@ -816,7 +754,7 @@ namespace Tgstation.Server.Host.Components.Session /// A new errored . BridgeResponse BridgeError(string message) { - Logger.LogWarning("Bridge request chunking error: {message}", message); + Logger.LogWarning("Bridge request error: {message}", message); return new BridgeResponse { ErrorMessage = message, diff --git a/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs index fa9159527d..dc15334e75 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs @@ -255,6 +255,11 @@ namespace Tgstation.Server.Host.Components.Watchdog await CheckLaunchResult(Server, "Server", cancellationToken); Server.EnableCustomChatCommands(); + + // persist again, because the DMAPI can say we need a different topic port (Original OD behavior) + // kinda hacky imo, but at least we can safely forget about this + if (!reattachInProgress) + await SessionPersistor.Save(Server.ReattachInformation, cancellationToken); } catch (Exception ex) { diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs index cdc5307352..af112dae3e 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Tgstation.Server.Api.Models; using Tgstation.Server.Api.Models.Internal; using Tgstation.Server.Host.Components.Chat; using Tgstation.Server.Host.Components.Deployment; @@ -245,6 +246,16 @@ namespace Tgstation.Server.Host.Components.Watchdog canSeamlesslySwap = false; } + if (Server.ByondVersion.Engine.Value != EngineType.Byond + || compileJobProvider.ByondVersion.Engine.Value != EngineType.Byond) + { + Logger.LogDebug( + "Not swapping to new compile job {newCompileJobId} as it or the current compile job ({oldCompileJobId}) is not using the BYOND engine. Queueing graceful restart instead...", + compileJobProvider.CompileJob.Id, + Server.CompileJob.Id); + canSeamlesslySwap = false; + } + if (!canSeamlesslySwap) { compileJobProvider.Dispose();