2using System.Collections.Generic;
 
    3using System.Globalization;
 
    5using System.Net.Sockets;
 
    8using System.Threading.Tasks;
 
   10using Microsoft.Extensions.Logging;
 
   11using Microsoft.Extensions.Options;
 
  131        readonly ILogger<SessionControllerFactory> 
logger;
 
  157            logger.LogTrace(
"Bind test: {port}", port);
 
  161                const int MaxAttempts = 5;
 
  162                for (var i = 0; i < MaxAttempts; ++i)
 
  167                            logger.LogDebug(
"Clearing the socket took {iterations} attempts :/", i + 1);
 
  171                    catch (SocketException ex) when (
platformIdentifier.
IsWindows && ex.SocketErrorCode == SocketError.AddressAlreadyInUse && i < (MaxAttempts - 1))
 
  176            catch (SocketException ex) when (ex.SocketErrorCode == SocketError.AddressAlreadyInUse)
 
 
  221            IMetricFactory metricFactory,
 
  224            ILogger<SessionControllerFactory> 
logger,
 
  234            this.chat = 
chat ?? 
throw new ArgumentNullException(nameof(
chat));
 
  242            ArgumentNullException.ThrowIfNull(metricFactory);
 
  245            this.logger = 
logger ?? 
throw new ArgumentNullException(nameof(
logger));
 
  246            this.instance = 
instance ?? 
throw new ArgumentNullException(nameof(
instance));
 
  248            sessionsLaunched = metricFactory.CreateCounter(
"tgs_sessions_launched", 
"The number of game server processes created");
 
  249            lastSessionLaunch = metricFactory.CreateGauge(
"tgs_session_start_time", 
"The UTC unix timestamp the most recent session was started");
 
 
  253        #pragma warning disable CA1506  
  259            CancellationToken cancellationToken)
 
  261            logger.LogTrace(
"Begin session launch...");
 
  262            if (!launchParameters.
Port.HasValue)
 
  263                throw new InvalidOperationException(
"Given port is null!");
 
  272                        logger.LogTrace(
"Boosting security level to minimum of Safe");
 
  279                        logger.LogTrace(
"Boosting security level to minimum of Trusted");
 
  284                    throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, 
"Invalid DreamDaemonSecurity value: {0}", dmbProvider.
CompileJob.
MinimumSecurityLevel));
 
  295                    "Launching session with CompileJob {compileJobId}...",
 
  303                await 
PortBindTest(launchParameters.
Port.Value, engineType, cancellationToken);
 
  305                string? outputFilePath = 
null;
 
  306                var preserveLogFile = 
true;
 
  308                var hasStandardOutput = engineLock.HasStandardOutput;
 
  311                    var now = DateTimeOffset.UtcNow;
 
  317                            $
"server-utc-{now.ToString("yyyy-MM-dd-HH-mm-ss
", CultureInfo.InvariantCulture)}{(apiValidate ? "-dmapi
" : String.Empty)}.log"));
 
  319                    logger.LogInformation(
"Logging server output to {path}...", outputFilePath);
 
  321                else if (!hasStandardOutput)
 
  324                    preserveLogFile = 
false;
 
  330                    logger.LogDebug(
"Session will have no DMAPI support!");
 
  360                            launchParameters.
Port.Value);
 
  363                            TimeSpan.FromMilliseconds(
 
  385                                CancellationToken.None), 
 
  396                        return sessionController;
 
  400                        chatTrackingContext.Dispose();
 
  406                    await 
using (process)
 
  409                        await process.Lifetime;
 
  416                if (currentByondLock == 
null)
 
  417                    engineLock.Dispose();
 
 
  421#pragma warning restore CA1506 
  424        public async ValueTask<ISessionController?> 
Reattach(
 
  426            CancellationToken cancellationToken)
 
  428            ArgumentNullException.ThrowIfNull(reattachInformation);
 
  430            logger.LogTrace(
"Begin session reattach...");
 
  440                    "Attaching to session PID: {pid}, CompileJob: {compileJobId}...",
 
  450                    if (engineLock.PromptsForNetworkAccess)
 
  457                            reattachInformation.
Dmb,
 
  478                            () => ValueTask.CompletedTask,
 
  485                        chatTrackingContext = 
