tgstation-server 6.19.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
12using Prometheus;
13
31
33{
35 sealed class InstanceManager :
41 {
43 public Task Ready => readyTcs.Task;
44
49
54
59
64
69
74
79
84
89
94
98 readonly IConsole console;
99
104
108 readonly IOptions<GeneralConfiguration> generalConfigurationOptions;
109
113 readonly IOptions<SwarmConfiguration> swarmConfigurationOptions;
114
118 readonly IOptions<InternalConfiguration> internalConfigurationOptions;
119
123 readonly ILogger<InstanceManager> logger;
124
128 readonly Dictionary<long, ReferenceCountingContainer<IInstance, InstanceWrapper>> instances;
129
133 readonly Dictionary<string, IBridgeHandler> bridgeHandlers;
134
138 readonly SemaphoreSlim instanceStateChangeSemaphore;
139
143 readonly TaskCompletionSource readyTcs;
144
148 readonly CancellationTokenSource startupCancellationTokenSource;
149
153 readonly CancellationTokenSource shutdownCancellationTokenSource;
154
158 readonly Gauge onlineInstances;
159
163 readonly string? originalConsoleTitle;
164
169
174
209 IMetricFactory metricFactory,
210 ICollectorRegistry collectorRegistry,
211 IOptions<GeneralConfiguration> generalConfigurationOptions,
212 IOptions<SwarmConfiguration> swarmConfigurationOptions,
213 IOptions<InternalConfiguration> internalConfigurationOptions,
214 ILogger<InstanceManager> logger)
215 {
216 this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
217 this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
218 this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
219 this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
220 this.jobService = jobService ?? throw new ArgumentNullException(nameof(jobService));
221 this.serverControl = serverControl ?? throw new ArgumentNullException(nameof(serverControl));
222 this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
223 this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
224 this.serverPortProvider = serverPortProvider ?? throw new ArgumentNullException(nameof(serverPortProvider));
225 this.swarmServiceController = swarmServiceController ?? throw new ArgumentNullException(nameof(swarmServiceController));
226 this.console = console ?? throw new ArgumentNullException(nameof(console));
227 this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
228 ArgumentNullException.ThrowIfNull(metricFactory);
229 ArgumentNullException.ThrowIfNull(collectorRegistry);
230 this.generalConfigurationOptions = generalConfigurationOptions ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
231 this.swarmConfigurationOptions = swarmConfigurationOptions ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
232 this.internalConfigurationOptions = internalConfigurationOptions ?? throw new ArgumentNullException(nameof(internalConfigurationOptions));
233 this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
234
236
237 onlineInstances = metricFactory.CreateGauge("tgs_online_instances", "The total number of instances online");
238
239 instances = new Dictionary<long, ReferenceCountingContainer<IInstance, InstanceWrapper>>();
240 bridgeHandlers = new Dictionary<string, IBridgeHandler>();
241 readyTcs = new TaskCompletionSource();
242 instanceStateChangeSemaphore = new SemaphoreSlim(1);
243 startupCancellationTokenSource = new CancellationTokenSource();
244 shutdownCancellationTokenSource = new CancellationTokenSource();
245
246 collectorRegistry.AddBeforeCollectCallback(async cancellationToken =>
247 {
248 using (await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken))
249 foreach (var container in instances.Values)
250 container.Instance.Watchdog.RunMetricsScrape();
251 });
252 }
253
255 public async ValueTask DisposeAsync()
256 {
257 lock (instances)
258 {
259 if (disposed)
260 return;
261 disposed = true;
262 }
263
264 foreach (var instanceKvp in instances)
265 await instanceKvp.Value.Instance.DisposeAsync();
266
270
271 logger.LogInformation("Server shutdown");
272 }
273
276 {
277 lock (instances)
278 {
279 if (!instances.TryGetValue(instanceId, out var instance))
280 return null;
281
282 return instance.AddReference();
283 }
284 }
285
287 public async ValueTask MoveInstance(Models.Instance instance, string oldPath, CancellationToken cancellationToken)
288 {
289 ArgumentNullException.ThrowIfNull(oldPath);
290
291 using var lockContext = await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken);
292 using var instanceReferenceCheck = this.GetInstanceReference(instance);
293 if (instanceReferenceCheck != null)
294 throw new InvalidOperationException("Cannot move an online instance!");
295 var newPath = instance.Path!;
296 try
297 {
298 await ioManager.MoveDirectory(oldPath, newPath, cancellationToken);
299
300 // Delete the Game directory to clear out broken symlinks
301 var instanceGameIOManager = instanceFactory.CreateGameIOManager(instance);
302 await instanceGameIOManager.DeleteDirectory(DefaultIOManager.CurrentDirectory, cancellationToken);
303 }
304 catch (Exception ex)
305 {
306 logger.LogError(
307 ex,
308 "Error moving instance {instanceId}!",
309 instance.Id);
310 try
311 {
312 logger.LogDebug("Reverting instance {instanceId}'s path to {oldPath} in the DB...", instance.Id, oldPath);
313
314 // DCT: Operation must always run
316 {
317 var targetInstance = new Models.Instance
318 {
319 Id = instance.Id,
320 };
321 db.Instances.Attach(targetInstance);
322 targetInstance.Path = oldPath;
323 return db.Save(CancellationToken.None);
324 });
325 }
326 catch (Exception innerEx)
327 {
328 logger.LogCritical(
329 innerEx,
330 "Error reverting database after failing to move instance {instanceId}! Attempting to detach...",
331 instance.Id);
332
333 try
334 {
335 // DCT: Operation must always run
338 Array.Empty<byte>(),
339 CancellationToken.None);
340 }
341 catch (Exception tripleEx)
342 {
343 logger.LogCritical(
344 tripleEx,
345 "Okay, what gamma radiation are you under? Failed to write instance attach file!");
346
347 throw new AggregateException(tripleEx, innerEx, ex);
348 }
349
350 throw new AggregateException(ex, innerEx);
351 }
352
353 throw;
354 }
355 }
356
358 public async ValueTask OfflineInstance(Models.Instance metadata, User user, CancellationToken cancellationToken)
359 {
360 ArgumentNullException.ThrowIfNull(metadata);
361
362 using (await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken))
363 {
365 var instanceId = metadata.Require(x => x.Id);
366 lock (instances)
367 {
368 if (!instances.TryGetValue(instanceId, out container))
369 {
370 logger.LogDebug("Not offlining removed instance {instanceId}", metadata.Id);
371 return;
372 }
373
374 instances.Remove(instanceId);
375 }
376
377 logger.LogInformation("Offlining instance ID {instanceId}", metadata.Id);
378
379 try
380 {
381 await container.OnZeroReferences.WaitAsync(cancellationToken);
382
383 // we are the one responsible for cancelling his jobs
384 ValueTask<Job?[]> groupedTask = default;
386 async db =>
387 {
388 var jobs = await db
389 .Jobs
390 .Where(x => x.Instance!.Id == metadata.Id && !x.StoppedAt.HasValue)
391 .Select(x => new Job(x.Id!.Value))
392 .ToListAsync(cancellationToken);
393
394 groupedTask = ValueTaskExtensions.WhenAll(
395 jobs.Select(job => jobService.CancelJob(job, user, true, cancellationToken)),
396 jobs.Count);
397 });
398
399 await groupedTask;
400 }
401 catch
402 {
403 // not too late to change your mind
404 lock (instances)
405 instances.Add(instanceId, container);
406
407 throw;
408 }
409
410 try
411 {
412 // at this point we can't really stop offlining the instance just because the request was cancelled
413 await container.Instance.StopAsync(shutdownCancellationTokenSource.Token);
414 }
415 finally
416 {
417 await container.Instance.DisposeAsync();
418 onlineInstances.Dec();
419 }
420 }
421 }
422
424 public async ValueTask OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
425 {
426 ArgumentNullException.ThrowIfNull(metadata);
427
428 var instanceId = metadata.Require(x => x.Id);
429 using var lockContext = await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken);
430 lock (instances)
431 if (instances.ContainsKey(instanceId))
432 {
433 logger.LogDebug("Aborting instance creation due to it seemingly already being online");
434 return;
435 }
436
437 logger.LogInformation("Onlining instance ID {instanceId} ({instanceName}) at {instancePath}", metadata.Id, metadata.Name, metadata.Path);
438 var instance = await instanceFactory.CreateInstance(this, metadata);
439 try
440 {
441 await instance.StartAsync(cancellationToken);
442
443 try
444 {
445 lock (instances)
446 instances.Add(
447 instanceId,
449
450 onlineInstances.Inc();
451 }
452 catch (Exception ex)
453 {
454 logger.LogError("Unable to commit onlined instance {instanceId} into service, offlining!", metadata.Id);
455 try
456 {
457 // DCT: Must always run
458 await instance.StopAsync(CancellationToken.None);
459 }
460 catch (Exception innerEx)
461 {
462 throw new AggregateException(innerEx, ex);
463 }
464
465 throw;
466 }
467 }
468 catch
469 {
470 await instance.DisposeAsync();
471 throw;
472 }
473 }
474
476 public Task StartAsync(CancellationToken cancellationToken)
477 {
478 cancellationToken.Register(startupCancellationTokenSource.Cancel);
480 return Task.CompletedTask;
481 }
482
484 public async Task StopAsync(CancellationToken cancellationToken)
485 {
486 try
487 {
488 using (cancellationToken.Register(shutdownCancellationTokenSource.Cancel))
489 try
490 {
491 if (startupTask == null)
492 {
493 logger.LogWarning("InstanceManager was never started!");
494 return;
495 }
496
497 logger.LogDebug("Stopping instance manager...");
498
499 if (!startupTask.IsCompleted)
500 {
501 logger.LogTrace("Interrupting startup task...");
503 await startupTask;
504 }
505
506 var instanceFactoryStopTask = instanceFactory.StopAsync(cancellationToken);
507 await jobService.StopAsync(cancellationToken);
508
509 async ValueTask OfflineInstanceImmediate(IInstance instance, CancellationToken cancellationToken)
510 {
511 try
512 {
513 await instance.StopAsync(cancellationToken);
514 }
515 catch (Exception ex)
516 {
517 logger.LogError(ex, "Instance shutdown exception!");
518 }
519 }
520
521 await ValueTaskExtensions.WhenAll(instances.Select(x => OfflineInstanceImmediate(x.Value.Instance, cancellationToken)));
522 await instanceFactoryStopTask;
523
524 await swarmServiceController.Shutdown(cancellationToken);
525 }
526 finally
527 {
528 if (originalConsoleTitle != null)
530 }
531 }
532 catch (Exception ex)
533 {
534 logger.LogCritical(ex, "Instance manager stop exception!");
535 }
536 }
537
539 public async ValueTask<BridgeResponse?> ProcessBridgeRequest(BridgeParameters parameters, CancellationToken cancellationToken)
540 {
541 ArgumentNullException.ThrowIfNull(parameters);
542
543 var accessIdentifier = parameters.AccessIdentifier;
544 if (accessIdentifier == null)
545 {
546 logger.LogWarning("Received invalid bridge request with null access identifier!");
547 return null;
548 }
549
550 IBridgeHandler? bridgeHandler = null;
551 var loggedDelay = false;
552 for (var i = 0; bridgeHandler == null && i < 30; ++i)
553 {
554 // There's a miniscule time period where we could potentially receive a bridge request and not have the registration ready when we launch DD
555 // This is a stopgap
556 Task delayTask = Task.CompletedTask;
557 lock (bridgeHandlers)
558 if (!bridgeHandlers.TryGetValue(accessIdentifier, out bridgeHandler))
559 {
560 if (!loggedDelay)
561 {
562 logger.LogTrace("Received bridge request with unregistered access identifier \"{aid}\". Waiting up to 3 seconds for it to be registered...", accessIdentifier);
563 loggedDelay = true;
564 }
565
566 delayTask = asyncDelayer.Delay(TimeSpan.FromMilliseconds(100), cancellationToken).AsTask();
567 }
568
569 await delayTask;
570 }
571
572 if (bridgeHandler == null)
573 lock (bridgeHandlers)
574 if (!bridgeHandlers.TryGetValue(accessIdentifier, out bridgeHandler))
575 {
576 logger.LogWarning("Received invalid bridge request with access identifier: {accessIdentifier}", accessIdentifier);
577 return null;
578 }
579
580 return await bridgeHandler.ProcessBridgeRequest(parameters, cancellationToken);
581 }
582
585 {
586 ArgumentNullException.ThrowIfNull(bridgeHandler);
587
588 var accessIdentifier = bridgeHandler.DMApiParameters.AccessIdentifier
589 ?? throw new InvalidOperationException("Attempted bridge registration with null AccessIdentifier!");
590 lock (bridgeHandlers)
591 {
592 bridgeHandlers.Add(accessIdentifier, bridgeHandler);
593 logger.LogTrace("Registered bridge handler: {accessIdentifier}", accessIdentifier);
594 }
595
596 return new BridgeRegistration(() =>
597 {
598 lock (bridgeHandlers)
599 {
600 bridgeHandlers.Remove(accessIdentifier);
601 logger.LogTrace("Unregistered bridge handler: {accessIdentifier}", accessIdentifier);
602 }
603 });
604 }
605
607 public IInstanceCore? GetInstance(Models.Instance metadata)
608 {
609 lock (instances)
610 {
611 instances.TryGetValue(metadata.Require(x => x.Id), out var container);
612 return container?.Instance;
613 }
614 }
615
621 async Task Initialize(CancellationToken cancellationToken)
622 {
623 try
624 {
625 logger.LogInformation("{versionString}", assemblyInformationProvider.VersionString);
627
629
630 // To let the web server startup immediately before we do any intense work
631 await Task.Yield();
632
633 await InitializeSwarm(cancellationToken);
634
635 List<Models.Instance>? dbInstances = null;
636
637 async ValueTask EnumerateInstances(IDatabaseContext databaseContext)
638 => dbInstances = await databaseContext
639 .Instances
640 .Where(x => x.Online!.Value && x.SwarmIdentifer == swarmConfigurationOptions.Value.Identifier)
641 .Include(x => x.RepositorySettings)
642 .Include(x => x.ChatSettings)
643 .ThenInclude(x => x.Channels)
644 .Include(x => x.DreamDaemonSettings)
645 .ToListAsync(cancellationToken);
646
647 var instanceEnumeration = databaseContextFactory.UseContext(EnumerateInstances);
648
649 var factoryStartup = instanceFactory.StartAsync(cancellationToken);
650 var jobManagerStartup = jobService.StartAsync(cancellationToken);
651
652 await Task.WhenAll(instanceEnumeration.AsTask(), factoryStartup, jobManagerStartup);
653
654 var instanceOnliningTasks = dbInstances!.Select(
655 async metadata =>
656 {
657 try
658 {
659 await OnlineInstance(metadata, cancellationToken);
660 }
661 catch (Exception ex)
662 {
663 logger.LogError(ex, "Failed to online instance {instanceId}!", metadata.Id);
664 }
665 });
666
667 await Task.WhenAll(instanceOnliningTasks);
668
669 logger.LogInformation("Server ready!");
670 readyTcs.SetResult();
671
672 // this needs to happen after the HTTP API opens with readyTcs otherwise it can race and cause failed bridge requests with 503's
673 jobService.Activate(this);
674 }
675 catch (OperationCanceledException ex)
676 {
677 logger.LogInformation(ex, "Cancelled instance manager initialization!");
678 }
679 catch (Exception e)
680 {
681 logger.LogCritical(e, "Instance manager startup error!");
682 try
683 {
684 await serverControl.Die(e);
685 return;
686 }
687 catch (Exception e2)
688 {
689 logger.LogCritical(e2, "Failed to kill server!");
690 }
691 }
692 }
693
698 {
699 logger.LogDebug("Running as user: {username}", Environment.UserName);
700
701 generalConfigurationOptions.Value.CheckCompatibility(logger, ioManager);
702
703 using (var systemIdentity = systemIdentityFactory.GetCurrent())
704 {
705 if (!systemIdentity.CanCreateSymlinks)
706 throw new InvalidOperationException($"The user running {Constants.CanonicalPackageName} cannot create symlinks! Please try running as an administrative user!");
707
708 if (!platformIdentifier.IsWindows && systemIdentity.IsSuperUser && !internalConfigurationOptions.Value.UsingDocker)
709 {
710 logger.LogWarning("TGS is being run as the root account. This is not recommended.");
711 }
712 }
713
714 // This runs before the real socket is opened, ensures we don't perform reattaches unless we're fairly certain the bind won't fail
715 // If it does fail, DD will be killed.
717 }
718
724 async ValueTask InitializeSwarm(CancellationToken cancellationToken)
725 {
726 SwarmRegistrationResult registrationResult;
727 do
728 {
729 registrationResult = await swarmServiceController.Initialize(cancellationToken);
730
731 if (registrationResult == SwarmRegistrationResult.Unauthorized)
732 throw new InvalidOperationException("Swarm private key does not match the swarm controller's!");
733
734 if (registrationResult == SwarmRegistrationResult.VersionMismatch)
735 throw new InvalidOperationException("Swarm controller's TGS version does not match our own!");
736
737 if (registrationResult != SwarmRegistrationResult.Success)
738 await asyncDelayer.Delay(TimeSpan.FromSeconds(5), cancellationToken);
739 }
740 while (registrationResult != SwarmRegistrationResult.Success && !cancellationToken.IsCancellationRequested);
741 }
742 }
743}
Extension methods for the ValueTask and ValueTask<TResult> classes.
static async ValueTask WhenAll(IEnumerable< ValueTask > tasks)
Fully await a given list of tasks .
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, IMetricFactory metricFactory, ICollectorRegistry collectorRegistry, IOptions< GeneralConfiguration > generalConfigurationOptions, IOptions< SwarmConfiguration > swarmConfigurationOptions, IOptions< InternalConfiguration > internalConfigurationOptions, ILogger< InstanceManager > logger)
Initializes a new instance of the InstanceManager class.
Task Ready
Task that completes when the IInstanceManager finishes initializing.
readonly TaskCompletionSource readyTcs
The TaskCompletionSource for Ready.
readonly IOptions< InternalConfiguration > internalConfigurationOptions
The IOptions<TOptions> of InternalConfiguration for the InstanceManager.
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 Gauge onlineInstances
The count of online instances.
IInstanceReference? GetInstanceReference(long instanceId)
Get the IInstanceReference associated with given instanceId .The IInstance associated with the given ...
readonly IIOManager ioManager
The IIOManager for the InstanceManager.
readonly IOptions< GeneralConfiguration > generalConfigurationOptions
The IOptions<TOptions> of GeneralConfiguration for the InstanceManager.
bool disposed
If the InstanceManager has been DisposeAsync'd.
readonly IOptions< SwarmConfiguration > swarmConfigurationOptions
The IOptions<TOptions> of SwarmConfiguration for the InstanceManager.
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.
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 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.
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.
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.
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:14
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.