tgstation-server 5.12.7
The /tg/station 13 server suite
Loading...
Searching...
No Matches
WindowsByondInstaller.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading;
5using System.Threading.Tasks;
6
7using Microsoft.Extensions.Logging;
8using Microsoft.Extensions.Options;
9
16
18{
23 {
27 const string ByondConfigDirectory = "byond/cfg";
28
32 const string ByondDreamDaemonConfigFilename = "daemon.txt";
33
37 const string ByondNoPromptTrustedMode = "trusted-check 0";
38
42 const string ByondDXDir = "byond/directx";
43
47 const string TgsFirewalledDDFile = "TGSFirewalledDD";
48
50 public override string DreamMakerName => "dm.exe";
51
53 public override string PathToUserByondFolder { get; }
54
56 protected override string ByondRevisionsUrlTemplate => "https://secure.byond.com/download/build/{0}/{0}.{1}_byond.zip";
57
62
67
71 readonly SemaphoreSlim semaphore;
72
77
88 IIOManager ioManager,
90 IOptions<GeneralConfiguration> generalConfigurationOptions,
91 ILogger<WindowsByondInstaller> logger)
92 : base(ioManager, fileDownloader, logger)
93 {
94 this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
95 generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
96
97 PathToUserByondFolder = IOManager.ResolvePath(IOManager.ConcatPath(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "BYOND"));
98
99 semaphore = new SemaphoreSlim(1);
100 installedDirectX = false;
101 }
102
104 public void Dispose() => semaphore.Dispose();
105
107 public override string GetDreamDaemonName(Version version, out bool supportsCli)
108 {
109 ArgumentNullException.ThrowIfNull(version);
110
111 supportsCli = version.Major >= 515 && version.Minor >= 1598;
112 return supportsCli ? "dd.exe" : "dreamdaemon.exe";
113 }
114
116 public override Task InstallByond(Version version, string path, CancellationToken cancellationToken)
117 {
118 var tasks = new List<Task>
119 {
120 SetNoPromptTrusted(path, cancellationToken),
121 InstallDirectX(path, cancellationToken),
122 };
123
125 tasks.Add(AddDreamDaemonToFirewall(version, path, cancellationToken));
126
127 return Task.WhenAll(tasks);
128 }
129
131 public override async Task UpgradeInstallation(Version version, string path, CancellationToken cancellationToken)
132 {
133 ArgumentNullException.ThrowIfNull(version);
134 ArgumentNullException.ThrowIfNull(path);
135
137 return;
138
139 GetDreamDaemonName(version, out var usesDDExe);
140 if (!usesDDExe)
141 return;
142
143 if (await IOManager.FileExists(IOManager.ConcatPath(path, TgsFirewalledDDFile), cancellationToken))
144 return;
145
146 Logger.LogInformation("BYOND Version {version} needs dd.exe added to firewall", version);
147 await AddDreamDaemonToFirewall(version, path, cancellationToken);
148 }
149
156 async Task SetNoPromptTrusted(string path, CancellationToken cancellationToken)
157 {
158 var configPath = IOManager.ConcatPath(path, ByondConfigDirectory);
159 await IOManager.CreateDirectory(configPath, cancellationToken);
160
161 var configFilePath = IOManager.ConcatPath(configPath, ByondDreamDaemonConfigFilename);
162 Logger.LogTrace("Disabling trusted prompts in {configFilePath}...", configFilePath);
164 configFilePath,
165 Encoding.UTF8.GetBytes(ByondNoPromptTrustedMode),
166 cancellationToken);
167 }
168
175 async Task InstallDirectX(string path, CancellationToken cancellationToken)
176 {
177 using var lockContext = await SemaphoreSlimContext.Lock(semaphore, cancellationToken);
179 {
180 Logger.LogTrace("DirectX already installed.");
181 return;
182 }
183
184 Logger.LogTrace("Installing DirectX redistributable...");
185
186 // always install it, it's pretty fast and will do better redundancy checking than us
187 var rbdx = IOManager.ConcatPath(path, ByondDXDir);
188
189 try
190 {
191 // noShellExecute because we aren't doing runas shennanigans
192 await using var directXInstaller = await processExecutor.LaunchProcess(
193 IOManager.ConcatPath(rbdx, "DXSETUP.exe"),
194 rbdx,
195 "/silent",
196 noShellExecute: true);
197
198 int exitCode;
199 using (cancellationToken.Register(() => directXInstaller.Terminate()))
200 exitCode = await directXInstaller.Lifetime;
201 cancellationToken.ThrowIfCancellationRequested();
202
203 if (exitCode != 0)
204 throw new JobException(ErrorCode.ByondDirectXInstallFail, new JobException($"Invalid exit code: {exitCode}"));
205 installedDirectX = true;
206 }
207 catch (Exception e)
208 {
209 throw new JobException(ErrorCode.ByondDirectXInstallFail, e);
210 }
211 }
212
220 async Task AddDreamDaemonToFirewall(Version version, string path, CancellationToken cancellationToken)
221 {
222 var dreamDaemonName = GetDreamDaemonName(version, out var supportsCli);
223
224 var dreamDaemonPath = IOManager.ResolvePath(
226 path,
228 dreamDaemonName));
229
230 Logger.LogInformation("Adding Windows Firewall exception for {path}...", dreamDaemonPath);
231 try
232 {
233 await using var netshProcess = await processExecutor.LaunchProcess(
234 "netsh.exe",
236 $"advfirewall firewall add rule name=\"TGS DreamDaemon\" program=\"{dreamDaemonPath}\" protocol=tcp dir=in enable=yes action=allow",
237 readStandardHandles: true,
238 noShellExecute: true);
239
240 int exitCode;
241 using (cancellationToken.Register(() => netshProcess.Terminate()))
242 exitCode = await netshProcess.Lifetime;
243 cancellationToken.ThrowIfCancellationRequested();
244
245 Logger.LogDebug(
246 "netsh.exe output:{newLine}{output}",
247 Environment.NewLine,
248 await netshProcess.GetCombinedOutput(cancellationToken));
249
250 if (exitCode != 0)
251 throw new JobException(ErrorCode.ByondDreamDaemonFirewallFail, new JobException($"Invalid exit code: {exitCode}"));
252
253 if (supportsCli)
256 Array.Empty<byte>(),
257 cancellationToken);
258 }
259 catch (Exception ex)
260 {
261 throw new JobException(ErrorCode.ByondDreamDaemonFirewallFail, ex);
262 }
263 }
264 }
265}
IIOManager IOManager
Gets the IIOManager for the ByondInstallerBase.
readonly IFileDownloader fileDownloader
The IFileDownloader for the ByondInstallerBase.
ILogger< ByondInstallerBase > Logger
Gets the ILogger for the ByondInstallerBase.
const string BinPath
The path to the BYOND bin folder.
Definition: ByondManager.cs:27
async Task SetNoPromptTrusted(string path, CancellationToken cancellationToken)
Creates the BYOND cfg file that prevents the trusted mode dialog from appearing when launching DreamD...
const string ByondDXDir
The directory that contains the BYOND directx redistributable.
override Task InstallByond(Version version, string path, CancellationToken cancellationToken)
Does actions necessary to get an extracted BYOND installation working. A Task representing the runnin...
override string PathToUserByondFolder
The path to the BYOND folder for the user.
override string GetDreamDaemonName(Version version, out bool supportsCli)
Get the file name of the DreamDaemon executable. The file name of the DreamDaemon executable.
const string ByondNoPromptTrustedMode
Setting to add to ByondDreamDaemonConfigFilename to suppress an invisible user prompt for running a t...
override string DreamMakerName
Get the file name of the DreamMaker executable.
const string ByondConfigDirectory
Directory to byond installation configuration.
const string ByondDreamDaemonConfigFilename
BYOND's DreamDaemon config file.
readonly IProcessExecutor processExecutor
The IProcessExecutor for the WindowsByondInstaller.
async Task InstallDirectX(string path, CancellationToken cancellationToken)
Attempt to install the DirectX redistributable included with BYOND.
async Task AddDreamDaemonToFirewall(Version version, string path, CancellationToken cancellationToken)
Attempt to add the DreamDaemon executable as an exception to the Windows firewall.
readonly GeneralConfiguration generalConfiguration
The GeneralConfiguration for the WindowsByondInstaller.
readonly SemaphoreSlim semaphore
The SemaphoreSlim for the WindowsByondInstaller.
WindowsByondInstaller(IProcessExecutor processExecutor, IIOManager ioManager, IFileDownloader fileDownloader, IOptions< GeneralConfiguration > generalConfigurationOptions, ILogger< WindowsByondInstaller > logger)
Initializes a new instance of the WindowsByondInstaller class.
const string TgsFirewalledDDFile
The file TGS uses to determine if dd.exe has been firewalled.
override async Task UpgradeInstallation(Version version, string path, CancellationToken cancellationToken)
Does actions necessary to get upgrade a BYOND version installed by a previous version of TGS....
bool SkipAddingByondFirewallException
If the netsh.exe execution to exempt DreamDaemon from Windows firewall should be skipped.
Operation exceptions thrown from the context of a Models.Job.
Definition: JobException.cs:11
static async ValueTask< SemaphoreSlimContext > Lock(SemaphoreSlim semaphore, CancellationToken cancellationToken)
Asyncronously locks a semaphore .
Interface for using filesystems.
Definition: IIOManager.cs:13
string ResolvePath()
Retrieve the full path of the current working directory.
string ConcatPath(params string[] paths)
Combines an array of strings into a path.
Task CreateDirectory(string path, CancellationToken cancellationToken)
Create a directory at path .
Task< bool > FileExists(string path, CancellationToken cancellationToken)
Check that the file at path exists.
Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken)
Writes some contents to a file at path overwriting previous content.
Task< IProcess > LaunchProcess(string fileName, string workingDirectory, string arguments=null, string fileRedirect=null, bool readStandardHandles=false, bool noShellExecute=false)
Launch a IProcess.
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
Definition: ErrorCode.cs:11