null;
 
  491                        chatTrackingContext?.Dispose();
 
  498                        await process.DisposeAsync();
 
  505                engineLock?.Dispose();
 
 
  525            string accessIdentifier,
 
  528            CancellationToken cancellationToken)
 
  530            var serverMayHaveDMApi = apiValidate || dmbProvider.CompileJob.DMApiVersion != 
null;
 
  532            var serverArguments = serverMayHaveDMApi
 
  533                ? 
new Dictionary<string, string>
 
  541            var environment = await engineLock.
LoadEnv(
logger, 
false, cancellationToken);
 
  555                    Enumerable.Empty<
string?>(),
 
  574                    if (sessionConfiguration.HighPriorityLiveDreamDaemon)
 
  575                        process.AdjustPriority(
true);
 
  577                else if (sessionConfiguration.LowPriorityDeploymentProcesses)
 
  578                    process.AdjustPriority(
false);
 
  588                            process.Id.ToString(CultureInfo.InvariantCulture),
 
  597                await 
using (process)
 
  600                    await process.Lifetime;
 
 
  615        async ValueTask 
LogDDOutput(
IProcess process, 
string? outputFilePath, 
bool cliSupported, 
bool preserveFile, CancellationToken cancellationToken)
 
  619                string? ddOutput = 
null;
 
  623                if (String.IsNullOrWhiteSpace(ddOutput) && outputFilePath != 
null)
 
  626                        var dreamDaemonLogBytes = await gameIOManager.ReadAllBytes(
 
  630                        ddOutput = Encoding.UTF8.GetString(dreamDaemonLogBytes);
 
  637                                logger.LogTrace(
"Deleting temporary log file {path}...", outputFilePath);
 
  638                                await gameIOManager.DeleteFile(outputFilePath, cancellationToken);
 
  644                                logger.LogWarning(ex, 
"Failed to delete server log file {outputFilePath}!", outputFilePath);
 
  649                    "Server Output:{newLine}{output}",
 
  655                logger.LogWarning(ex, 
"Error reading server output!");
 
 
  673            bool apiValidateOnly)
 
  677                assemblyInformationProvider.Version,
 
  681                serverPortProvider.HttpApiPort,
 
  690            if (!platformIdentifier.IsWindows)
 
  693            await 
using var otherProcess = processExecutor.GetProcessByName(
"byond");
 
  694            if (otherProcess == 
null)
 
  697            var otherUsername = otherProcess.GetExecutingUsername();
 
  699            await 
using var ourProcess = processExecutor.GetCurrentProcess();
 
  700            var ourUsername = ourProcess.GetExecutingUsername();
 
  702            if (otherUsername.Equals(ourUsername, StringComparison.Ordinal))
 
 
 
EngineType? Engine
The EngineType.
 
virtual ? long Id
The ID of the entity.
 
Metadata about a server instance.
 
virtual ? Version DMApiVersion
The DMAPI Version.
 
DreamDaemonSecurity? MinimumSecurityLevel
The minimum DreamDaemonSecurity required to run the CompileJob's output.
 
Launch settings for DreamDaemon.
 
ushort? Port
The port DreamDaemon uses. This should be publically accessible.
 
DreamDaemonVisibility? Visibility
The DreamDaemonVisibility level of DreamDaemon. No-op for EngineType.OpenDream.
 
bool? LogOutput
If process output/error text should be logged.
 
uint? TopicRequestTimeout
The timeout for sending and receiving BYOND topics in milliseconds.
 
uint? StartupTimeout
The DreamDaemon startup timeout in seconds.
 
DreamDaemonSecurity? SecurityLevel
The DreamDaemonSecurity level of DreamDaemon. No-op for EngineType.OpenDream.
 
Constants used for communication with the DMAPI.
 
const string ParamServerPort
Identifies the Core.IServerPortProvider.HttpApiPort of the server.
 
const string ParamAccessIdentifier
Identifies the DMApiParameters.AccessIdentifier for the session.
 
const string ParamApiVersion
Identifies a DMAPI execution with the version as the value.
 
static readonly Version InteropVersion
The DMAPI InteropVersion being used.
 
readonly IEventConsumer eventConsumer
The IEventConsumer for the SessionControllerFactory.
 
