tgstation-server 6.12.0
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
74 IIOManager installationIOManager,
77 string serverExePath,
78 string compilerExePath,
79 Task installationTask,
80 EngineVersion version)
81 : base(installationIOManager)
82 {
83 this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
84 this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
85 ServerExePath = serverExePath ?? throw new ArgumentNullException(nameof(serverExePath));
86 CompilerExePath = compilerExePath ?? throw new ArgumentNullException(nameof(compilerExePath));
87 InstallationTask = installationTask ?? throw new ArgumentNullException(nameof(installationTask));
88 Version = version ?? throw new ArgumentNullException(nameof(version));
89
90 if (version.Engine!.Value != EngineType.OpenDream)
91 throw new ArgumentException($"Invalid EngineType: {version.Engine.Value}", nameof(version));
92 }
93
95 public override string FormatServerArguments(
96 IDmbProvider dmbProvider,
97 IReadOnlyDictionary<string, string> parameters,
98 DreamDaemonLaunchParameters launchParameters,
99 string? logFilePath)
100 {
101 ArgumentNullException.ThrowIfNull(dmbProvider);
102 ArgumentNullException.ThrowIfNull(parameters);
103 ArgumentNullException.ThrowIfNull(launchParameters);
104
105 if (!parameters.TryGetValue(DMApiConstants.ParamAccessIdentifier, out var accessIdentifier))
106 throw new ArgumentException($"parameters must have \"{DMApiConstants.ParamAccessIdentifier}\" set!", nameof(parameters));
107
108 var parametersString = EncodeParameters(parameters, launchParameters);
109
110 var arguments = $"--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}\"";
111 return arguments;
112 }
113
115 public override string FormatCompilerArguments(string dmePath, string? additionalArguments)
116 {
117 if (String.IsNullOrWhiteSpace(additionalArguments))
118 additionalArguments = String.Empty;
119 else
120 additionalArguments = $"{additionalArguments.Trim()} ";
121
122 return $"--suppress-unimplemented --notices-enabled {additionalArguments}\"{dmePath ?? throw new ArgumentNullException(nameof(dmePath))}\"";
123 }
124
126 public override async ValueTask StopServerProcess(
127 ILogger logger,
128 IProcess process,
129 string accessIdentifier,
130 ushort port,
131 CancellationToken cancellationToken)
132 {
133 ArgumentNullException.ThrowIfNull(logger);
134
135 const int MaximumTerminationSeconds = 5;
136
137 logger.LogTrace("Attempting Robust.Server graceful exit (Timeout: {seconds}s)...", MaximumTerminationSeconds);
138 var timeout = asyncDelayer.Delay(TimeSpan.FromSeconds(MaximumTerminationSeconds), cancellationToken).AsTask();
139 var lifetime = process.Lifetime;
140 if (lifetime.IsCompleted)
141 logger.LogTrace("Robust.Server already exited");
142
143 var stopwatch = Stopwatch.StartNew();
144 try
145 {
146 using var httpClient = httpClientFactory.CreateClient();
147 using var request = new HttpRequestMessage();
148 request.Headers.Add("WatchdogToken", accessIdentifier);
149 request.RequestUri = new Uri($"http://localhost:{port}/shutdown");
150 request.Content = new StringContent(
151 "{\"Reason\":\"TGS session termination\"}",
152 Encoding.UTF8,
153 new MediaTypeHeaderValue(MediaTypeNames.Application.Json));
154 request.Method = HttpMethod.Post;
155
156 var responseTask = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
157 try
158 {
159 await Task.WhenAny(timeout, lifetime, responseTask);
160 if (responseTask.IsCompleted)
161 {
162 using var response = await responseTask;
163 if (response.IsSuccessStatusCode)
164 {
165 logger.LogDebug("Robust.Server responded to the shutdown command successfully ({requestMs}ms). Waiting for exit...", stopwatch.ElapsedMilliseconds);
166 await Task.WhenAny(timeout, lifetime);
167 }
168 }
169
170 if (!lifetime.IsCompleted)
171 logger.LogWarning("Robust.Server graceful exit timed out!");
172 }
173 catch (Exception ex) when (ex is not OperationCanceledException)
174 {
175 logger.LogDebug(ex, "Unable to send graceful exit request to Robust.Server watchdog API!");
176 }
177
178 if (lifetime.IsCompleted)
179 {
180 logger.LogTrace("Robust.Server exited without termination");
181 return;
182 }
183 }
184 finally
185 {
186 logger.LogTrace("Robust.Server graceful shutdown attempt took {totalMs}ms", stopwatch.ElapsedMilliseconds);
187 }
188
189 await base.StopServerProcess(logger, process, accessIdentifier, port, cancellationToken);
190 }
191 }
192}
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.
OpenDreamInstallation(IIOManager installationIOManager, IAsyncDelayer asyncDelayer, IAbstractHttpClientFactory httpClientFactory, string serverExePath, string compilerExePath, Task installationTask, EngineVersion version)
Initializes a new instance of the OpenDreamInstallation class.
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the OpenDreamInstallation.
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.
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