tgstation-server 6.12.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
137
141 readonly TaskCompletionSource readyTcs;
142
146 readonly CancellationTokenSource startupCancellationTokenSource;
147
151 readonly CancellationTokenSource shutdownCancellationTokenSource;
152
156 readonly string? originalConsoleTitle;
157
162
167
200 IOptions<GeneralConfiguration> generalConfigurationOptions,
201 IOptions<SwarmConfiguration> swarmConfigurationOptions,
202 IOptions<InternalConfiguration> internalConfigurationOptions,
203 ILogger<InstanceManager> logger)
204 {
205 this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
206 this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
207 this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
208 this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
209 this.jobService = jobService ?? throw new ArgumentNullException(nameof(jobService));
210 this.serverControl = serverControl ?? throw new ArgumentNullException(nameof(serverControl));
211 this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
212 this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
213 this.serverPortProvider = serverPortProvider ?? throw new ArgumentNullException(nameof(serverPortProvider));
214 this.swarmServiceController = swarmServiceController ?? throw new ArgumentNullException(nameof(swarmServiceController));
215 this.console = console ?? throw new ArgumentNullException(nameof(console));
216 this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
217 generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
218 swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
219 internalConfiguration = internalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(internalConfigurationOptions));
220 this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
221
223
224 instances = new Dictionary<long, ReferenceCountingContainer<IInstance, InstanceWrapper>>();
225 bridgeHandlers = new Dictionary<string, IBridgeHandler>();
226 readyTcs = new TaskCompletionSource();
227 instanceStateChangeSemaphore = new SemaphoreSlim(1);
228 startupCancellationTokenSource = new CancellationTokenSource();
229 shutdownCancellationTokenSource = new CancellationTokenSource();
230 }
231
233 public async ValueTask DisposeAsync()
234 {
235 lock (instances)
236 {
237 if (disposed)
238 return;
239 disposed = true;
240 }
241
242 foreach (var instanceKvp in instances)
243 await instanceKvp.Value.Instance.DisposeAsync();
244
248
249 logger.LogInformation("Server shutdown");
250 }
251
253 public IInstanceReference? GetInstanceReference(Api.Models.Instance metadata)
254 {
255 ArgumentNullException.ThrowIfNull(metadata);
256
257 lock (instances)
258 {
259 if (!instances.TryGetValue(metadata.Require(x => x.Id), out var instance))
260 return null;
261
262 return instance.AddReference();
263 }
264 }
265
267 public async ValueTask MoveInstance(Models.Instance instance, string oldPath, CancellationToken cancellationToken)
268 {
269 ArgumentNullException.ThrowIfNull(oldPath);
270
271 using var lockContext = await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken);
272 using var instanceReferenceCheck = GetInstanceReference(instance);
273 if (instanceReferenceCheck != null)
274 throw new InvalidOperationException("Cannot move an online instance!");
275 var newPath = instance.Path!;
276 try
277 {
278 await ioManager.MoveDirectory(oldPath, newPath, cancellationToken);
279
280 // Delete the Game directory to clear out broken symlinks
281 var instanceGameIOManager = instanceFactory.CreateGameIOManager(instance);
282 await instanceGameIOManager.DeleteDirectory(DefaultIOManager.CurrentDirectory, cancellationToken);
283 }
284 catch (Exception ex)
285 {
286 logger.LogError(
287 ex,
288 "Error moving instance {instanceId}!",
289 instance.Id);
290 try
291 {
292 logger.LogDebug("Reverting instance {instanceId}'s path to {oldPath} in the DB...", instance.Id, oldPath);
293
294 // DCT: Operation must always run
296 {
297 var targetInstance = new Models.Instance
298 {
299 Id = instance.Id,
300 };
301 db.Instances.Attach(targetInstance);
302 targetInstance.Path = oldPath;
303 return db.Save(CancellationToken.None);
304 });
305 }
306 catch (Exception innerEx)
307 {
308 logger.LogCritical(
309 innerEx,
310 "Error reverting database after failing to move instance {instanceId}! Attempting to detach...",
311 instance.Id);
312
313 try
314 {
315 // DCT: Operation must always run
318 Array.Empty<byte>(),
319 CancellationToken.None);
320 }
321 catch (Exception tripleEx)
322 {
323 logger.LogCritical(
324 tripleEx,
325 "Okay, what gamma radiation are you under? Failed to write instance attach file!");
326
327 throw new AggregateException(tripleEx, innerEx, ex);
328 }
329
330 throw new AggregateException(ex, innerEx);
331 }
332
333 throw;
334 }
335 }
336
338 public async ValueTask OfflineInstance(Models.Instance metadata, User user, CancellationToken cancellationToken)
339 {
340 ArgumentNullException.ThrowIfNull(metadata);
341
342 using (await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken))
343 {
345 var instanceId = metadata.Require(x => x.Id);
346 lock (instances)
347 {
348 if (!instances.TryGetValue(instanceId, out container))
349 {
350 logger.LogDebug("Not offlining removed instance {instanceId}", metadata.Id);
351 return;
352 }
353
354 instances.Remove(instanceId);
355 }
356
357 logger.LogInformation("Offlining instance ID {instanceId}", metadata.Id);
358
359 try
360 {
361 await container.OnZeroReferences.WaitAsync(cancellationToken);
362
363 // we are the one responsible for cancelling his jobs
364 ValueTask<Job?[]> groupedTask = default;
366 async db =>
367 {
368 var jobs = await db
369 .Jobs
370 .AsQueryable()
371 .Where(x => x.Instance!.Id == metadata.Id && !x.StoppedAt.HasValue)
372 .Select(x => new Job(x.Id!.Value))
373 .ToListAsync(cancellationToken);
374
375 groupedTask = ValueTaskExtensions.WhenAll(
376 jobs.Select(job => jobService.CancelJob(job, user, true, cancellationToken)),
377 jobs.Count);
378 });
379
380 await groupedTask;
381 }
382 catch
383 {
384 // not too late to change your mind
385 lock (instances)
386 instances.Add(instanceId, container);
387
388 throw;
389 }
390
391 try
392 {
393 // at this point we can't really stop offlining the instance just because the request was cancelled
394 await container.Instance.StopAsync(shutdownCancellationTokenSource.Token);
395 }
396 finally
397 {
398 await container.Instance.DisposeAsync();
399 }
400 }
401 }
402
404 public async ValueTask OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
405 {
406 ArgumentNullException.ThrowIfNull(metadata);
407
408 var instanceId = metadata.Require(x => x.Id);
409 using var lockContext = await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken);
410 lock (instances)
411 if (instances.ContainsKey(instanceId))
412 {
413 logger.LogDebug("Aborting instance creation due to it seemingly already being online");
414 return;
415 }
416
417 logger.LogInformation("Onlining instance ID {instanceId} ({instanceName}) at {instancePath}", metadata.Id, metadata.Name, metadata.Path);
418 var instance = await instanceFactory.CreateInstance(this, metadata);
419 try
420 {
421 await instance.StartAsync(cancellationToken);
422
423 try
424 {
425 lock (instances)
426 instances.Add(
427 instanceId,
429 }
430 catch (Exception ex)
431 {
432 logger.LogError("Unable to commit onlined instance {instanceId} into service, offlining!", metadata.Id);
433 try
434 {
435 // DCT: Must always run
436 await instance.StopAsync(CancellationToken.None);
437 }
438 catch (Exception innerEx)
439 {
440 throw new AggregateException(innerEx, ex);
441 }
442
443 throw;
444 }
445 }
446 catch
447 {
448 await instance.DisposeAsync();
449 throw;
450 }
451 }
452
454 public Task StartAsync(CancellationToken cancellationToken)
455 {
456 cancellationToken.Register(startupCancellationTokenSource.Cancel);
458 return Task.CompletedTask;
459 }
460
462 public async Task StopAsync(CancellationToken cancellationToken)
463 {
464 try
465 {
466 using (cancellationToken.Register(shutdownCancellationTokenSource.Cancel))
467 try
468 {
469 if (startupTask == null)
470 {
471 logger.LogWarning("InstanceManager was never started!");
472 return;
473 }
474
475 logger.LogDebug("Stopping instance manager...");
476
477 if (!startupTask.IsCompleted)
478 {
479 logger.LogTrace("Interrupting startup task...");
481 await startupTask;
482 }
483
484 var instanceFactoryStopTask = instanceFactory.StopAsync(cancellationToken);
485 await jobService.StopAsync(cancellationToken);
486
487 async ValueTask OfflineInstanceImmediate(IInstance instance, CancellationToken cancellationToken)
488 {
489 try
490 {
491 await instance.StopAsync(cancellationToken);
492 }
493 catch (Exception ex)
494 {
495 logger.LogError(ex, "Instance shutdown exception!");
496 }
497 }
498
499 await ValueTaskExtensions.WhenAll(instances.Select(x => OfflineInstanceImmediate(x.Value.Instance, cancellationToken)));
500 await instanceFactoryStopTask;
501
502 await swarmServiceController.Shutdown(cancellationToken);
503 }
504 finally
505 {
506 if (originalConsoleTitle != null)
508 }
509 }
510 catch (Exception ex)
511 {
512 logger.LogCritical(ex, "Instance manager stop exception!");
513 }
514 }
515
517 public async ValueTask<BridgeResponse?> ProcessBridgeRequest(BridgeParameters parameters, CancellationToken cancellationToken)
518 {
519 ArgumentNullException.ThrowIfNull(parameters);
520
521 var accessIdentifier = parameters.AccessIdentifier;
522 if (accessIdentifier == null)
523 {
524 logger.LogWarning("Received invalid bridge request with null access identifier!");
525 return null;
526 }
527
528 IBridgeHandler? bridgeHandler = null;
529 var loggedDelay = false;
530 for (var i = 0; bridgeHandler == null && i < 30; ++i)
531 {
532 // There's a miniscule time period where we could potentially receive a bridge request and not have the registration ready when we launch DD
533 // This is a stopgap
534 Task delayTask = Task.CompletedTask;
535 lock (bridgeHandlers)
536 if (!bridgeHandlers.TryGetValue(accessIdentifier, out bridgeHandler))
537 {
538 if (!loggedDelay)
539 {
540 logger.LogTrace("Received bridge request with unregistered access identifier \"{aid}\". Waiting up to 3 seconds for it to be registered...", accessIdentifier);
541 loggedDelay = true;
542 }
543
544 delayTask = asyncDelayer.Delay(TimeSpan.FromMilliseconds(100), cancellationToken).AsTask();
545 }
546
547 await delayTask;
548 }
549
550 if (bridgeHandler == null)
551 lock (bridgeHandlers)
552 if (!bridgeHandlers.TryGetValue(accessIdentifier, out bridgeHandler))
553 {
554 logger.LogWarning("Received invalid bridge request with access identifier: {accessIdentifier}", accessIdentifier);
555 return null;
556 }
557
558 return await bridgeHandler.ProcessBridgeRequest(parameters, cancellationToken);
559 }
560
563 {
564 ArgumentNullException.ThrowIfNull(bridgeHandler);
565
566 var accessIdentifier = bridgeHandler.DMApiParameters.AccessIdentifier
567 ?? throw new InvalidOperationException("Attempted bridge registration with null AccessIdentifier!");
568 lock (bridgeHandlers)
569 {
570 bridgeHandlers.Add(accessIdentifier, bridgeHandler);
571 logger.LogTrace("Registered bridge handler: {accessIdentifier}", accessIdentifier);
572 }
573
574 return new BridgeRegistration(() =>
575 {
576 lock (bridgeHandlers)
577 {
578 bridgeHandlers.Remove(accessIdentifier);
579 logger.LogTrace("Unregistered bridge handler: {accessIdentifier}", accessIdentifier);
580 }
581 });
582 }
583
585 public IInstanceCore? GetInstance(Models.Instance metadata)
586 {
587 lock (instances)
588 {
589 instances.TryGetValue(metadata.Require(x => x.Id), out var container);
590 return container?.Instance;
591 }
592 }
593
599 async Task Initialize(CancellationToken cancellationToken)
600 {
601 try
602 {
603 logger.LogInformation("{versionString}", assemblyInformationProvider.VersionString);
605
607
608 // To let the web server startup immediately before we do any intense work
609 await Task.Yield();
610
611 await InitializeSwarm(cancellationToken);
612
613 List<Models.Instance>? dbInstances = null;
614
615 async ValueTask EnumerateInstances(IDatabaseContext databaseContext)
616 => dbInstances = await databaseContext
617 .Instances
618 .AsQueryable()
619 .Where(x => x.Online!.Value && x.SwarmIdentifer == swarmConfiguration.Identifier)
620 .Include(x => x.RepositorySettings)
621 .Include(x => x.ChatSettings)
622 .ThenInclude(x => x.Channels)
623 .Include(x => x.DreamDaemonSettings)
624 .ToListAsync(cancellationToken);
625
626 var instanceEnumeration = databaseContextFactory.UseContext(EnumerateInstances);
627
628 var factoryStartup = instanceFactory.StartAsync(cancellationToken);
629 var jobManagerStartup = jobService.StartAsync(cancellationToken);
630
631 await Task.WhenAll(instanceEnumeration.AsTask(), factoryStartup, jobManagerStartup);
632
633 var instanceOnliningTasks = dbInstances!.Select(
634 async metadata =>
635 {
636 try
637 {
638 await OnlineInstance(metadata, cancellationToken);
639 }
640 catch (Exception ex)
641 {
642 logger.LogError(ex, "Failed to online instance {instanceId}!", metadata.Id);
643 }
644 });
645
646 await Task.WhenAll(instanceOnliningTasks);
647
648 logger.LogInformation("Server ready!");
649 readyTcs.SetResult();
650
651 // this needs to happen after the HTTP API opens with readyTcs otherwise it can race and cause failed bridge requests with 503's
652 jobService.Activate(this);
653 }
654 catch (OperationCanceledException ex)
655 {
656 logger.LogInformation(ex, "Cancelled instance manager initialization!");
657 }
658 catch (Exception e)
659 {
660 logger.LogCritical(e, "Instance manager startup error!");
661 try
662 {
663 await serverControl.Die(e);
664 return;
665 }
666 catch (Exception e2)
667 {
668 logger.LogCritical(e2, "Failed to kill server!");
669 }
670 }
671 }
672
677 {
678 logger.LogDebug("Running as user: {username}", Environment.UserName);
679
681
682 using (var systemIdentity = systemIdentityFactory.GetCurrent())
683 {
684 if (!systemIdentity.CanCreateSymlinks)
685 throw new InvalidOperationException($"The user running {Constants.CanonicalPackageName} cannot create symlinks! Please try running as an administrative user!");
686
687 if (!platformIdentifier.IsWindows && systemIdentity.IsSuperUser && !internalConfiguration.UsingDocker)
688 {
689 logger.LogWarning("TGS is being run as the root account. This is not recommended.");
690 }
691 }
692
693 // This runs before the real socket is opened, ensures we don't perform reattaches unless we're fairly certain the bind won't fail
694 // If it does fail, DD will be killed.
696 }
697
703 async ValueTask InitializeSwarm(CancellationToken cancellationToken)
704 {
705 SwarmRegistrationResult registrationResult;
706 do
707 {
708 registrationResult = await swarmServiceController.Initialize(cancellationToken);
709
710 if (registrationResult == SwarmRegistrationResult.Unauthorized)
711 throw new InvalidOperationException("Swarm private key does not match the swarm controller's!");
712
713 if (registrationResult == SwarmRegistrationResult.VersionMismatch)
714 throw new InvalidOperationException("Swarm controller's TGS version does not match our own!");
715
716 if (registrationResult != SwarmRegistrationResult.Success)
717 await asyncDelayer.Delay(TimeSpan.FromSeconds(5), cancellationToken);
718 }
719 while (registrationResult != SwarmRegistrationResult.Success && !cancellationToken.IsCancellationRequested);
720 }
721 }
722}
string? Identifier
The server's identifier.
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.
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.
readonly InternalConfiguration internalConfiguration
The InternalConfiguration for the InstanceManager.
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.
void PreflightChecks()
Check we have a valid system and configuration.
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 me...
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 reque...
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.
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, IOptions< InternalConfiguration > internalConfigurationOptions, ILogger< InstanceManager > logger)
Initializes a new instance of the InstanceManager class.
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.
Unstable configuration options used internally by TGS.
bool UsingDocker
If the server is running inside of a Docker container.
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, ILogger? logger=null)
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.
bool IsWindows
If the current platform is a Windows platform.
ValueTask Delay(TimeSpan timeSpan, CancellationToken cancellationToken)
Create a Task that completes after a given timeSpan .
SwarmRegistrationResult
Result of attempting to register with a swarm controller.