2using System.Collections.Generic;
3using System.Diagnostics;
5using System.Net.Http.Headers;
9using System.Threading.Tasks;
11using Microsoft.Extensions.Logging;
89 Task installationTask,
91 : base(installationIOManager)
96 ServerExePath = dotnetPath ??
throw new ArgumentNullException(nameof(dotnetPath));
101 InstallationTask = installationTask ??
throw new ArgumentNullException(nameof(installationTask));
102 Version = version ??
throw new ArgumentNullException(nameof(version));
105 throw new ArgumentException($
"Invalid EngineType: {version.Engine.Value}", nameof(version));
111 IReadOnlyDictionary<string, string>? parameters,
113 string accessIdentifier,
116 ArgumentNullException.ThrowIfNull(dmbProvider);
117 ArgumentNullException.ThrowIfNull(launchParameters);
118 ArgumentNullException.ThrowIfNull(accessIdentifier);
120 var encodedParameters = EncodeParameters(parameters, launchParameters);
121 var parametersString = !String.IsNullOrEmpty(encodedParameters)
122 ? $
" --cvar opendream.world_params=\"{encodedParameters}\""
125 var
arguments = $
"{serverDllPath} --cvar {(logFilePath != null ? $"log.path=\
"{InstallationIOManager.GetDirectoryName(logFilePath)}\" --cvar log.format=\"{InstallationIOManager.GetFileName(logFilePath)}\"" :
"log.enabled=false")} --cvar watchdog.token={accessIdentifier} --cvar log.runtimelog=
false --cvar net.port={launchParameters.Port!.Value} --cvar opendream.topic_port={launchParameters.OpenDreamTopicPort!.Value}{parametersString} --cvar opendream.json_path=\
"./{dmbProvider.DmbName}\"";
132 if (String.IsNullOrWhiteSpace(additionalArguments))
133 additionalArguments = String.Empty;
135 additionalArguments = $
"{additionalArguments.Trim()} ";
137 return $
"{compilerDllPath} --suppress-unimplemented --notices-enabled {additionalArguments}\"{dmePath ?? throw new ArgumentNullException(nameof(dmePath))}\"";
144 string accessIdentifier,
146 CancellationToken cancellationToken)
148 ArgumentNullException.ThrowIfNull(logger);
150 const int MaximumTerminationSeconds = 5;
152 logger.LogTrace(
"Attempting Robust.Server graceful exit (Timeout: {seconds}s)...", MaximumTerminationSeconds);
153 var timeout = asyncDelayer.Delay(TimeSpan.FromSeconds(MaximumTerminationSeconds), cancellationToken).AsTask();
155 if (lifetime.IsCompleted)
156 logger.LogTrace(
"Robust.Server already exited");
158 var stopwatch = Stopwatch.StartNew();
161 using var httpClient = httpClientFactory.CreateClient();
162 using var request =
new HttpRequestMessage();
163 request.Headers.Add(
"WatchdogToken", accessIdentifier);
164 request.RequestUri =
new Uri($
"http://localhost:{port}/shutdown");
165 request.Content =
new StringContent(
166 "{\"Reason\":\"TGS session termination\"}",
168 new MediaTypeHeaderValue(MediaTypeNames.Application.Json));
169 request.Method = HttpMethod.Post;
171 var responseTask = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
174 await Task.WhenAny(timeout, lifetime, responseTask);
175 if (responseTask.IsCompleted)
177 using var response = await responseTask;
178 if (response.IsSuccessStatusCode)
180 logger.LogDebug(
"Robust.Server responded to the shutdown command successfully ({requestMs}ms). Waiting for exit...", stopwatch.ElapsedMilliseconds);
181 await Task.WhenAny(timeout, lifetime);
185 if (!lifetime.IsCompleted)
186 logger.LogWarning(
"Robust.Server graceful exit timed out!");
188 catch (
Exception ex) when (ex is not OperationCanceledException)
190 logger.LogDebug(ex,
"Unable to send graceful exit request to Robust.Server watchdog API!");
193 if (lifetime.IsCompleted)
195 logger.LogTrace(
"Robust.Server exited without termination");
201 logger.LogTrace(
"Robust.Server graceful shutdown attempt took {totalMs}ms", stopwatch.ElapsedMilliseconds);
204 await base.StopServerProcess(logger, process, accessIdentifier, port, cancellationToken);
Information about an engine installation.
EngineType? Engine
The EngineType.
Launch settings for DreamDaemon.
Implementation of IEngineInstallation for EngineType.OpenDream.
readonly IHttpClientFactory httpClientFactory
The IHttpClientFactory for the OpenDreamInstallation.
override string CompilerExePath
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the OpenDreamInstallation.
readonly string compilerDllPath
Path to the DMCompiler.dll.
override string FormatServerArguments(IDmbProvider dmbProvider, IReadOnlyDictionary< string, string >? parameters, DreamDaemonLaunchParameters launchParameters, string accessIdentifier, string? logFilePath)
override bool PromptsForNetworkAccess
override async ValueTask StopServerProcess(ILogger logger, IProcess process, string accessIdentifier, ushort port, CancellationToken cancellationToken)
override string FormatCompilerArguments(string dmePath, string? additionalArguments)
override bool HasStandardOutput
override EngineVersion Version
override string ServerExePath
override bool PreferFileLogging
override bool UseDotnetDump
override Task InstallationTask
OpenDreamInstallation(IIOManager installationIOManager, IAsyncDelayer asyncDelayer, IHttpClientFactory httpClientFactory, string dotnetPath, string serverDllPath, string compilerDllPath, Task installationTask, EngineVersion version)
Initializes a new instance of the OpenDreamInstallation class.
readonly string serverDllPath
Path to the Robust.Server.dll.
Provides absolute paths to the latest compiled .dmbs.
Interface for using filesystems.
Task< int?> Lifetime
The Task<TResult> resulting in the exit code of the process or null if the process was detached.
Abstraction over a global::System.Diagnostics.Process.
For waiting asynchronously.
EngineType
The type of engine the codebase is using.