readonly ITopicClientFactory topicClientFactory
The ITopicClientFactory for the SessionControllerFactory.
 
readonly IServerPortProvider serverPortProvider
The IServerPortProvider for the SessionControllerFactory.
 
readonly ILoggerFactory loggerFactory
The ILoggerFactory for the SessionControllerFactory.
 
readonly IProcessExecutor processExecutor
The IProcessExecutor for the SessionControllerFactory.
 
readonly IIOManager gameIOManager
The IIOManager for the Game directory.
 
async ValueTask CheckPagerIsNotRunning()
Make sure the BYOND pager is not running.
 
readonly IPlatformIdentifier platformIdentifier
The IPlatformIdentifier for the SessionControllerFactory.
 
async ValueTask LogDDOutput(IProcess process, string? outputFilePath, bool cliSupported, bool preserveFile, CancellationToken cancellationToken)
Attempts to log DreamDaemon output.
 
RuntimeInformation CreateRuntimeInformation(IDmbProvider dmbProvider, IChatTrackingContext chatTrackingContext, DreamDaemonSecurity securityLevel, DreamDaemonVisibility visibility, bool apiValidateOnly)
Create RuntimeInformation.
 
readonly ICryptographySuite cryptographySuite
The ICryptographySuite for the SessionControllerFactory.
 
async ValueTask< IProcess > CreateGameServerProcess(IDmbProvider dmbProvider, IEngineExecutableLock engineLock, DreamDaemonLaunchParameters launchParameters, string accessIdentifier, string? logFilePath, bool apiValidate, CancellationToken cancellationToken)
Creates the game server IProcess.
 
readonly IBridgeRegistrar bridgeRegistrar
The IBridgeRegistrar for the SessionControllerFactory.
 
async ValueTask PortBindTest(ushort port, EngineType engineType, CancellationToken cancellationToken)
Check if a given port  can be bound to.
 
const string DreamDaemonLogsPath
Path in Diagnostics folder to DreamDaemon logs.
 
readonly Counter sessionsLaunched
The number of sessions launched.
 
readonly Api.Models.Instance instance
The Api.Models.Instance for the SessionControllerFactory.
 
readonly INetworkPromptReaper networkPromptReaper
The INetworkPromptReaper for the SessionControllerFactory.
 
readonly ILogger< SessionControllerFactory > logger
The ILogger for the SessionControllerFactory.
 
readonly IChatManager chat
The IChatManager for the SessionControllerFactory.
 
readonly IAssemblyInformationProvider assemblyInformationProvider
The IAssemblyInformationProvider for the SessionControllerFactory.
 
async ValueTask< ISessionController > LaunchNew(IDmbProvider dmbProvider, IEngineExecutableLock? currentByondLock, DreamDaemonLaunchParameters launchParameters, bool apiValidate, CancellationToken cancellationToken)
Create a ISessionController from a freshly launch DreamDaemon instance.A ValueTask<TResult> resulting...
 
readonly IDotnetDumpService dotnetDumpService
The IDotnetDumpService for the SessionControllerFactory.
 
readonly IEngineManager engineManager
The IEngineManager for the SessionControllerFactory.
 
SessionControllerFactory(IProcessExecutor processExecutor, IEngineManager engineManager, ITopicClientFactory topicClientFactory, ICryptographySuite cryptographySuite, IAssemblyInformationProvider assemblyInformationProvider, IIOManager gameIOManager, IIOManager diagnosticsIOManager, IChatManager chat, INetworkPromptReaper networkPromptReaper, IPlatformIdentifier platformIdentifier, IBridgeRegistrar bridgeRegistrar, IServerPortProvider serverPortProvider, IEventConsumer eventConsumer, IAsyncDelayer asyncDelayer, IDotnetDumpService dotnetDumpService, IMetricFactory metricFactory, ILoggerFactory loggerFactory, IOptionsMonitor< SessionConfiguration > sessionConfigurationOptions, ILogger< SessionControllerFactory > logger, Api.Models.Instance instance)
Initializes a new instance of the SessionControllerFactory class.
 
async ValueTask< ISessionController?> Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken)
Create a ISessionController from an existing DreamDaemon instance.A ValueTask<TResult> resulting in a...
 
