tgstation-server 6.19.1
The /tg/station 13 server suite
Loading...
Searching...
No Matches
EngineManager.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Linq;
5using System.Net.Http;
6using System.Text;
7using System.Threading;
8using System.Threading.Tasks;
9
10using Microsoft.Extensions.Logging;
11
19
21{
24 {
28 const string VersionFileName = "Version.txt";
29
33 const string ActiveVersionFileName = "ActiveVersion.txt";
34
36 public EngineVersion? ActiveVersion { get; private set; }
37
39 public IReadOnlyList<EngineVersion> InstalledVersions
40 {
41 get
42 {
44 return installedVersions.Keys.ToList();
45 }
46 }
47
52
57
62
67
71 readonly ILogger<EngineManager> logger;
72
76 readonly Dictionary<EngineVersion, ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock>> installedVersions;
77
81 readonly SemaphoreSlim changeDeleteSemaphore;
82
86 volatile TaskCompletionSource activeVersionChanged;
87
93 {
94 ArgumentNullException.ThrowIfNull(version);
95
96 if (!version.Engine.HasValue)
97 throw new InvalidOperationException("version.Engine cannot be null!");
98
99 if (version.CustomIteration == 0)
100 throw new InvalidOperationException("version.CustomIteration cannot be 0!");
101 }
102
116 ILogger<EngineManager> logger)
117 {
118 this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
119 this.engineInstaller = engineInstaller ?? throw new ArgumentNullException(nameof(engineInstaller));
120 this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
121 this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
122 this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
123
124 installedVersions = new Dictionary<EngineVersion, ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock>>();
125 changeDeleteSemaphore = new SemaphoreSlim(1);
126 activeVersionChanged = new TaskCompletionSource();
127 }
128
130 public void Dispose() => changeDeleteSemaphore.Dispose();
131
133 public async ValueTask ChangeVersion(
134 JobProgressReporter progressReporter,
135 EngineVersion version,
136 Stream? customVersionStream,
137 bool allowInstallation,
138 CancellationToken cancellationToken)
139 {
140 CheckVersionParameter(version);
141
142 using (await SemaphoreSlimContext.Lock(changeDeleteSemaphore, cancellationToken))
143 {
144 using var installLock = await AssertAndLockVersion(
145 progressReporter,
146 version,
147 customVersionStream,
148 false,
149 allowInstallation,
150 cancellationToken);
151
152 // We reparse the version because it could be changed after a custom install.
153 version = new EngineVersion(installLock.Version);
154
155 var stringVersion = version.ToString();
156 await ioManager.WriteAllBytes(ActiveVersionFileName, Encoding.UTF8.GetBytes(stringVersion), cancellationToken);
158 EventType.EngineActiveVersionChange,
159 new List<string?>
160 {
161 ActiveVersion?.ToString(),
162 stringVersion,
163 },
164 false,
165 false,
166 cancellationToken);
167
168 ActiveVersion = version;
169
170 logger.LogInformation("Active version changed to {version}", version);
171 var oldTcs = Interlocked.Exchange(ref activeVersionChanged, new TaskCompletionSource());
172 oldTcs.SetResult();
173 }
174 }
175
177 public async ValueTask<IEngineExecutableLock> UseExecutables(EngineVersion? requiredVersion, string? trustDmbFullPath, CancellationToken cancellationToken)
178 {
179 logger.LogTrace(
180 "Acquiring lock on BYOND version {version}...",
181 requiredVersion?.ToString() ?? $"{ActiveVersion} (active)");
182 var versionToUse = requiredVersion ?? ActiveVersion ?? throw new JobException(ErrorCode.EngineNoVersionsInstalled);
183
184 using var progressReporter = new JobProgressReporter();
185
186 var installLock = await AssertAndLockVersion(
187 progressReporter,
188 versionToUse,
189 null,
190 requiredVersion != null,
191 true,
192 cancellationToken);
193 try
194 {
195 if (trustDmbFullPath != null)
196 await engineInstaller.TrustDmbPath(installLock.Version, trustDmbFullPath, cancellationToken);
197
198 return installLock;
199 }
200 catch
201 {
202 installLock.Dispose();
203 throw;
204 }
205 }
206
208 public async ValueTask DeleteVersion(JobProgressReporter progressReporter, EngineVersion version, CancellationToken cancellationToken)
209 {
210 ArgumentNullException.ThrowIfNull(progressReporter);
211
212 CheckVersionParameter(version);
213
214 logger.LogTrace("DeleteVersion {version}", version);
215
216 var activeVersion = ActiveVersion;
217 if (activeVersion != null && version.Equals(activeVersion))
218 throw new JobException(ErrorCode.EngineCannotDeleteActiveVersion);
219
221 logger.LogTrace("Waiting to acquire installedVersions lock...");
222 lock (installedVersions)
223 {
224 if (!installedVersions.TryGetValue(version, out var containerNullable))
225 {
226 logger.LogTrace("Version {version} already deleted.", version);
227 return;
228 }
229
230 container = containerNullable;
231 logger.LogTrace("Installation container acquired for deletion");
232 }
233
234 progressReporter.StageName = "Waiting for version to not be in use...";
235 while (true)
236 {
237 var containerTask = container.OnZeroReferences;
238
239 // We also want to check when the active version changes in case we need to fail the job because of that.
240 Task activeVersionUpdate;
241 using (await SemaphoreSlimContext.Lock(changeDeleteSemaphore, cancellationToken))
242 activeVersionUpdate = activeVersionChanged.Task;
243
244 logger.LogTrace("Waiting for container.OnZeroReferences or switch of active version...");
245 if (!containerTask.IsCompleted)
247
248 await Task.WhenAny(
249 containerTask,
250 activeVersionUpdate)
251 .WaitAsync(cancellationToken);
252
253 if (containerTask.IsCompleted)
254 logger.LogTrace("All locks for version {version} are gone", version);
255 else
256 logger.LogTrace("activeVersion changed, we may have to wait again. Acquiring semaphore...");
257
258 using (await SemaphoreSlimContext.Lock(changeDeleteSemaphore, cancellationToken))
259 {
260 // check again because it could have become the active version.
261 activeVersion = ActiveVersion;
262 if (activeVersion != null && version.Equals(activeVersion))
263 throw new JobException(ErrorCode.EngineCannotDeleteActiveVersion);
264
265 bool proceed;
266 logger.LogTrace("Locking installedVersions...");
267 lock (installedVersions)
268 {
269 proceed = container.OnZeroReferences.IsCompleted;
270 if (proceed)
271 if (!installedVersions.TryGetValue(version, out var newerContainer))
272 logger.LogWarning("Unable to remove engine installation {version} from list! Is there a duplicate job running?", version);
273 else
274 {
275 if (container != newerContainer)
276 {
277 // Okay let me get this straight, there was a duplicate delete job, it ran before us after we grabbed the container, AND another installation of the same version completed?
278 // I know realistically this is practically impossible, but god damn that small possiblility
279 // best thing to do is check we exclusively own the newer container
280 logger.LogDebug("Extreme race condition encountered, applying concentrated copium...");
281 container = newerContainer;
282 proceed = container.OnZeroReferences.IsCompleted;
283 }
284
285 if (proceed)
286 {
287 logger.LogTrace("Proceeding with installation deletion...");
288 installedVersions.Remove(version);
289 }
290 }
291 }
292
293 if (proceed)
294 {
295 logger.LogInformation("Deleting version {version}...", version);
296 progressReporter.StageName = "Deleting installation...";
297
298 // delete the version file first, because we will know not to re-discover the installation if it's not present and it will get cleaned on reboot
299 var installPath = version.ToString();
300 await ioManager.DeleteFile(
302 cancellationToken);
303 await ioManager.DeleteDirectory(installPath, cancellationToken);
304 return;
305 }
306
307 if (containerTask.IsCompleted)
308 logger.LogDebug(
309 "Another lock was acquired before we could remove version {version} from the list. We will have to wait again.",
310 version);
311 else
312 logger.LogTrace("Not proceeding for some reason or another");
313 }
314 }
315 }
316
318 public async Task StartAsync(CancellationToken cancellationToken)
319 {
320 async ValueTask<byte[]?> GetActiveVersion()
321 {
322 var activeVersionFileExists = await ioManager.FileExists(ActiveVersionFileName, cancellationToken);
323 return !activeVersionFileExists ? null : await ioManager.ReadAllBytes(ActiveVersionFileName, cancellationToken);
324 }
325
326 var activeVersionBytesTask = GetActiveVersion();
327
329 var directories = await ioManager.GetDirectories(DefaultIOManager.CurrentDirectory, cancellationToken);
330
331 var installedVersionPaths = new Dictionary<string, EngineVersion>();
332
333 async ValueTask ReadVersion(string path)
334 {
335 var versionFile = ioManager.ConcatPath(path, VersionFileName);
336 if (!await ioManager.FileExists(versionFile, cancellationToken))
337 {
338 logger.LogWarning("Cleaning path with no version file: {versionPath}", ioManager.ResolvePath(path));
339 await ioManager.DeleteDirectory(path, cancellationToken); // cleanup
340 return;
341 }
342
343 var bytes = await ioManager.ReadAllBytes(versionFile, cancellationToken);
344 var text = Encoding.UTF8.GetString(bytes);
345 EngineVersion version;
346 if (!EngineVersion.TryParse(text, out var versionNullable))
347 {
348 logger.LogWarning("Cleaning path with unparsable version file: {versionPath}", ioManager.ResolvePath(path));
349 await ioManager.DeleteDirectory(path, cancellationToken); // cleanup
350 return;
351 }
352 else
353 version = versionNullable!;
354
355 try
356 {
357 var installation = await engineInstaller.GetInstallation(version, path, Task.CompletedTask, cancellationToken);
358 AddInstallationContainer(installation);
359 logger.LogDebug("Added detected BYOND version {versionKey}...", version);
360 }
361 catch (Exception ex)
362 {
363 logger.LogWarning(
364 ex,
365 "It seems that there are multiple directories that say they contain BYOND version {version}. We're ignoring and cleaning the duplicate: {duplicatePath}",
366 version,
367 ioManager.ResolvePath(path));
368 await ioManager.DeleteDirectory(path, cancellationToken);
369 return;
370 }
371
372 lock (installedVersionPaths)
373 installedVersionPaths.Add(ioManager.ResolvePath(version.ToString()), version);
374 }
375
377 directories
378 .Select(ReadVersion));
379
380 logger.LogTrace("Upgrading BYOND installations...");
382 installedVersionPaths
383 .Select(kvp => engineInstaller.UpgradeInstallation(kvp.Value, kvp.Key, cancellationToken)));
384
385 var activeVersionBytes = await activeVersionBytesTask;
386 if (activeVersionBytes != null)
387 {
388 var activeVersionString = Encoding.UTF8.GetString(activeVersionBytes);
389
390 EngineVersion? activeVersion;
391 bool hasRequestedActiveVersion;
392 lock (installedVersions)
393 hasRequestedActiveVersion = EngineVersion.TryParse(activeVersionString, out activeVersion)
394 && installedVersions.ContainsKey(activeVersion!);
395
396 if (hasRequestedActiveVersion)
397 ActiveVersion = activeVersion; // not setting TCS because there's no need during init
398 else
399 {
400 logger.LogWarning("Failed to load saved active version {activeVersion}!", activeVersionString);
401 await ioManager.DeleteFile(ActiveVersionFileName, cancellationToken);
402 }
403 }
404 }
405
407 public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
408
419 async ValueTask<EngineExecutableLock> AssertAndLockVersion(
420 JobProgressReporter progressReporter,
421 EngineVersion version,
422 Stream? customVersionStream,
423 bool neededForLock,
424 bool allowInstallation,
425 CancellationToken cancellationToken)
426 {
427 var ourTcs = new TaskCompletionSource();
428 IEngineInstallation installation;
429 EngineExecutableLock installLock;
430 bool installedOrInstalling;
431
432 // loop is because of the race condition with potentialInstallation, installedVersions, and CustomIteration selection
433 while (true)
434 {
435 lock (installedVersions)
436 {
437 if (customVersionStream != null)
438 {
439 var customInstallationNumber = 1;
440 do
441 {
442 version.CustomIteration = customInstallationNumber++;
443 }
444 while (installedVersions.ContainsKey(version));
445 }
446 }
447
448 var potentialInstallation = await engineInstaller.GetInstallation(
449 version,
450 ioManager.ResolvePath(version.ToString()),
451 ourTcs.Task,
452 cancellationToken);
453
454 lock (installedVersions)
455 {
456 if (customVersionStream != null && installedVersions.ContainsKey(version))
457 continue;
458
459 installedOrInstalling = installedVersions.TryGetValue(version, out var installationContainerNullable);
461 if (!installedOrInstalling)
462 {
463 if (!allowInstallation)
464 throw new InvalidOperationException($"Engine version {version} not installed!");
465
466 installationContainer = AddInstallationContainer(potentialInstallation);
467 }
468 else
469 installationContainer = installationContainerNullable!;
470
471 installation = installationContainer.Instance;
472 installLock = installationContainer.AddReference();
473 }
474
475 var deploymentPipelineProcesses = !neededForLock;
476 try
477 {
478 if (installedOrInstalling)
479 {
480 progressReporter.StageName = "Waiting for existing installation job...";
481
482 if (neededForLock && !installation.InstallationTask.IsCompleted)
483 logger.LogWarning("The required engine version ({version}) is not readily available! We will have to wait for it to install.", version);
484
485 await installation.InstallationTask.WaitAsync(cancellationToken);
486 return installLock;
487 }
488
489 // okay up to us to install it then
490 string? installPath = null;
491 try
492 {
493 if (customVersionStream != null)
494 logger.LogInformation("Installing custom engine version as {version}...", version);
495 else if (neededForLock)
496 {
497 if (version.CustomIteration.HasValue)
498 throw new JobException(ErrorCode.EngineNonExistentCustomVersion);
499
500 logger.LogWarning("The required engine version ({version}) is not readily available! We will have to install it.", version);
501 }
502 else
503 logger.LogInformation("Requested engine version {version} not currently installed. Doing so now...", version);
504
505 progressReporter.StageName = "Running event";
506
507 var versionString = version.ToString();
508 await eventConsumer.HandleEvent(EventType.EngineInstallStart, new List<string> { versionString }, false, deploymentPipelineProcesses, cancellationToken);
509
510 installPath = await InstallVersionFiles(progressReporter, version, customVersionStream, deploymentPipelineProcesses, cancellationToken);
511 await eventConsumer.HandleEvent(EventType.EngineInstallComplete, new List<string> { versionString }, false, deploymentPipelineProcesses, cancellationToken);
512
513 ourTcs.SetResult();
514 }
515 catch (Exception ex)
516 {
517 if (installPath != null)
518 {
519 try
520 {
521 logger.LogDebug("Cleaning up failed installation at {path}...", installPath);
522 await ioManager.DeleteDirectory(installPath, cancellationToken);
523 }
524 catch (Exception ex2)
525 {
526 logger.LogError(ex2, "Error cleaning up failed installation!");
527 }
528 }
529 else if (ex is not OperationCanceledException)
530 await eventConsumer.HandleEvent(EventType.EngineInstallFail, new List<string> { ex.Message }, false, deploymentPipelineProcesses, cancellationToken);
531
532 lock (installedVersions)
533 installedVersions.Remove(version);
534
535 ourTcs.SetException(ex);
536 throw;
537 }
538
539 return installLock;
540 }
541 catch
542 {
543 installLock.Dispose();
544 throw;
545 }
546 }
547 }
548
558 async ValueTask<string> InstallVersionFiles(
559 JobProgressReporter progressReporter,
560 EngineVersion version,
561 Stream? customVersionStream,
562 bool deploymentPipelineProcesses,
563 CancellationToken cancellationToken)
564 {
565 var installFullPath = ioManager.ResolvePath(version.ToString());
566 async ValueTask DirectoryCleanup()
567 {
568 await ioManager.DeleteDirectory(installFullPath, cancellationToken);
569 await ioManager.CreateDirectory(installFullPath, cancellationToken);
570 }
571
572 var directoryCleanupTask = DirectoryCleanup();
573 try
574 {
575 IEngineInstallationData engineInstallationData;
576 var remainingProgress = 1.0;
577 if (customVersionStream == null)
578 {
579 using var subReporter = progressReporter.CreateSection("Downloading Version", 0.5);
580 remainingProgress -= 0.5;
581 engineInstallationData = await engineInstaller.DownloadVersion(version, subReporter, cancellationToken);
582 }
583 else
584#pragma warning disable CA2000 // Dispose objects before losing scope, false positive
585 engineInstallationData = new ZipStreamEngineInstallationData(
586 ioManager,
587 customVersionStream);
588#pragma warning restore CA2000 // Dispose objects before losing scope
589
590 JobProgressReporter remainingReporter;
591 try
592 {
593 remainingReporter = progressReporter.CreateSection(null, remainingProgress);
594 }
595 catch
596 {
597 await engineInstallationData.DisposeAsync();
598 throw;
599 }
600
601 using (remainingReporter)
602 {
603 await using (engineInstallationData)
604 {
605 remainingReporter.StageName = "Cleaning target directory";
606
607 await directoryCleanupTask;
608 remainingReporter.ReportProgress(0.1);
609 remainingReporter.StageName = "Extracting data";
610
611 logger.LogTrace("Extracting engine to {extractPath}...", installFullPath);
612 await engineInstallationData.ExtractToPath(installFullPath, cancellationToken);
613 remainingReporter.ReportProgress(0.3);
614 }
615
616 remainingReporter.StageName = "Running installation actions";
617
618 var installation = await engineInstaller.Install(version, installFullPath, deploymentPipelineProcesses, cancellationToken);
619
620 // some minor validation
621 var serverInstallTask = ioManager.FileExists(installation.ServerExePath, cancellationToken);
622 if (!await ioManager.FileExists(installation.CompilerExePath, cancellationToken))
623 {
624 logger.LogError("Compiler executable does not exist after engine installation!");
625 throw new JobException(ErrorCode.EngineDownloadFail);
626 }
627
628 if (!await serverInstallTask)
629 {
630 logger.LogError("Server executable does not exist after engine installation!");
631 throw new JobException(ErrorCode.EngineDownloadFail);
632 }
633
634 remainingReporter.ReportProgress(0.9);
635 remainingReporter.StageName = "Writing version file";
636
637 // make sure to do this last because this is what tells us we have a valid version in the future
639 ioManager.ConcatPath(installFullPath, VersionFileName),
640 Encoding.UTF8.GetBytes(version.ToString()),
641 cancellationToken);
642 }
643 }
644 catch (HttpRequestException ex)
645 {
646 // since the user can easily provide non-exitent version numbers, we'll turn this into a JobException
647 throw new JobException(ErrorCode.EngineDownloadFail, ex);
648 }
649 catch (OperationCanceledException)
650 {
651 throw;
652 }
653 catch
654 {
655 await ioManager.DeleteDirectory(installFullPath, cancellationToken);
656 throw;
657 }
658
659 return installFullPath;
660 }
661
668 {
669 var installationContainer = new ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock>(installation);
670
671 lock (installedVersions)
672 installedVersions.Add(installation.Version, installationContainer);
673
674 return installationContainer;
675 }
676 }
677}
Information about an engine installation.
static bool TryParse(string input, out EngineVersion? engineVersion)
Attempts to parse a stringified EngineVersion.
int? CustomIteration
The revision of the custom build.
Extension methods for the ValueTask and ValueTask<TResult> classes.
static async ValueTask WhenAll(IEnumerable< ValueTask > tasks)
Fully await a given list of tasks .
static void CheckVersionParameter(EngineVersion version)
Validates a given version parameter.
readonly Dictionary< EngineVersion, ReferenceCountingContainer< IEngineInstallation, EngineExecutableLock > > installedVersions
Map of byond EngineVersions to Tasks that complete when they are installed.
Task StopAsync(CancellationToken cancellationToken)
const string VersionFileName
The file in which we store the Version for installations.
async Task StartAsync(CancellationToken cancellationToken)
IReadOnlyList< EngineVersion > InstalledVersions
The installed EngineVersions.
async ValueTask< EngineExecutableLock > AssertAndLockVersion(JobProgressReporter progressReporter, EngineVersion version, Stream? customVersionStream, bool neededForLock, bool allowInstallation, CancellationToken cancellationToken)
Ensures a BYOND version is installed if it isn't already.
volatile TaskCompletionSource activeVersionChanged
TaskCompletionSource that notifes when the ActiveVersion changes.
const string ActiveVersionFileName
The file in which we store the ActiveVersion.
async ValueTask< IEngineExecutableLock > UseExecutables(EngineVersion? requiredVersion, string? trustDmbFullPath, CancellationToken cancellationToken)
Lock the current installation's location and return a IEngineExecutableLock.A ValueTask<TResult> resu...
async ValueTask DeleteVersion(JobProgressReporter progressReporter, EngineVersion version, CancellationToken cancellationToken)
Deletes a given version from the disk.A Task representing the running operation.
EngineVersion? ActiveVersion
The currently active EngineVersion.
readonly SemaphoreSlim changeDeleteSemaphore
The SemaphoreSlim for changing or deleting the active BYOND version.
async ValueTask ChangeVersion(JobProgressReporter progressReporter, EngineVersion version, Stream? customVersionStream, bool allowInstallation, CancellationToken cancellationToken)
Change the active EngineVersion.A ValueTask representing the running operation.
readonly IEventConsumer eventConsumer
The IEventConsumer for the EngineManager.
readonly IIOManager ioManager
The IIOManager for the EngineManager.
async ValueTask< string > InstallVersionFiles(JobProgressReporter progressReporter, EngineVersion version, Stream? customVersionStream, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
Installs the files for a given BYOND version .
ReferenceCountingContainer< IEngineInstallation, EngineExecutableLock > AddInstallationContainer(IEngineInstallation installation)
Create and add a new IEngineInstallation to installedVersions.
readonly IEngineInstaller engineInstaller
The IEngineInstaller for the EngineManager.
EngineManager(IIOManager ioManager, IEngineInstaller engineInstaller, IEventConsumer eventConsumer, IDmbFactory dmbFactory, ILogger< EngineManager > logger)
Initializes a new instance of the EngineManager class.
readonly ILogger< EngineManager > logger
The ILogger for the EngineManager.
readonly IDmbFactory dmbFactory
The IDmbFactory for the EngineManager.
Implementation of IEngineInstallationData for a zip file in a Stream.
IIOManager that resolves paths to Environment.CurrentDirectory.
const string CurrentDirectory
Path to the current working directory for the IIOManager.
Operation exceptions thrown from the context of a Models.Job.
JobProgressReporter CreateSection(string? newStageName, double percentage)
Create a subsection of the JobProgressReporter with its optional own stage name.
void ReportProgress(double? progress)
Report progress.
TReference AddReference()
Create a new TReference to the Instance.
Task OnZeroReferences
A Task that completes when there are no TReference s active for the Instance.
static async ValueTask< SemaphoreSlimContext > Lock(SemaphoreSlim semaphore, CancellationToken cancellationToken, ILogger? logger=null)
Asyncronously locks a semaphore .
void LogLockStates()
Log the states of all active IDmbProviders.
ValueTask ExtractToPath(string path, CancellationToken cancellationToken)
Extracts the installation to a given path.
Task InstallationTask
The Task that completes when the BYOND version finished installing.
EngineVersion Version
The EngineVersion of the IEngineInstallation.
For downloading and installing game engines for a given system.
ValueTask< IEngineInstallationData > DownloadVersion(EngineVersion version, JobProgressReporter jobProgressReporter, CancellationToken cancellationToken)
Download a given engine version .
ValueTask< IEngineInstallation > Install(EngineVersion version, string path, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
Does actions necessary to get an extracted installation working.
ValueTask UpgradeInstallation(EngineVersion version, string path, CancellationToken cancellationToken)
Does actions necessary to get upgrade a version installed by a previous version of TGS.
ValueTask< IEngineInstallation > GetInstallation(EngineVersion version, string path, Task installationTask, CancellationToken cancellationToken)
Creates an IEngineInstallation for a given version .
ValueTask TrustDmbPath(EngineVersion version, string fullDmbPath, CancellationToken cancellationToken)
Add a given fullDmbPath to the trusted DMBs list in BYOND's config.
Consumes EventTypes and takes the appropriate actions.
ValueTask HandleEvent(EventType eventType, IEnumerable< string?> parameters, bool sensitiveParameters, bool deploymentPipeline, CancellationToken cancellationToken)
Handle a given eventType .
Interface for using filesystems.
Definition IIOManager.cs:14
string ResolvePath()
Retrieve the full path of the current working directory.
ValueTask< byte[]> ReadAllBytes(string path, CancellationToken cancellationToken)
Returns all the contents of a file at path as a byte array.
string ConcatPath(params string[] paths)
Combines an array of strings into a path.
Task< IReadOnlyList< string > > GetDirectories(string path, CancellationToken cancellationToken)
Returns full directory names in a given path .
Task CreateDirectory(string path, CancellationToken cancellationToken)
Create a directory at path .
Task DeleteFile(string path, CancellationToken cancellationToken)
Deletes a file at path .
ValueTask WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken)
Writes some contents to a file at path overwriting previous content.
Task DeleteDirectory(string path, CancellationToken cancellationToken)
Recursively delete a directory, removes and does not enter any symlinks encounterd.
Task< bool > FileExists(string path, CancellationToken cancellationToken)
Check that the file at path exists.
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
Definition ErrorCode.cs:12
EventType
Types of events. Mirror in tgs.dm. Prefer last listed name for script.
Definition EventType.cs:7