tgstation-server 6.14.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 ILogger<InstanceManager> logger;
109
113 readonly Dictionary<long, ReferenceCountingContainer<IInstance, InstanceWrapper>> instances;
114
118 readonly Dictionary<string, IBridgeHandler> bridgeHandlers;
119
123 readonly SemaphoreSlim instanceStateChangeSemaphore;
124
129
134
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 generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
231 swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
232 internalConfiguration = internalConfigurationOptions?.Value ?? 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
275 public IInstanceReference? GetInstanceReference(Api.Models.Instance metadata)
276 {
277 ArgumentNullException.ThrowIfNull(metadata);
278
279 lock (instances)
280 {
281 if (!instances.TryGetValue(metadata.Require(x => x.Id), out var instance))
282 return null;
283
284 return instance.AddReference();
285 }
286 }
287
289 public async ValueTask MoveInstance(Models.Instance instance, string oldPath, CancellationToken cancellationToken)
290 {
291 ArgumentNullException.ThrowIfNull(oldPath);
292
293 using var lockContext = await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken);
294 using var instanceReferenceCheck = GetInstanceReference(instance);
295 if (instanceReferenceCheck != null)
296 throw new InvalidOperationException("Cannot move an online instance!");
297 var newPath = instance.Path!;
298 try
299 {
300 await ioManager.MoveDirectory(oldPath, newPath, cancellationToken);
301
302 // Delete the Game directory to clear out broken symlinks
303 var instanceGameIOManager = instanceFactory.CreateGameIOManager(instance);
304 await instanceGameIOManager.DeleteDirectory(DefaultIOManager.CurrentDirectory, cancellationToken);
305 }
306 catch (Exception ex)
307 {
308 logger.LogError(
309 ex,
310 "Error moving instance {instanceId}!",
311 instance.Id);
312 try
313 {
314 logger.LogDebug("Reverting instance {instanceId}'s path to {oldPath} in the DB...", instance.Id, oldPath);
315
316 // DCT: Operation must always run
318 {
319 var targetInstance = new Models.Instance
320 {
321 Id = instance.Id,
322 };
323 db.Instances.Attach(targetInstance);
324 targetInstance.Path = oldPath;
325 return db.Save(CancellationToken.None);
326 });
327 }
328 catch (Exception innerEx)
329 {
330 logger.LogCritical(
331 innerEx,
332 "Error reverting database after failing to move instance {instanceId}! Attempting to detach...",
333 instance.Id);
334
335 try
336 {
337 // DCT: Operation must always run
340 Array.Empty<byte>(),
341 CancellationToken.None);
342 }
343 catch (Exception tripleEx)
344 {
345 logger.LogCritical(
346 tripleEx,
347 "Okay, what gamma radiation are you under? Failed to write instance attach file!");
348
349 throw new AggregateException(tripleEx, innerEx, ex);
350 }
351
352 throw new AggregateException(ex, innerEx);
353 }
354
355 throw;
356 }
357 }
358
360 public async ValueTask OfflineInstance(Models.Instance metadata, User user, CancellationToken cancellationToken)
361 {
362 ArgumentNullException.ThrowIfNull(metadata);
363
364 using (await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken))
365 {
367 var instanceId = metadata.Require(x => x.Id);
368 lock (instances)
369 {
370 if (!instances.TryGetValue(instanceId, out container))
371 {
372 logger.LogDebug("Not offlining removed instance {instanceId}", metadata.Id);
373 return;
374 }
375
376 instances.Remove(instanceId);
377 }
378
379 logger.LogInformation("Offlining instance ID {instanceId}", metadata.Id);
380
381 try
382 {
383 await container.OnZeroReferences.WaitAsync(cancellationToken);
384
385 // we are the one responsible for cancelling his jobs
386 ValueTask<Job?[]> groupedTask = default;
388 async db =>
389 {
390 var jobs = await db
391 .Jobs
392 .AsQueryable()
393 .Where(x => x.Instance!.Id == metadata.Id && !x.StoppedAt.HasValue)
394 .Select(x => new Job(x.Id!.Value))
395 .ToListAsync(cancellationToken);
396
397 groupedTask = ValueTaskExtensions.WhenAll(
398 jobs.Select(job => jobService.CancelJob(job, user, true, cancellationToken)),
399 jobs.Count);
400 });
401
402 await groupedTask;
403 }
404 catch
405 {
406 // not too late to change your mind
407 lock (instances)
408 instances.Add(instanceId, container);
409
410 throw;
411 }
412
413 try
414 {
415 // at this point we can't really stop offlining the instance just because the request was cancelled
416 await container.Instance.StopAsync(shutdownCancellationTokenSource.Token);
417 }
418 finally
419 {
420 await container.Instance.DisposeAsync();
421 onlineInstances.Dec();
422 }
423 }
424 }
425
427 public async ValueTask OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
428 {
429 ArgumentNullException.ThrowIfNull(metadata);
430
431 var instanceId = metadata.Require(x => x.Id);
432 using var lockContext = await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken);
433 lock (instances)
434 if (instances.ContainsKey(instanceId))
435 {
436 logger.LogDebug("Aborting instance creation due to it seemingly already being online");
437 return;
438 }
439
440 logger.LogInformation("Onlining instance ID {instanceId} ({instanceName}) at {instancePath}", metadata.Id, metadata.Name, metadata.Path);
441 var instance = await instanceFactory.CreateInstance(this, metadata);
442 try
443 {
444 await instance.StartAsync(cancellationToken);
445
446 try
447 {
448 lock (instances)
449 instances.Add(
450 instanceId,
452
453 onlineInstances.Inc();
454 }
455 catch (Exception ex)
456 {
457 logger.LogError("Unable to commit onlined instance {instanceId} into service, offlining!", metadata.Id);
458 try
459 {
460 // DCT: Must always run
461 await instance.StopAsync(CancellationToken.None);
462 }
463 catch (Exception innerEx)
464 {
465 throw new AggregateException(innerEx, ex);
466 }
467
468 throw;
469 }
470 }
471 catch
472 {
473 await instance.DisposeAsync();
474 throw;
475 }
476 }
477
479 public Task StartAsync(CancellationToken cancellationToken)
480 {
481 cancellationToken.Register(startupCancellationTokenSource.Cancel);
483 return Task.CompletedTask;
484 }
485
487 public async Task StopAsync(CancellationToken cancellationToken)
488 {
489 try
490 {
491 using (cancellationToken.Register(shutdownCancellationTokenSource.Cancel))
492 try
493 {
494 if (startupTask == null)
495 {
496 logger.LogWarning("InstanceManager was never started!");
497 return;
498 }
499
500 logger.LogDebug("Stopping instance manager...");
501
502 if (!startupTask.IsCompleted)
503 {
504 logger.LogTrace("Interrupting startup task...");
506 await startupTask;
507 }
508
509 var instanceFactoryStopTask = instanceFactory.StopAsync(cancellationToken);
510 await jobService.StopAsync(cancellationToken);
511
512 async ValueTask OfflineInstanceImmediate(IInstance instance, CancellationToken cancellationToken)
513 {
514 try
515 {
516 await instance.StopAsync(cancellationToken);
517 }
518 catch (Exception ex)
519 {
520 logger.LogError(ex, "Instance shutdown exception!");
521 }
522 }
523
524 await ValueTaskExtensions.WhenAll(instances.Select(x => OfflineInstanceImmediate(x.Value.Instance, cancellationToken)));
525 await instanceFactoryStopTask;
526
527 await swarmServiceController.Shutdown(cancellationToken);
528 }
529 finally
530 {
531 if (originalConsoleTitle != null)
533 }
534 }
535 catch (Exception ex)
536 {
537 logger.LogCritical(ex, "Instance manager stop exception!");
538 }
539 }
540
542 public async ValueTask<BridgeResponse?> ProcessBridgeRequest(BridgeParameters parameters, CancellationToken cancellationToken)
543 {
544 ArgumentNullException.ThrowIfNull(parameters);
545
546 var accessIdentifier = parameters.AccessIdentifier;
547 if (accessIdentifier == null)
548 {
549 logger.LogWarning("Received invalid bridge request with null access identifier!");
550 return null;
551 }
552
553 IBridgeHandler? bridgeHandler = null;
554 var loggedDelay = false;
555 for (var i = 0; bridgeHandler == null && i < 30; ++i)
556 {
557 // There's a miniscule time period where we could potentially receive a bridge request and not have the registration ready when we launch DD
558 // This is a stopgap
559 Task delayTask = Task.CompletedTask;
560 lock (bridgeHandlers)
561 if (!bridgeHandlers.TryGetValue(accessIdentifier, out bridgeHandler))
562 {
563 if (!loggedDelay)
564 {
565 logger.LogTrace("Received bridge request with unregistered access identifier \"{aid}\". Waiting up to 3 seconds for it to be registered...", accessIdentifier);
566 loggedDelay = true;
567 }
568
569 delayTask = asyncDelayer.Delay(TimeSpan.FromMilliseconds(100), cancellationToken).AsTask();
570 }
571
572 await delayTask;
573 }
574
575 if (bridgeHandler == null)
576 lock (bridgeHandlers)
577 if (!bridgeHandlers.TryGetValue(accessIdentifier, out bridgeHandler))
578 {
579 logger.LogWarning("Received invalid bridge request with access identifier: {accessIdentifier}", accessIdentifier);
580 return null;
581 }
582
583 return await bridgeHandler.ProcessBridgeRequest(parameters, cancellationToken);
584 }
585
588 {
589 ArgumentNullException.ThrowIfNull(bridgeHandler);
590
591 var accessIdentifier = bridgeHandler.DMApiParameters.AccessIdentifier
592 ?? throw new InvalidOperationException("Attempted bridge registration with null AccessIdentifier!");
593 lock (bridgeHandlers)
594 {
595 bridgeHandlers.Add(accessIdentifier, bridgeHandler);
596 logger.LogTrace("Registered bridge handler: {accessIdentifier}", accessIdentifier);
597 }
598
599 return new BridgeRegistration(() =>
600 {
601 lock (bridgeHandlers)
602 {
603 bridgeHandlers.Remove(accessIdentifier);
604 logger.LogTrace("Unregistered bridge handler: {accessIdentifier}", accessIdentifier);
605 }
606 });
607 }
608
610 public IInstanceCore? GetInstance(Models.Instance metadata)
611 {
612 lock (instances)
613 {
614 instances.TryGetValue(metadata.Require(x => x.Id), out var container);
615 return container?.Instance;
616 }
617 }
618
624 async Task Initialize(CancellationToken cancellationToken)
625 {
626 try
627 {
628 logger.LogInformation("{versionString}", assemblyInformationProvider.VersionString);
630
632
633 // To let the web server startup immediately before we do any intense work
634 await Task.Yield();
635
636 await InitializeSwarm(cancellationToken);
637
638 List<Models.Instance>? dbInstances = null;
639
640 async ValueTask EnumerateInstances(IDatabaseContext databaseContext)
641 => dbInstances = await databaseContext
642 .Instances
643 .AsQueryable()
644 .Where(x => x.Online!.Value && x.SwarmIdentifer == swarmConfiguration.Identifier)
645 .Include(x => x.RepositorySettings)
646 .Include(x => x.ChatSettings)
647 .ThenInclude(x => x.Channels)
648 .Include(x => x.DreamDaemonSettings)
649 .ToListAsync(cancellationToken);
650
651 var instanceEnumeration = databaseContextFactory.UseContext(EnumerateInstances);
652
653 var factoryStartup = instanceFactory.StartAsync(cancellationToken);
654 var jobManagerStartup = jobService.StartAsync(cancellationToken);
655
656 await Task.WhenAll(instanceEnumeration.AsTask(), factoryStartup, jobManagerStartup);
657
658 var instanceOnliningTasks = dbInstances!.Select(
659 async metadata =>
660 {
661 try
662 {
663 await OnlineInstance(metadata, cancellationToken);
664 }
665 catch (Exception ex)
666 {
667 logger.LogError(ex, "Failed to online instance {instanceId}!", metadata.Id);
668 }
669 });
670
671 await Task.WhenAll(instanceOnliningTasks);
672
673 logger.LogInformation("Server ready!");
674 readyTcs.SetResult();
675
676 // this needs to happen after the HTTP API opens with readyTcs otherwise it can race and cause failed bridge requests with 503's
677 jobService.Activate(this);
678 }
679 catch (OperationCanceledException ex)
680 {
681 logger.LogInformation(ex, "Cancelled instance manager initialization!");
682 }
683 catch (Exception e)
684 {
685 logger.LogCritical(e, "Instance manager startup error!");
686 try
687 {
688 await serverControl.Die(e);
689 return;
690 }
691 catch (Exception e2)
692 {
693 logger.LogCritical(e2, "Failed to kill server!");
694 }
695 }
696 }
697
702 {
703 logger.LogDebug("Running as user: {username}", Environment.UserName);
704
706
707 using (var systemIdentity = systemIdentityFactory.GetCurrent())
708 {
709 if (!systemIdentity.CanCreateSymlinks)
710 throw new InvalidOperationException($"The user running {Constants.CanonicalPackageName} cannot create symlinks! Please try running as an administrative user!");
711
712 if (!platformIdentifier.IsWindows && systemIdentity.IsSuperUser && !internalConfiguration.UsingDocker)
713 {
714 logger.LogWarning("TGS is being run as the root account. This is not recommended.");
715 }
716 }
717
718 // This runs before the real socket is opened, ensures we don't perform reattaches unless we're fairly certain the bind won't fail
719 // If it does fail, DD will be killed.
721 }
722
728 async ValueTask InitializeSwarm(CancellationToken cancellationToken)
729 {
730 SwarmRegistrationResult registrationResult;
731 do
732 {
733 registrationResult = await swarmServiceController.Initialize(cancellationToken);
734
735 if (registrationResult == SwarmRegistrationResult.Unauthorized)
736 throw new InvalidOperationException("Swarm private key does not match the swarm controller's!");
737
738 if (registrationResult == SwarmRegistrationResult.VersionMismatch)
739 throw new InvalidOperationException("Swarm controller's TGS version does not match our own!");
740
741 if (registrationResult != SwarmRegistrationResult.Success)
742 await asyncDelayer.Delay(TimeSpan.FromSeconds(5), cancellationToken);
743 }
744 while (registrationResult != SwarmRegistrationResult.Success && !cancellationToken.IsCancellationRequested);
745 }
746 }
747}
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 .
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 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 Gauge onlineInstances
The count of online instances.
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.
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.