tgstation-server 6.8.0
The /tg/station 13 server suite
Loading...
Searching...
No Matches
InstanceManager.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Threading;
5using System.Threading.Tasks;
6
7using Microsoft.EntityFrameworkCore;
8using Microsoft.Extensions.Hosting;
9using Microsoft.Extensions.Logging;
10using Microsoft.Extensions.Options;
11
29
31{
33 sealed class InstanceManager :
39 {
41 public Task Ready => readyTcs.Task;
42
47
52
57
62
67
72
77
82
87
92
96 readonly IConsole console;
97
102
106 readonly ILogger<InstanceManager> logger;
107
111 readonly Dictionary<long, ReferenceCountingContainer<IInstance, InstanceWrapper>> instances;
112
116 readonly Dictionary<string, IBridgeHandler> bridgeHandlers;
117
121 readonly SemaphoreSlim instanceStateChangeSemaphore;
122
127
132
136 readonly TaskCompletionSource readyTcs;
137
141 readonly CancellationTokenSource startupCancellationTokenSource;
142
146 readonly CancellationTokenSource shutdownCancellationTokenSource;
147
151 readonly string? originalConsoleTitle;
152
157
162
194 IOptions<GeneralConfiguration> generalConfigurationOptions,
195 IOptions<SwarmConfiguration> swarmConfigurationOptions,
196 ILogger<InstanceManager> logger)
197 {
198 this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
199 this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
200 this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
201 this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
202 this.jobService = jobService ?? throw new ArgumentNullException(nameof(jobService));
203 this.serverControl = serverControl ?? throw new ArgumentNullException(nameof(serverControl));
204 this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
205 this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
206 this.serverPortProvider = serverPortProvider ?? throw new ArgumentNullException(nameof(serverPortProvider));
207 this.swarmServiceController = swarmServiceController ?? throw new ArgumentNullException(nameof(swarmServiceController));
208 this.console = console ?? throw new ArgumentNullException(nameof(console));
209 this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
210 generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
211 swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
212 this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
213
215
216 instances = new Dictionary<long, ReferenceCountingContainer<IInstance, InstanceWrapper>>();
217 bridgeHandlers = new Dictionary<string, IBridgeHandler>();
218 readyTcs = new TaskCompletionSource();
219 instanceStateChangeSemaphore = new SemaphoreSlim(1);
220 startupCancellationTokenSource = new CancellationTokenSource();
221 shutdownCancellationTokenSource = new CancellationTokenSource();
222 }
223
225 public async ValueTask DisposeAsync()
226 {
227 lock (instances)
228 {
229 if (disposed)
230 return;
231 disposed = true;
232 }
233
234 foreach (var instanceKvp in instances)
235 await instanceKvp.Value.Instance.DisposeAsync();
236
240
241 logger.LogInformation("Server shutdown");
242 }
243
245 public IInstanceReference? GetInstanceReference(Api.Models.Instance metadata)
246 {
247 ArgumentNullException.ThrowIfNull(metadata);
248
249 lock (instances)
250 {
251 if (!instances.TryGetValue(metadata.Require(x => x.Id), out var instance))
252 return null;
253
254 return instance.AddReference();
255 }
256 }
257
259 public async ValueTask MoveInstance(Models.Instance instance, string oldPath, CancellationToken cancellationToken)
260 {
261 ArgumentNullException.ThrowIfNull(oldPath);
262
263 using var lockContext = await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken);
264 using var instanceReferenceCheck = GetInstanceReference(instance);
265 if (instanceReferenceCheck != null)
266 throw new InvalidOperationException("Cannot move an online instance!");
267 var newPath = instance.Path!;
268 try
269 {
270 await ioManager.MoveDirectory(oldPath, newPath, cancellationToken);
271
272 // Delete the Game directory to clear out broken symlinks
273 var instanceGameIOManager = instanceFactory.CreateGameIOManager(instance);
274 await instanceGameIOManager.DeleteDirectory(DefaultIOManager.CurrentDirectory, cancellationToken);
275 }
276 catch (Exception ex)
277 {
278 logger.LogError(
279 ex,
280 "Error moving instance {instanceId}!",
281 instance.Id);
282 try
283 {
284 logger.LogDebug("Reverting instance {instanceId}'s path to {oldPath} in the DB...", instance.Id, oldPath);
285
286 // DCT: Operation must always run
288 {
289 var targetInstance = new Models.Instance
290 {
291 Id = instance.Id,
292 };
293 db.Instances.Attach(targetInstance);
294 targetInstance.Path = oldPath;
295 return db.Save(CancellationToken.None);
296 });
297 }
298 catch (Exception innerEx)
299 {
300 logger.LogCritical(
301 innerEx,
302 "Error reverting database after failing to move instance {instanceId}! Attempting to detach...",
303 instance.Id);
304
305 try
306 {
307 // DCT: Operation must always run
310 Array.Empty<byte>(),
311 CancellationToken.None);
312 }
313 catch (Exception tripleEx)
314 {
315 logger.LogCritical(
316 tripleEx,
317 "Okay, what gamma radiation are you under? Failed to write instance attach file!");
318
319 throw new AggregateException(tripleEx, innerEx, ex);
320 }
321
322 throw new AggregateException(ex, innerEx);
323 }
324
325 throw;
326 }
327 }
328
330 public async ValueTask OfflineInstance(Models.Instance metadata, User user, CancellationToken cancellationToken)
331 {
332 ArgumentNullException.ThrowIfNull(metadata);
333
334 using (await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken))
335 {
337 var instanceId = metadata.Require(x => x.Id);
338 lock (instances)
339 {
340 if (!instances.TryGetValue(instanceId, out container))
341 {
342 logger.LogDebug("Not offlining removed instance {instanceId}", metadata.Id);
343 return;
344 }
345
346 instances.Remove(instanceId);
347 }
348
349 logger.LogInformation("Offlining instance ID {instanceId}", metadata.Id);
350
351 try
352 {
353 await container.OnZeroReferences.WaitAsync(cancellationToken);
354
355 // we are the one responsible for cancelling his jobs
356 ValueTask<Job?[]> groupedTask = default;
358 async db =>
359 {
360 var jobs = await db
361 .Jobs
362 .AsQueryable()
363 .Where(x => x.Instance!.Id == metadata.Id && !x.StoppedAt.HasValue)
364 .Select(x => new Job(x.Id!.Value))
365 .ToListAsync(cancellationToken);
366
367 groupedTask = ValueTaskExtensions.WhenAll(
368 jobs.Select(job => jobService.CancelJob(job, user, true, cancellationToken)),
369 jobs.Count);
370 });
371
372 await groupedTask;
373 }
374 catch
375 {
376 // not too late to change your mind
377 lock (instances)
378 instances.Add(instanceId, container);
379
380 throw;
381 }
382
383 try
384 {
385 // at this point we can't really stop offlining the instance just because the request was cancelled
386 await container.Instance.StopAsync(shutdownCancellationTokenSource.Token);
387 }
388 finally
389 {
390 await container.Instance.DisposeAsync();
391 }
392 }
393 }
394
396 public async ValueTask OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
397 {
398 ArgumentNullException.ThrowIfNull(metadata);
399
400 var instanceId = metadata.Require(x => x.Id);
401 using var lockContext = await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken);
402 lock (instances)
403 if (instances.ContainsKey(instanceId))
404 {
405 logger.LogDebug("Aborting instance creation due to it seemingly already being online");
406 return;
407 }
408
409 logger.LogInformation("Onlining instance ID {instanceId} ({instanceName}) at {instancePath}", metadata.Id, metadata.Name, metadata.Path);
410 var instance = await instanceFactory.CreateInstance(this, metadata);
411 try
412 {
413 await instance.StartAsync(cancellationToken);
414
415 try
416 {
417 lock (instances)
418 instances.Add(
419 instanceId,
421 }
422 catch (Exception ex)
423 {
424 logger.LogError("Unable to commit onlined instance {instanceId} into service, offlining!", metadata.Id);
425 try
426 {
427 // DCT: Must always run
428 await instance.StopAsync(CancellationToken.None);
429 }
430 catch (Exception innerEx)
431 {
432 throw new AggregateException(innerEx, ex);
433 }
434
435 throw;
436 }
437 }
438 catch
439 {
440 await instance.DisposeAsync();
441 throw;
442 }
443 }
444
446 public Task StartAsync(CancellationToken cancellationToken)
447 {
448 cancellationToken.Register(startupCancellationTokenSource.Cancel);
450 return Task.CompletedTask;
451 }
452
454 public async Task StopAsync(CancellationToken cancellationToken)
455 {
456 try
457 {
458 using (cancellationToken.Register(shutdownCancellationTokenSource.Cancel))
459 try
460 {
461 if (startupTask == null)
462 {
463 logger.LogWarning("InstanceManager was never started!");
464 return;
465 }
466
467 logger.LogDebug("Stopping instance manager...");
468
469 if (!startupTask.IsCompleted)
470 {
471 logger.LogTrace("Interrupting startup task...");
473 await startupTask;
474 }
475
476 var instanceFactoryStopTask = instanceFactory.StopAsync(cancellationToken);
477 await jobService.StopAsync(cancellationToken);
478
479 async ValueTask OfflineInstanceImmediate(IInstance instance, CancellationToken cancellationToken)
480 {
481 try
482 {
483 await instance.StopAsync(cancellationToken);
484 }
485 catch (Exception ex)
486 {
487 logger.LogError(ex, "Instance shutdown exception!");
488 }
489 }
490
491 await ValueTaskExtensions.WhenAll(instances.Select(x => OfflineInstanceImmediate(x.Value.Instance, cancellationToken)));
492 await instanceFactoryStopTask;
493
494 await swarmServiceController.Shutdown(cancellationToken);
495 }
496 finally
497 {
498 if (originalConsoleTitle != null)
500 }
501 }
502 catch (Exception ex)
503 {
504 logger.LogCritical(ex, "Instance manager stop exception!");
505 }
506 }
507
509 public async ValueTask<BridgeResponse?> ProcessBridgeRequest(BridgeParameters parameters, CancellationToken cancellationToken)
510 {
511 ArgumentNullException.ThrowIfNull(parameters);
512
513 var accessIdentifier = parameters.AccessIdentifier;
514 if (accessIdentifier == null)
515 {
516 logger.LogWarning("Received invalid bridge request with null access identifier!");
517 return null;
518 }
519
520 IBridgeHandler? bridgeHandler = null;
521 var loggedDelay = false;
522 for (var i = 0; bridgeHandler == null && i < 30; ++i)
523 {
524 // There's a miniscule time period where we could potentially receive a bridge request and not have the registration ready when we launch DD
525 // This is a stopgap
526 Task delayTask = Task.CompletedTask;
527 lock (bridgeHandlers)
528 if (!bridgeHandlers.TryGetValue(accessIdentifier, out bridgeHandler))
529 {
530 if (!loggedDelay)
531 {
532 logger.LogTrace("Received bridge request with unregistered access identifier \"{aid}\". Waiting up to 3 seconds for it to be registered...", accessIdentifier);
533 loggedDelay = true;
534 }
535
536 delayTask = asyncDelayer.Delay(TimeSpan.FromMilliseconds(100), cancellationToken);
537 }
538
539 await delayTask;
540 }
541
542 if (bridgeHandler == null)
543 lock (bridgeHandlers)
544 if (!bridgeHandlers.TryGetValue(accessIdentifier, out bridgeHandler))
545 {
546 logger.LogWarning("Received invalid bridge request with access identifier: {accessIdentifier}", accessIdentifier);
547 return null;
548 }
549
550 return await bridgeHandler.ProcessBridgeRequest(parameters, cancellationToken);
551 }
552
555 {
556 ArgumentNullException.ThrowIfNull(bridgeHandler);
557
558 var accessIdentifier = bridgeHandler.DMApiParameters.AccessIdentifier
559 ?? throw new InvalidOperationException("Attempted bridge registration with null AccessIdentifier!");
560 lock (bridgeHandlers)
561 {
562 bridgeHandlers.Add(accessIdentifier, bridgeHandler);
563 logger.LogTrace("Registered bridge handler: {accessIdentifier}", accessIdentifier);
564 }
565
566 return new BridgeRegistration(() =>
567 {
568 lock (bridgeHandlers)
569 {
570 bridgeHandlers.Remove(accessIdentifier);
571 logger.LogTrace("Unregistered bridge handler: {accessIdentifier}", accessIdentifier);
572 }
573 });
574 }
575
577 public IInstanceCore? GetInstance(Models.Instance metadata)
578 {
579 lock (instances)
580 {
581 instances.TryGetValue(metadata.Require(x => x.Id), out var container);
582 return container?.Instance;
583 }
584 }
585
591 async Task Initialize(CancellationToken cancellationToken)
592 {
593 try
594 {
595 logger.LogInformation("{versionString}", assemblyInformationProvider.VersionString);
597
599
600 // To let the web server startup immediately before we do any intense work
601 await Task.Yield();
602
603 await InitializeSwarm(cancellationToken);
604
605 List<Models.Instance>? dbInstances = null;
606
607 async ValueTask EnumerateInstances(IDatabaseContext databaseContext)
608 => dbInstances = await databaseContext
609 .Instances
610 .AsQueryable()
611 .Where(x => x.Online!.Value && x.SwarmIdentifer == swarmConfiguration.Identifier)
612 .Include(x => x.RepositorySettings)
613 .Include(x => x.ChatSettings)
614 .ThenInclude(x => x.Channels)
615 .Include(x => x.DreamDaemonSettings)
616 .ToListAsync(cancellationToken);
617
618 var instanceEnumeration = databaseContextFactory.UseContext(EnumerateInstances);
619
620 var factoryStartup = instanceFactory.StartAsync(cancellationToken);
621 var jobManagerStartup = jobService.StartAsync(cancellationToken);
622
623 await Task.WhenAll(instanceEnumeration.AsTask(), factoryStartup, jobManagerStartup);
624
625 var instanceOnliningTasks = dbInstances!.Select(
626 async metadata =>
627 {
628 try
629 {
630 await OnlineInstance(metadata, cancellationToken);
631 }
632 catch (Exception ex)
633 {
634 logger.LogError(ex, "Failed to online instance {instanceId}!", metadata.Id);
635 }
636 });
637
638 await Task.WhenAll(instanceOnliningTasks);
639
640 logger.LogInformation("Server ready!");
641 readyTcs.SetResult();
642
643 // this needs to happen after the HTTP API opens with readyTcs otherwise it can race and cause failed bridge requests with 503's
644 jobService.Activate(this);
645 }
646 catch (OperationCanceledException ex)
647 {
648 logger.LogInformation(ex, "Cancelled instance manager initialization!");
649 }
650 catch (Exception e)
651 {
652 logger.LogCritical(e, "Instance manager startup error!");
653 try
654 {
655 await serverControl.Die(e);
656 return;
657 }
658 catch (Exception e2)
659 {
660 logger.LogCritical(e2, "Failed to kill server!");
661 }
662 }
663 }
664
669 {
670 logger.LogDebug("Running as user: {username}", Environment.UserName);
671
673
674 using (var systemIdentity = systemIdentityFactory.GetCurrent())
675 {
676 if (!systemIdentity.CanCreateSymlinks)
677 throw new InvalidOperationException($"The user running {Constants.CanonicalPackageName} cannot create symlinks! Please try running as an administrative user!");
678 }
679
680 // This runs before the real socket is opened, ensures we don't perform reattaches unless we're fairly certain the bind won't fail
681 // If it does fail, DD will be killed.
683 }
684
690 async ValueTask InitializeSwarm(CancellationToken cancellationToken)
691 {
692 SwarmRegistrationResult registrationResult;
693 do
694 {
695 registrationResult = await swarmServiceController.Initialize(cancellationToken);
696
697 if (registrationResult == SwarmRegistrationResult.Unauthorized)
698 throw new InvalidOperationException("Swarm private key does not match the swarm controller's!");
699
700 if (registrationResult == SwarmRegistrationResult.VersionMismatch)
701 throw new InvalidOperationException("Swarm controller's TGS version does not match our own!");
702
703 if (registrationResult != SwarmRegistrationResult.Success)
704 await asyncDelayer.Delay(TimeSpan.FromSeconds(5), cancellationToken);
705 }
706 while (registrationResult != SwarmRegistrationResult.Success && !cancellationToken.IsCancellationRequested);
707 }
708 }
709}
virtual ? long Id
The ID of the entity.
Definition: EntityId.cs:13
string? Identifier
The server's identifier.
Definition: SwarmServer.cs:26
Extension methods for the ValueTask and ValueTask<TResult> classes.
static async ValueTask WhenAll(IEnumerable< ValueTask > tasks)
Fully await a given list of tasks .
Task Ready
Task that completes when the IInstanceManager finishes initializing.
readonly TaskCompletionSource readyTcs
The TaskCompletionSource for Ready.
readonly Dictionary< long, ReferenceCountingContainer< IInstance, InstanceWrapper > > instances
Map of instance EntityId.Ids to the respective ReferenceCountingContainer<TWrapped,...
readonly IPlatformIdentifier platformIdentifier
The IPlatformIdentifier for the InstanceManager.
void CheckSystemCompatibility()
Check we have a valid system and configuration.
readonly IJobService jobService
The IJobService for the InstanceManager.
async ValueTask OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
Online an IInstance. A ValueTask representing the running operation.
InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IAssemblyInformationProvider assemblyInformationProvider, IJobService jobService, IServerControl serverControl, ISystemIdentityFactory systemIdentityFactory, IAsyncDelayer asyncDelayer, IServerPortProvider serverPortProvider, ISwarmServiceController swarmServiceController, IConsole console, IPlatformIdentifier platformIdentifier, IOptions< GeneralConfiguration > generalConfigurationOptions, IOptions< SwarmConfiguration > swarmConfigurationOptions, ILogger< InstanceManager > logger)
Initializes a new instance of the InstanceManager class.
readonly IIOManager ioManager
The IIOManager for the InstanceManager.
bool disposed
If the InstanceManager has been DisposeAsync'd.
readonly ISwarmServiceController swarmServiceController
The ISwarmServiceController for the InstanceManager.
readonly CancellationTokenSource startupCancellationTokenSource
The CancellationTokenSource for Initialize(CancellationToken).
IBridgeRegistration RegisterHandler(IBridgeHandler bridgeHandler)
Register a given bridgeHandler . A representative IBridgeRegistration.
Task? startupTask
The Task returned by Initialize(CancellationToken).
readonly ISystemIdentityFactory systemIdentityFactory
The ISystemIdentityFactory for the InstanceManager.
readonly ILogger< InstanceManager > logger
The ILogger for the InstanceManager.
readonly SwarmConfiguration swarmConfiguration
The SwarmConfiguration for the InstanceManager.
Task StartAsync(CancellationToken cancellationToken)
readonly IAssemblyInformationProvider assemblyInformationProvider
The IAssemblyInformationProvider for the InstanceManager.
async Task Initialize(CancellationToken cancellationToken)
Initializes the InstanceManager.
async Task StopAsync(CancellationToken cancellationToken)
readonly GeneralConfiguration generalConfiguration
The GeneralConfiguration for the InstanceManager.
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the InstanceManager.
readonly SemaphoreSlim instanceStateChangeSemaphore
SemaphoreSlim used to guard calls to OnlineInstance(Models.Instance, CancellationToken) and OfflineIn...
readonly IServerControl serverControl
The IServerControl for the InstanceManager.
IInstanceReference? GetInstanceReference(Api.Models.Instance metadata)
Get the IInstanceReference associated with given metadata . The IInstance associated with the given m...
async ValueTask MoveInstance(Models.Instance instance, string oldPath, CancellationToken cancellationToken)
Move an IInstance. A ValueTask representing the running operation.
async ValueTask< BridgeResponse?> ProcessBridgeRequest(BridgeParameters parameters, CancellationToken cancellationToken)
Handle a set of bridge parameters . A ValueTask<TResult> resulting in the BridgeResponse for the requ...
readonly IServerPortProvider serverPortProvider
The IServerPortProvider for the InstanceManager.
readonly IConsole console
The IConsole for the InstanceManager.
readonly? string originalConsoleTitle
The original IConsole.Title of console.
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the InstanceManager.
IInstanceCore? GetInstance(Models.Instance metadata)
Get the IInstanceCore for a given instance if it's online. The IInstanceCore if it is online,...
readonly IInstanceFactory instanceFactory
The IInstanceFactory for the InstanceManager.
async ValueTask InitializeSwarm(CancellationToken cancellationToken)
Initializes the connection to the TGS swarm.
readonly CancellationTokenSource shutdownCancellationTokenSource
The CancellationTokenSource linked with the token given to StopAsync(CancellationToken).
async ValueTask OfflineInstance(Models.Instance metadata, User user, CancellationToken cancellationToken)
Offline an IInstance. A ValueTask representing the running operation.
readonly Dictionary< string, IBridgeHandler > bridgeHandlers
Map of DMApiParameters.AccessIdentifiers to their respective IBridgeHandlers.
string AccessIdentifier
Used to identify and authenticate the DreamDaemon instance.
void CheckCompatibility(ILogger logger)
Validates the current ConfigVersion's compatibility and provides migration instructions.
Configuration for the server swarm system.
ApiController for managing Components.Instances.
const string InstanceAttachFileName
File name to allow attaching instances.
Extension methods for the Socket class.
static void BindTest(IPlatformIdentifier platformIdentifier, ushort port, bool includeIPv6, bool udp)
Attempt to exclusively bind to a given port .
IIOManager that resolves paths to Environment.CurrentDirectory.
const string CurrentDirectory
Path to the current working directory for the IIOManager.
Represents an Api.Models.Instance in the database.
Definition: Instance.cs:11
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)
Asyncronously locks a semaphore .
For interacting with the instance services.
IIOManager CreateGameIOManager(Models.Instance metadata)
Create an IIOManager that resolves to the "Game" directory of the Models.Instance defined by metadata...
ValueTask< IInstance > CreateInstance(IBridgeRegistrar bridgeRegistrar, Models.Instance metadata)
Create an IInstance.
Component version of IInstanceCore.
Definition: IInstance.cs:9
ValueTask< BridgeResponse?> ProcessBridgeRequest(BridgeParameters parameters, CancellationToken cancellationToken)
Handle a set of bridge parameters .
Represents a service that may take an updated Host assembly and run it, stopping the current assembly...
ValueTask Die(Exception? exception)
Kill the server with a fatal exception.
Provides access to the server's HttpApiPort.
ushort HttpApiPort
The port the server listens on.
Factory for scoping usage of IDatabaseContexts. Meant for use by Components.
ValueTask UseContextTaskReturn(Func< IDatabaseContext, Task > operation)
Run an operation in the scope of an IDatabaseContext.
ValueTask UseContext(Func< IDatabaseContext, ValueTask > operation)
Run an operation in the scope of an IDatabaseContext.
IDatabaseCollection< Instance > Instances
The Instances in the IDatabaseContext.
Abstraction for global::System.Console.
Definition: IConsole.cs:10
string? Title
Gets or sets the IConsole window's title. Can return null if getting the console title is not support...
Definition: IConsole.cs:14
void SetTitle(string newTitle)
Sets a newTitle console window.
Interface for using filesystems.
Definition: IIOManager.cs:13
string ConcatPath(params string[] paths)
Combines an array of strings into a 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 MoveDirectory(string source, string destination, CancellationToken cancellationToken)
Moves a directory at source to destination .
ValueTask< Job?> CancelJob(Job job, User? user, bool blocking, CancellationToken cancellationToken)
Cancels a give job .
The service that manages everything to do with jobs.
Definition: IJobService.cs:9
void Activate(IInstanceCoreProvider instanceCoreProvider)
Activate the IJobManager.
ISystemIdentity GetCurrent()
Retrieves a ISystemIdentity representing the user executing tgstation-server.
Start and stop controllers for a swarm service.
ValueTask Shutdown(CancellationToken cancellationToken)
Deregister with the swarm controller or put clients into querying state.
ValueTask< SwarmRegistrationResult > Initialize(CancellationToken cancellationToken)
Attempt to register with the swarm controller if not one, sets up the database otherwise.
For identifying the current platform.
Task Delay(TimeSpan timeSpan, CancellationToken cancellationToken)
Create a Task that completes after a given timeSpan .
SwarmRegistrationResult
Result of attempting to register with a swarm controller.