/// <summary> /// Construct a <see cref="SessionController"/> /// </summary> /// <param name="reattachInformation">The value of <see cref="reattachInformation"/></param> /// <param name="process">The value of <see cref="process"/></param> /// <param name="byondLock">The value of <see cref="byondLock"/></param> /// <param name="byondTopicSender">The value of <see cref="byondTopicSender"/></param> /// <param name="bridgeRegistrar">The <see cref="IBridgeRegistrar"/> used to populate <see cref="bridgeRegistration"/>.</param> /// <param name="chat">The value of <see cref="chat"/></param> /// <param name="chatTrackingContext">The value of <see cref="chatTrackingContext"/></param> /// <param name="assemblyInformationProvider">The <see cref="IAssemblyInformationProvider"/> for the <see cref="SessionController"/>.</param> /// <param name="logger">The value of <see cref="logger"/></param> /// <param name="startupTimeout">The optional time to wait before failing the <see cref="LaunchResult"/></param> /// <param name="reattached">If this is a reattached session.</param> public SessionController( ReattachInformation reattachInformation, IProcess process, IByondExecutableLock byondLock, ITopicClient byondTopicSender, IChatTrackingContext chatTrackingContext, IBridgeRegistrar bridgeRegistrar, IChatManager chat, IAssemblyInformationProvider assemblyInformationProvider, ILogger <SessionController> logger, uint?startupTimeout, bool reattached) { this.reattachInformation = reattachInformation ?? throw new ArgumentNullException(nameof(reattachInformation)); this.process = process ?? throw new ArgumentNullException(nameof(process)); this.byondLock = byondLock ?? throw new ArgumentNullException(nameof(byondLock)); this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); this.chatTrackingContext = chatTrackingContext ?? throw new ArgumentNullException(nameof(chatTrackingContext)); bridgeRegistration = bridgeRegistrar?.RegisterHandler(this) ?? throw new ArgumentNullException(nameof(bridgeRegistrar)); this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); this.chatTrackingContext.SetChannelSink(this); portClosedForReboot = false; disposed = false; apiValidationStatus = ApiValidationStatus.NeverValidated; released = false; rebootTcs = new TaskCompletionSource <object>(); primeTcs = new TaskCompletionSource <object>(); reattachTopicCts = new CancellationTokenSource(); synchronizationLock = new object(); _ = process.Lifetime.ContinueWith( x => { if (!disposed) { reattachTopicCts.Cancel(); } chatTrackingContext.Active = false; }, TaskScheduler.Current); LaunchResult = GetLaunchResult( assemblyInformationProvider, startupTimeout, reattached); logger.LogDebug("Created session controller. Primary: {0}, CommsKey: {1}, Port: {2}", IsPrimary, reattachInformation.AccessIdentifier, Port); }
/// <summary> /// Construct a <see cref="SessionController"/> /// </summary> /// <param name="reattachInformation">The value of <see cref="reattachInformation"/></param> /// <param name="metadata">The owning <see cref="Instance"/>.</param> /// <param name="process">The value of <see cref="process"/></param> /// <param name="byondLock">The value of <see cref="byondLock"/></param> /// <param name="byondTopicSender">The value of <see cref="byondTopicSender"/></param> /// <param name="bridgeRegistrar">The <see cref="IBridgeRegistrar"/> used to populate <see cref="bridgeRegistration"/>.</param> /// <param name="chat">The value of <see cref="chat"/></param> /// <param name="chatTrackingContext">The value of <see cref="chatTrackingContext"/></param> /// <param name="assemblyInformationProvider">The <see cref="IAssemblyInformationProvider"/> for the <see cref="SessionController"/>.</param> /// <param name="logger">The value of <see cref="logger"/></param> /// <param name="postLifetimeCallback">The <see cref="Func{TResult}"/> returning a <see cref="Task"/> to be run after the <paramref name="process"/> ends.</param> /// <param name="startupTimeout">The optional time to wait before failing the <see cref="LaunchResult"/></param> /// <param name="reattached">If this is a reattached session.</param> /// <param name="apiValidate">If this is a DMAPI validation session.</param> public SessionController( ReattachInformation reattachInformation, Api.Models.Instance metadata, IProcess process, IByondExecutableLock byondLock, ITopicClient byondTopicSender, IChatTrackingContext chatTrackingContext, IBridgeRegistrar bridgeRegistrar, IChatManager chat, IAssemblyInformationProvider assemblyInformationProvider, ILogger <SessionController> logger, Func <Task> postLifetimeCallback, uint?startupTimeout, bool reattached, bool apiValidate) { this.reattachInformation = reattachInformation ?? throw new ArgumentNullException(nameof(reattachInformation)); this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); this.process = process ?? throw new ArgumentNullException(nameof(process)); this.byondLock = byondLock ?? throw new ArgumentNullException(nameof(byondLock)); this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); this.chatTrackingContext = chatTrackingContext ?? throw new ArgumentNullException(nameof(chatTrackingContext)); if (bridgeRegistrar == null) { throw new ArgumentNullException(nameof(bridgeRegistrar)); } this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); portClosedForReboot = false; disposed = false; apiValidationStatus = ApiValidationStatus.NeverValidated; released = false; rebootTcs = new TaskCompletionSource <object>(); primeTcs = new TaskCompletionSource <object>(); initialBridgeRequestTcs = new TaskCompletionSource <object>(); reattachTopicCts = new CancellationTokenSource(); synchronizationLock = new object(); if (apiValidate || DMApiAvailable) { bridgeRegistration = bridgeRegistrar.RegisterHandler(this); this.chatTrackingContext.SetChannelSink(this); } else { logger.LogTrace( "Not registering session with {0} DMAPI version for interop!", reattachInformation.Dmb.CompileJob.DMApiVersion == null ? "no" : $"incompatible ({reattachInformation.Dmb.CompileJob.DMApiVersion})"); } async Task <int> WrapLifetime() { var exitCode = await process.Lifetime.ConfigureAwait(false); await postLifetimeCallback().ConfigureAwait(false); return(exitCode); } Lifetime = WrapLifetime(); LaunchResult = GetLaunchResult( assemblyInformationProvider, startupTimeout, reattached, apiValidate); logger.LogDebug( "Created session controller. CommsKey: {0}, Port: {1}", reattachInformation.AccessIdentifier, reattachInformation.Port); }
/// <inheritdoc /> #pragma warning disable CA1506 // TODO: Decomplexify public async Task <IInstance> CreateInstance(IBridgeRegistrar bridgeRegistrar, Models.Instance metadata) { // Create the ioManager for the instance var instanceIoManager = new ResolvingIOManager(ioManager, metadata.Path); // various other ioManagers var repoIoManager = new ResolvingIOManager(instanceIoManager, "Repository"); var byondIOManager = new ResolvingIOManager(instanceIoManager, "Byond"); var gameIoManager = new ResolvingIOManager(instanceIoManager, "Game"); var diagnosticsIOManager = new ResolvingIOManager(instanceIoManager, "Diagnostics"); var configurationIoManager = new ResolvingIOManager(instanceIoManager, "Configuration"); var configuration = new StaticFiles.Configuration( configurationIoManager, synchronousIOManager, symlinkFactory, processExecutor, postWriteHandler, platformIdentifier, fileTransferService, loggerFactory.CreateLogger <StaticFiles.Configuration>()); var eventConsumer = new EventConsumer(configuration); var repoManager = new RepositoryManager( repositoryFactory, repositoryCommands, repoIoManager, eventConsumer, gitRemoteFeaturesFactory, loggerFactory.CreateLogger <Repository.Repository>(), loggerFactory.CreateLogger <RepositoryManager>()); try { var byond = new ByondManager(byondIOManager, byondInstaller, eventConsumer, loggerFactory.CreateLogger <ByondManager>()); var commandFactory = new CommandFactory(assemblyInformationProvider, byond, repoManager, databaseContextFactory, metadata); var chatManager = chatFactory.CreateChatManager(instanceIoManager, commandFactory, metadata.ChatSettings); try { var sessionControllerFactory = new SessionControllerFactory( processExecutor, byond, topicClientFactory, cryptographySuite, assemblyInformationProvider, gameIoManager, chatManager, networkPromptReaper, platformIdentifier, bridgeRegistrar, serverPortProvider, eventConsumer, loggerFactory, loggerFactory.CreateLogger <SessionControllerFactory>(), metadata); var dmbFactory = new DmbFactory( databaseContextFactory, gameIoManager, remoteDeploymentManagerFactory, loggerFactory.CreateLogger <DmbFactory>(), metadata); try { var reattachInfoHandler = new SessionPersistor( databaseContextFactory, dmbFactory, processExecutor, loggerFactory.CreateLogger <SessionPersistor>(), metadata); var watchdog = watchdogFactory.CreateWatchdog( chatManager, dmbFactory, reattachInfoHandler, sessionControllerFactory, gameIoManager, diagnosticsIOManager, eventConsumer, remoteDeploymentManagerFactory, metadata, metadata.DreamDaemonSettings); eventConsumer.SetWatchdog(watchdog); commandFactory.SetWatchdog(watchdog); try { Instance instance = null; var dreamMaker = new DreamMaker( byond, gameIoManager, configuration, sessionControllerFactory, eventConsumer, chatManager, processExecutor, dmbFactory, repoManager, remoteDeploymentManagerFactory, loggerFactory.CreateLogger <DreamMaker>(), metadata); instance = new Instance( metadata, repoManager, byond, dreamMaker, watchdog, chatManager, configuration, dmbFactory, jobManager, eventConsumer, remoteDeploymentManagerFactory, loggerFactory.CreateLogger <Instance>()); return(instance); } catch { await watchdog.DisposeAsync().ConfigureAwait(false); throw; } } catch { dmbFactory.Dispose(); throw; } } catch { await chatManager.DisposeAsync().ConfigureAwait(false); throw; } } catch { repoManager.Dispose(); throw; } }