readonly Gauge lastSessionLaunch
The time the current session was launched.
 
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the SessionControllerFactory.
 
readonly IOptionsMonitor< SessionConfiguration > sessionConfigurationOptions
The IOptionsMonitor<TOptions> of SessionConfiguration for the SessionControllerFactory.
 
readonly IIOManager diagnosticsIOManager
The IIOManager for the Diagnostics directory.
 
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 .
 
Operation exceptions thrown from the context of a Models.Job.
 
For managing connected chat services.
 
IChatTrackingContext CreateTrackingContext()
Start tracking Commands.CustomCommands and ChannelRepresentations.
 
Represents a tracking of dynamic chat json files.
 
Provides absolute paths to the latest compiled .dmbs.
 
EngineVersion EngineVersion
The Api.Models.EngineVersion used to build the .dmb.
 
string Directory
The primary game directory.
 
string DmbName
The file name of the .dmb.
 
Models.CompileJob CompileJob
The CompileJob of the .dmb.
 
Represents usage of the two primary BYOND server executables.
 
string ServerExePath
The full path to the game server executable.
 
string FormatServerArguments(IDmbProvider dmbProvider, IReadOnlyDictionary< string, string >? parameters, DreamDaemonLaunchParameters launchParameters, string accessIdentifier, string? logFilePath)
Return the command line arguments for launching with given launchParameters .
 
ValueTask< Dictionary< string, string >?> LoadEnv(ILogger logger, bool forCompiler, CancellationToken cancellationToken)
Loads the environment settings for either the server or compiler.
 
bool PreferFileLogging
If HasStandardOutput is set, this indicates that the engine server has good file logging that should ...
 
bool HasStandardOutput
If ServerExePath supports being run as a command-line application and outputs log information to be c...
 
For managing the engine installations.
 
ValueTask< IEngineExecutableLock > UseExecutables(EngineVersion? requiredVersion, string? trustDmbFullPath, CancellationToken cancellationToken)
Lock the current installation's location and return a IEngineExecutableLock.
 
Consumes EventTypes and takes the appropriate actions.
 
ValueTask HandleEvent(EventType eventType, IEnumerable< string?> parameters, bool deploymentPipeline, CancellationToken cancellationToken)
Handle a given eventType .
 
Registers IBridgeHandlers.
 
Factory for ISessionControllers.
 
Factory for ITopicClients.
 
ITopicClient CreateTopicClient(TimeSpan timeout)
Create a ITopicClient.
 
Provides access to the server's HttpApiPort.
 
ushort HttpApiPort
The port the server listens on.
 
Interface for using filesystems.
 
string ResolvePath()
Retrieve the full path of the current working directory.
 
string ConcatPath(params string[] paths)
Combines an array of strings into a path.
 
Task CreateDirectory(string path, CancellationToken cancellationToken)
Create a directory at path .
 
Contains various cryptographic functions.
 
string GetSecureString()
Generates a 40-length secure ascii string.
 
Service for managing the dotnet-dump installation.
 
On Windows, DreamDaemon will show an unskippable prompt when using /world/proc/OpenPort()....
 
void RegisterProcess(IProcess process)
Register a given process  for network prompt reaping.
 
ValueTask< IProcess > LaunchProcess(string fileName, string workingDirectory, string arguments, CancellationToken cancellationToken, IReadOnlyDictionary< string, string >? environment=null, string? fileRedirect=null, bool readStandardHandles=false, bool noShellExecute=false)
Launch a IProcess.
 
IProcess? GetProcess(int id)
Get a IProcess by id .
 
Abstraction over a global::System.Diagnostics.Process.
 
Task< string?> GetCombinedOutput(CancellationToken cancellationToken)
Get the stderr and stdout output of the IProcess.
 
For waiting asynchronously.
 
ValueTask Delay(TimeSpan timeSpan, CancellationToken cancellationToken)
Create a Task that completes after a given timeSpan .
 
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
 
DreamDaemonVisibility
The visibility setting for DreamDaemon.
 
DreamDaemonSecurity
DreamDaemon's security level.
 
EngineType
The type of engine the codebase is using.
 
EventType
Types of events. Mirror in tgs.dm. Prefer last listed name for script.