tgstation-server 6.12.3
The /tg/station 13 server suite
Loading...
Searching...
No Matches
OpenDreamInstallation.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.Net.Http;
5using System.Net.Http.Headers;
6using System.Net.Mime;
7using System.Text;
8using System.Threading;
9using System.Threading.Tasks;
10
11using Microsoft.Extensions.Logging;
12
21
23{
28 {
30 public override EngineVersion Version { get; }
31
33 public override string ServerExePath { get; }
34
36 public override string CompilerExePath { get; }
37
39 public override bool PromptsForNetworkAccess => false;
40
42 public override bool HasStandardOutput => true;
43
45 public override bool PreferFileLogging => true;
46
48 public override bool UseDotnetDump => true;
49
51 public override Task InstallationTask { get; }
52
57
62
66 readonly string serverDllPath;
67
71 readonly string compilerDllPath;
72
85 IIOManager installationIOManager,
88 string dotnetPath,
89 string serverDllPath,
90 string compilerDllPath,
91 Task installationTask,
92 EngineVersion version)
93 : base(installationIOManager)
94 {
95 this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
96 this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
97
98 ServerExePath = dotnetPath ?? throw new ArgumentNullException(nameof(dotnetPath));
99 CompilerExePath = dotnetPath;
100
101 this.serverDllPath = serverDllPath ?? throw new ArgumentNullException(nameof(serverDllPath));
102 this.compilerDllPath = compilerDllPath ?? throw new ArgumentNullException(nameof(compilerDllPath));
103 InstallationTask = installationTask ?? throw new ArgumentNullException(nameof(installationTask));
104 Version = version ?? throw new ArgumentNullException(nameof(version));
105
106 if (version.Engine!.Value != EngineType.OpenDream)
107 throw new ArgumentException($"Invalid EngineType: {version.Engine.Value}", nameof(version));
108 }
109
111 public override string FormatServerArguments(
112 IDmbProvider dmbProvider,
113 IReadOnlyDictionary<string, string> parameters,
114 DreamDaemonLaunchParameters launchParameters,
115 string? logFilePath)
116 {
117 ArgumentNullException.ThrowIfNull(dmbProvider);
118 ArgumentNullException.ThrowIfNull(parameters);
119 ArgumentNullException.ThrowIfNull(launchParameters);
120
121 if (!parameters.TryGetValue(DMApiConstants.ParamAccessIdentifier, out var accessIdentifier))
122 throw new ArgumentException($"parameters must have \"{DMApiConstants.ParamAccessIdentifier}\" set!", nameof(parameters));
123
124 var parametersString = EncodeParameters(parameters, launchParameters);
125
126 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} --cvar opendream.world_params=\"{parametersString}\" --cvar opendream.json_path=\"./{dmbProvider.DmbName}\"";
127 return arguments;
128 }
129
131 public override string FormatCompilerArguments(string dmePath, string? additionalArguments)
132 {
133 if (String.IsNullOrWhiteSpace(additionalArguments))
134 additionalArguments = String.Empty;
135 else
136 additionalArguments = $"{additionalArguments.Trim()} ";
137
138 return $"{compilerDllPath} --suppress-unimplemented --notices-enabled {additionalArguments}\"{dmePath ?? throw new ArgumentNullException(nameof(dmePath))}\"";
139 }
140
142 public override async ValueTask StopServerProcess(
143 ILogger logger,
144 IProcess process,
145 string accessIdentifier,
146 ushort port,
147 CancellationToken cancellationToken)
148 {
149 ArgumentNullException.ThrowIfNull(logger);
150
151 const int MaximumTerminationSeconds = 5;
152
153 logger.LogTrace("Attempting Robust.Server graceful exit (Timeout: {seconds}s)...", MaximumTerminationSeconds);
154 var timeout = asyncDelayer.Delay(TimeSpan.FromSeconds(MaximumTerminationSeconds), cancellationToken).AsTask();
155 var lifetime = process.Lifetime;
156 if (lifetime.IsCompleted)
157 logger.LogTrace("Robust.Server already exited");
158
159 var stopwatch = Stopwatch.StartNew();
160 try
161 {
162 using var httpClient = httpClientFactory.CreateClient();
163 using var request = new HttpRequestMessage();
164 request.Headers.Add("WatchdogToken", accessIdentifier);
165 request.RequestUri = new Uri($"http://localhost:{port}/shutdown");
166 request.Content = new StringContent(
167 "{\"Reason\":\"TGS session termination\"}",
168 Encoding.UTF8,
169 new MediaTypeHeaderValue(MediaTypeNames.Application.Json));
170 request.Method = HttpMethod.Post;
171
172 var responseTask = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
173 try
174 {
175 await Task.WhenAny(timeout, lifetime, responseTask);
176 if (responseTask.IsCompleted)
177 {
178 using var response = await responseTask;
179 if (response.IsSuccessStatusCode)
180 {
181 logger.LogDebug("Robust.Server responded to the shutdown command successfully ({requestMs}ms). Waiting for exit...", stopwatch.ElapsedMilliseconds);
182 await Task.WhenAny(timeout, lifetime);
183 }
184 }
185
186 if (!lifetime.IsCompleted)
187 logger.LogWarning("Robust.Server graceful exit timed out!");
188 }
189 catch (Exception ex) when (ex is not OperationCanceledException)
190 {
191 logger.LogDebug(ex, "Unable to send graceful exit request to Robust.Server watchdog API!");
192 }
193
194 if (lifetime.IsCompleted)
195 {
196 logger.LogTrace("Robust.Server exited without termination");
197 return;
198 }
199 }
200 finally
201 {
202 logger.LogTrace("Robust.Server graceful shutdown attempt took {totalMs}ms", stopwatch.ElapsedMilliseconds);
203 }
204
205 await base.StopServerProcess(logger, process, accessIdentifier, port, cancellationToken);
206 }
207 }
208}
Information about an engine installation.
static string EncodeParameters(IReadOnlyDictionary< string, string > parameters, DreamDaemonLaunchParameters launchParameters)
Encode given parameters for passing as world.params on the command line.
Implementation of IEngineInstallation for EngineType.OpenDream.
override string CompilerExePath
The full path to the dm/DreamMaker executable.
readonly IAbstractHttpClientFactory httpClientFactory
The IAbstractHttpClientFactory for the OpenDreamInstallation.
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the OpenDreamInstallation.
OpenDreamInstallation(IIOManager installationIOManager, IAsyncDelayer asyncDelayer, IAbstractHttpClientFactory httpClientFactory, string dotnetPath, string serverDllPath, string compilerDllPath, Task installationTask, EngineVersion version)
Initializes a new instance of the OpenDreamInstallation class.
override bool PromptsForNetworkAccess
If ServerExePath may create network prompts.
override async ValueTask StopServerProcess(ILogger logger, IProcess process, string accessIdentifier, ushort port, CancellationToken cancellationToken)
Kills a given engine server process .A ValueTask representing the running operation.
override string FormatCompilerArguments(string dmePath, string? additionalArguments)
Return the command line arguments for compiling a given dmePath if compilation is necessary....
override string FormatServerArguments(IDmbProvider dmbProvider, IReadOnlyDictionary< string, string > parameters, DreamDaemonLaunchParameters launchParameters, string? logFilePath)
Return the command line arguments for launching with given launchParameters .The formatted arguments ...
override bool HasStandardOutput
If ServerExePath supports being run as a command-line application and outputs log information to be c...
override EngineVersion Version
The EngineVersion of the IEngineInstallation.
override string ServerExePath
The full path to the game server executable.
override bool PreferFileLogging
If HasStandardOutput is set, this indicates that the engine server has good file logging that should ...
override bool UseDotnetDump
If dotnet-dump should be used to create process dumps for this installation.
override Task InstallationTask
The Task that completes when the BYOND version finished installing.
readonly string serverDllPath
Path to the Robust.Server.dll.
Constants used for communication with the DMAPI.
const string ParamAccessIdentifier
Identifies the DMApiParameters.AccessIdentifier for the session.
Provides absolute paths to the latest compiled .dmbs.
Interface for using filesystems.
Definition IIOManager.cs:13
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.
Definition IProcess.cs:11
EngineType
The type of engine the codebase is using.
Definition EngineType.cs:7