tgstation-server 6.19.0
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 cancellationToken);
166
167 ActiveVersion = version;
168
169 logger.LogInformation("Active version changed to {version}", version);
170 var oldTcs = Interlocked.Exchange(ref activeVersionChanged, new TaskCompletionSource());
171 oldTcs.SetResult();
172 }
173 }
174
176 public async ValueTask<IEngineExecutableLock> UseExecutables(EngineVersion? requiredVersion, string? trustDmbFullPath, CancellationToken cancellationToken)
177 {
178 logger.LogTrace(
179 "Acquiring lock on BYOND version {version}...",
180 requiredVersion?.ToString() ?? $"{ActiveVersion} (active)");
181 var versionToUse = requiredVersion ?? ActiveVersion ?? throw new JobException(ErrorCode.EngineNoVersionsInstalled);
182
183 using var progressReporter = new JobProgressReporter();
184
185 var installLock = await AssertAndLockVersion(
186 progressReporter,
187 versionToUse,
188 null,
189 requiredVersion != null,
190 true,
191 cancellationToken);
192 try
193 {
194 if (trustDmbFullPath != null)
195 await engineInstaller.TrustDmbPath(installLock.Version, trustDmbFullPath, cancellationToken);
196
197 return installLock;
198 }
199 catch
200 {
201 installLock.Dispose();
202 throw;
203 }
204 }
205
207 public async ValueTask DeleteVersion(JobProgressReporter progressReporter, EngineVersion version, CancellationToken cancellationToken)
208 {
209 ArgumentNullException.ThrowIfNull(progressReporter);
210
211 CheckVersionParameter(version);
212
213 logger.LogTrace("DeleteVersion {version}", version);
214
215 var activeVersion = ActiveVersion;
216 if (activeVersion != null && version.Equals(activeVersion))
217 throw new JobException(ErrorCode.EngineCannotDeleteActiveVersion);
218
220 logger.LogTrace("Waiting to acquire installedVersions lock...");
221 lock (installedVersions)
222 {
223 if (!installedVersions.TryGetValue(version, out var containerNullable))
224 {
225 logger.LogTrace("Version {version} already deleted.", version);
226 return;
227 }
228
229 container = containerNullable;
230 logger.LogTrace("Installation container acquired for deletion");
231 }
232
233 progressReporter.StageName = "Waiting for version to not be in use...";
234 while (true)
235 {
236 var containerTask = container.OnZeroReferences;
237
238 // We also want to check when the active version changes in case we need to fail the job because of that.
239 Task activeVersionUpdate;
240 using (await SemaphoreSlimContext.Lock(changeDeleteSemaphore, cancellationToken))
241 activeVersionUpdate = activeVersionChanged.Task;
242
243 logger.LogTrace("Waiting for container.OnZeroReferences or switch of active version...");
244 if (!containerTask.IsCompleted)
246
247 await Task.WhenAny(
248 containerTask,
249 activeVersionUpdate)
250 .WaitAsync(cancellationToken);
251
252 if (containerTask.IsCompleted)
253 logger.LogTrace("All locks for version {version} are gone", version);
254 else
255 logger.LogTrace("activeVersion changed, we may have to wait again. Acquiring semaphore...");
256
257 using (await SemaphoreSlimContext.Lock(changeDeleteSemaphore, cancellationToken))
258 {
259 // check again because it could have become the active version.
260 activeVersion = ActiveVersion;
261 if (activeVersion != null && version.Equals(activeVersion))
262 throw new JobException(ErrorCode.EngineCannotDeleteActiveVersion);
263
264 bool proceed;
265 logger.LogTrace("Locking installedVersions...");
266 lock (installedVersions)
267 {
268 proceed = container.OnZeroReferences.IsCompleted;
269 if (proceed)
270 if (!installedVersions.TryGetValue(version, out var newerContainer))
271 logger.LogWarning("Unable to remove engine installation {version} from list! Is there a duplicate job running?", version);
272 else
273 {
274 if (container != newerContainer)
275 {
276 // 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?
277 // I know realistically this is practically impossible, but god damn that small possiblility
278 // best thing to do is check we exclusively own the newer container
279 logger.LogDebug("Extreme race condition encountered, applying concentrated copium...");
280 container = newerContainer;
281 proceed = container.OnZeroReferences.IsCompleted;
282 }
283
284 if (proceed)
285 {
286 logger.LogTrace("Proceeding with installation deletion...");
287 installedVersions.Remove(version);
288 }
289 }
290 }
291
292 if (proceed)
293 {
294 logger.LogInformation("Deleting version {version}...", version);
295 progressReporter.StageName = "Deleting installation...";
296
297 // 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
298 var installPath = version.ToString();
299 await ioManager.DeleteFile(
301 cancellationToken);
302 await ioManager.DeleteDirectory(installPath, cancellationToken);
303 return;
304 }
305
306 if (containerTask.IsCompleted)
307 logger.LogDebug(
308 "Another lock was acquired before we could remove version {version} from the list. We will have to wait again.",
309 version);
310 else
311 logger.LogTrace("Not proceeding for some reason or another");
312 }
313 }
314 }
315
317 public async Task StartAsync(CancellationToken cancellationToken)
318 {
319 async ValueTask<byte[]?> GetActiveVersion()
320 {
321 var activeVersionFileExists = await ioManager.FileExists(ActiveVersionFileName, cancellationToken);
322 return !activeVersionFileExists ? null : await ioManager.ReadAllBytes(ActiveVersionFileName, cancellationToken);
323 }
324
325 var activeVersionBytesTask = GetActiveVersion();
326
328 var directories = await ioManager.GetDirectories(DefaultIOManager.CurrentDirectory, cancellationToken);
329
330 var installedVersionPaths = new Dictionary<string, EngineVersion>();
331
332 async ValueTask ReadVersion(string path)
333 {
334 var versionFile = ioManager.ConcatPath(path, VersionFileName);
335 if (!await ioManager.FileExists(versionFile, cancellationToken))
336 {
337 logger.LogWarning("Cleaning path with no version file: {versionPath}", ioManager.ResolvePath(path));
338 await ioManager.DeleteDirectory(path, cancellationToken); // cleanup
339 return;
340 }
341
342 var bytes = await ioManager.ReadAllBytes(versionFile, cancellationToken);
343 var text = Encoding.UTF8.GetString(bytes);
344 EngineVersion version;
345 if (!EngineVersion.TryParse(text, out var versionNullable))
346 {
347 logger.LogWarning("Cleaning path with unparsable version file: {versionPath}", ioManager.ResolvePath(path));
348 await ioManager.DeleteDirectory(path, cancellationToken); // cleanup
349 return;
350 }
351 else
352 version = versionNullable!;
353
354 try
355 {
356 var installation = await engineInstaller.GetInstallation(version, path, Task.CompletedTask, cancellationToken);
357 AddInstallationContainer(installation);
358 logger.LogDebug("Added detected BYOND version {versionKey}...", version);
359 }
360 catch (Exception ex)
361 {
362 logger.LogWarning(
363 ex,
364 "It seems that there are multiple directories that say they contain BYOND version {version}. We're ignoring and cleaning the duplicate: {duplicatePath}",
365 version,
366 ioManager.ResolvePath(path));
367 await ioManager.DeleteDirectory(path, cancellationToken);
368 return;
369 }
370
371 lock (installedVersionPaths)
372 installedVersionPaths.Add(ioManager.ResolvePath(version.ToString()), version);
373 }
374
376 directories
377 .Select(ReadVersion));
378
379 logger.LogTrace("Upgrading BYOND installations...");
381 installedVersionPaths
382 .Select(kvp => engineInstaller.UpgradeInstallation(kvp.Value, kvp.Key, cancellationToken)));
383
384 var activeVersionBytes = await activeVersionBytesTask;
385 if (activeVersionBytes != null)
386 {
387 var activeVersionString = Encoding.UTF8.GetString(activeVersionBytes);
388
389 EngineVersion? activeVersion;
390 bool hasRequestedActiveVersion;
391 lock (installedVersions)
392 hasRequestedActiveVersion = EngineVersion.TryParse(activeVersionString, out activeVersion)
393 && installedVersions.ContainsKey(activeVersion!);
394
395 if (hasRequestedActiveVersion)
396 ActiveVersion = activeVersion; // not setting TCS because there's no need during init
397 else
398 {
399 logger.LogWarning("Failed to load saved active version {activeVersion}!", activeVersionString);
400 await ioManager.DeleteFile(ActiveVersionFileName, cancellationToken);
401 }
402 }
403 }
404
406 public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
407
418 async ValueTask<EngineExecutableLock> AssertAndLockVersion(
419 JobProgressReporter progressReporter,
420 EngineVersion version,
421 Stream? customVersionStream,
422 bool neededForLock,
423 bool allowInstallation,
424 CancellationToken cancellationToken)
425 {
426 var ourTcs = new TaskCompletionSource();
427 IEngineInstallation installation;
428 EngineExecutableLock installLock;
429 bool installedOrInstalling;
430
431 // loop is because of the race condition with potentialInstallation, installedVersions, and CustomIteration selection
432 while (true)
433 {
434 lock (installedVersions)
435 {
436 if (customVersionStream != null)
437 {
438 var customInstallationNumber = 1;
439 do
440 {
441 version.CustomIteration = customInstallationNumber++;
442 }
443 while (installedVersions.ContainsKey(version));
444 }
445 }
446
447 var potentialInstallation = await engineInstaller.GetInstallation(
448 version,
449 ioManager.ResolvePath(version.ToString()),
450 ourTcs.Task,
451 cancellationToken);
452
453 lock (installedVersions)
454 {
455 if (customVersionStream != null && installedVersions.ContainsKey(version))
456 continue;
457
458 installedOrInstalling = installedVersions.TryGetValue(version, out var installationContainerNullable);
460 if (!installedOrInstalling)
461 {
462 if (!allowInstallation)
463 throw new InvalidOperationException($"Engine version {version} not installed!");
464
465 installationContainer = AddInstallationContainer(potentialInstallation);
466 }
467 else
468 installationContainer = installationContainerNullable!;
469
470 installation = installationContainer.Instance;
471 installLock = installationContainer.AddReference();
472 }
473
474 var deploymentPipelineProcesses = !neededForLock;
475 try
476 {
477 if (installedOrInstalling)
478 {
479 progressReporter.StageName = "Waiting for existing installation job...";
480
481 if (neededForLock && !installation.InstallationTask.IsCompleted)
482 logger.LogWarning("The required engine version ({version}) is not readily available! We will have to wait for it to install.", version);
483
484 await installation.InstallationTask.WaitAsync(cancellationToken);
485 return installLock;
486 }
487
488 // okay up to us to install it then
489 string? installPath = null;
490 try
491 {
492 if (customVersionStream != null)
493 logger.LogInformation("Installing custom engine version as {version}...", version);
494 else if (neededForLock)
495 {
496 if (version.CustomIteration.HasValue)
497 throw new JobException(ErrorCode.EngineNonExistentCustomVersion);
498
499 logger.LogWarning("The required engine version ({version}) is not readily available! We will have to install it.", version);
500 }
501 else
502 logger.LogInformation("Requested engine version {version} not currently installed. Doing so now...", version);
503
504 progressReporter.StageName = "Running event";
505
506 var versionString = version.ToString();
507 await eventConsumer.HandleEvent(EventType.EngineInstallStart, new List<string> { versionString }, deploymentPipelineProcesses, cancellationToken);
508
509 installPath = await InstallVersionFiles(progressReporter, version, customVersionStream, deploymentPipelineProcesses, cancellationToken);
510 await eventConsumer.HandleEvent(EventType.EngineInstallComplete, new List<string> { versionString }, deploymentPipelineProcesses, cancellationToken);
511
512 ourTcs.SetResult();
513 }
514 catch (Exception ex)
515 {
516 if (installPath != null)
517 {
518 try
519 {
520 logger.LogDebug("Cleaning up failed installation at {path}...", installPath);
521 await ioManager.DeleteDirectory(installPath, cancellationToken);
522 }
523 catch (Exception ex2)
524 {
525 logger.LogError(ex2, "Error cleaning up failed installation!");
526 }
527 }
528 else if (ex is not OperationCanceledException)
529 await eventConsumer.HandleEvent(EventType.EngineInstallFail, new List<string> { ex.Message }, deploymentPipelineProcesses, cancellationToken);
530
531 lock (installedVersions)
532 installedVersions.Remove(version);
533
534 ourTcs.SetException(ex);
535 throw;
536 }
537
538 return installLock;
539 }
540 catch
541 {
542 installLock.Dispose();
543 throw;
544 }
545 }
546 }
547
557 async ValueTask<string> InstallVersionFiles(
558 JobProgressReporter progressReporter,
559 EngineVersion version,
560 Stream? customVersionStream,
561 bool deploymentPipelineProcesses,
562 CancellationToken cancellationToken)
563 {
564 var installFullPath = ioManager.ResolvePath(version.ToString());
565 async ValueTask DirectoryCleanup()
566 {
567 await ioManager.DeleteDirectory(installFullPath, cancellationToken);
568 await ioManager.CreateDirectory(installFullPath, cancellationToken);
569 }
570
571 var directoryCleanupTask = DirectoryCleanup();
572 try
573 {
574 IEngineInstallationData engineInstallationData;
575 var remainingProgress = 1.0;
576 if (customVersionStream == null)
577 {
578 using var subReporter = progressReporter.CreateSection("Downloading Version", 0.5);
579 remainingProgress -= 0.5;
580 engineInstallationData = await engineInstaller.DownloadVersion(version, subReporter, cancellationToken);
581 }
582 else
583#pragma warning disable CA2000 // Dispose objects before losing scope, false positive
584 engineInstallationData = new ZipStreamEngineInstallationData(
585 ioManager,
586 customVersionStream);
587#pragma warning restore CA2000 // Dispose objects before losing scope
588
589 JobProgressReporter remainingReporter;
590 try
591 {
592 remainingReporter = progressReporter.CreateSection(null, remainingProgress);
593 }
594 catch
595 {
596 await engineInstallationData.DisposeAsync();
597 throw;
598 }
599
600 using (remainingReporter)
601 {
602 await using (engineInstallationData)
603 {
604 remainingReporter.StageName = "Cleaning target directory";
605
606 await directoryCleanupTask;
607 remainingReporter.ReportProgress(0.1);
608 remainingReporter.StageName = "Extracting data";
609
610 logger.LogTrace("Extracting engine to {extractPath}...", installFullPath);
611 await engineInstallationData.ExtractToPath(installFullPath, cancellationToken);
612 remainingReporter.ReportProgress(0.3);
613 }
614
615 remainingReporter.StageName = "Running installation actions";
616
617 var installation = await engineInstaller.Install(version, installFullPath, deploymentPipelineProcesses, cancellationToken);
618
619 // some minor validation
620 var serverInstallTask = ioManager.FileExists(installation.ServerExePath, cancellationToken);
621 if (!await ioManager.FileExists(installation.CompilerExePath, cancellationToken))
622 {
623 logger.LogError("Compiler executable does not exist after engine installation!");
624 throw new JobException(ErrorCode.EngineDownloadFail);
625 }
626
627 if (!await serverInstallTask)
628 {
629 logger.LogError("Server executable does not exist after engine installation!");
630 throw new JobException(ErrorCode.EngineDownloadFail);
631 }
632
633 remainingReporter.ReportProgress(0.9);
634 remainingReporter.StageName = "Writing version file";
635
636 // make sure to do this last because this is what tells us we have a valid version in the future
638 ioManager.ConcatPath(installFullPath, VersionFileName),
639 Encoding.UTF8.GetBytes(version.ToString()),
640 cancellationToken);
641 }
642 }
643 catch (HttpRequestException ex)
644 {
645 // since the user can easily provide non-exitent version numbers, we'll turn this into a JobException
646 throw new JobException(ErrorCode.EngineDownloadFail, ex);
647 }
648 catch (OperationCanceledException)
649 {
650 throw;
651 }
652 catch
653 {
654 await ioManager.DeleteDirectory(installFullPath, cancellationToken);
655 throw;
656 }
657
658 return installFullPath;
659 }
660
667 {
668 var installationContainer = new ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock>(installation);
669
670 lock (installedVersions)
671 installedVersions.Add(installation.Version, installationContainer);
672
673 return installationContainer;
674 }
675 }
676}
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 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