예제 #1
0
 /// <inheritdoc />
 protected override void DisposeAndNullControllersImpl()
 {
     Server?.Dispose();
     Server  = null;
     Running = false;
     gracefulRebootRequired = false;
 }
예제 #2
0
        /// <inheritdoc />
        protected override async Task InitControllers(
            Task chatTask,
            ReattachInformation reattachInfo,
            CancellationToken cancellationToken)
        {
            // don't need a new dmb if reattaching
            var reattachInProgress = reattachInfo != null;
            var dmbToUse           = reattachInProgress ? null : DmbFactory.LockNextDmb(1);

            // if this try catches something, both servers are killed
            try
            {
                // start the alpha server task, either by launch a new process or attaching to an existing one
                // The tasks returned are mainly for writing interop files to the directories among other things and should generally never fail
                // The tasks pertaining to server startup times are in the ISessionControllers
                Task <ISessionController> serverLaunchTask;
                if (!reattachInProgress)
                {
                    dmbToUse = await PrepServerForLaunch(dmbToUse, cancellationToken).ConfigureAwait(false);

                    serverLaunchTask = SessionControllerFactory.LaunchNew(
                        dmbToUse,
                        null,
                        ActiveLaunchParameters,
                        false,
                        cancellationToken);
                }
                else
                {
                    serverLaunchTask = SessionControllerFactory.Reattach(reattachInfo, cancellationToken);
                }

                // retrieve the session controller
                Server = await serverLaunchTask.ConfigureAwait(false);

                // failed reattaches will return null
                Server?.SetHighPriority();

                // possiblity of null servers due to failed reattaches
                if (Server == null)
                {
                    await ReattachFailure(
                        chatTask,
                        cancellationToken)
                    .ConfigureAwait(false);

                    return;
                }

                await CheckLaunchResult(Server, "Server", cancellationToken).ConfigureAwait(false);

                Server.EnableCustomChatCommands();
            }
            catch
            {
                // kill the controllers
                bool serverWasActive = Server != null;

                // DCT: Operation must always run
                await DisposeAndNullControllers(default).ConfigureAwait(false);
예제 #3
0
 internal NetworkHandler(
     IHandleProvider provider,
     ISessionController sessionController)
 {
     _handleProvider    = provider;
     _sessionController = sessionController;
 }
예제 #4
0
        public void Dispose()
        {
            _disposed = true;

            StopAutomaticProgress();

            var controllers = SessionControllers.ToList();

            SessionControllers = new ISessionController[] { };

            foreach (var controller in controllers)
            {
                var disposable = controller as IDisposable;

                if (disposable != null)
                {
                    _logger.Debug("Disposing session controller {0}", disposable.GetType().Name);

                    try
                    {
                        disposable.Dispose();
                    }
                    catch (Exception ex)
                    {
                        _logger.ErrorException("Error disposing session controller", ex);
                    }
                }
            }

            _sessionManager = null;
        }
예제 #5
0
 public CharacterLayoutHostView()
 {
     InitializeComponent();
     _lastToken = null;
     _session   = new MMXSessionController(new CharacterInfo(), new MudRevBBS());
     _socket    = new TelnetSocket("MajorMud.DontExist.com");
 }
예제 #6
0
        public void AddController(ISessionController controller)
        {
            var controllers = SessionControllers.ToList();

            controllers.Add(controller);
            SessionControllers = controllers.ToArray();
        }
예제 #7
0
 /// <summary>
 /// Call <see cref="IDisposable.Dispose"/> on <see cref="alphaServer"/> and <see cref="bravoServer"/> and set them to <see langword="null"/>
 /// </summary>
 void DisposeAndNullControllers()
 {
     alphaServer?.Dispose();
     alphaServer = null;
     bravoServer?.Dispose();
     bravoServer = null;
     Running     = false;
 }
예제 #8
0
        internal HandshakeDecoder(
            IPlayerController playerController,
            ISessionController sessionController)
        {
            _playerController  = playerController;
            _sessionController = sessionController;

            status = HandshakeStatus.Handshake;
        }
예제 #9
0
        public SessionInfo(ISessionManager sessionManager, ILogger logger)
        {
            _sessionManager = sessionManager;
            _logger         = logger;

            AdditionalUsers    = new SessionUserInfo[] { };
            PlayState          = new PlayerStateInfo();
            SessionControllers = new ISessionController[] { };
        }
예제 #10
0
 public NetworkInitializer(
     IHandleProvider provider,
     IPlayerController playerController,
     ISessionController sessionController)
 {
     _handleProvider    = provider;
     _playerController  = playerController;
     _sessionController = sessionController;
 }
예제 #11
0
        /// <summary>
        /// Initialises a new instance of the <see cref="AccountController"/> class. 
        /// </summary>
        /// <param name="log">Logging module</param>
        /// <param name="memberDataAccess">Member data access</param>
        /// <param name="sessionController">Session controller</param>
        /// <param name="forgotPassword">Forgot password</param>
        /// <param name="dealDataAccess">Deal data access</param>
        /// <param name="hash">Hasher</param>
        /// <param name="emailSender">Email sender</param>
        /// <param name="currentUser">Current user</param>
        public AccountController(ILogger log, IMemberDataAccess memberDataAccess, ISessionController sessionController, IRecoverPassword forgotPassword, IDealDataAccess dealDataAccess, IHash hash, IEmailSender emailSender, ICurrentUser currentUser)
        {
            this.log = log;
            this.memberDataAccess = memberDataAccess;
            this.sessionController = sessionController;
            this.forgotPassword = forgotPassword;
            this.dealDataAccess = dealDataAccess;
            this.hash = hash;
            this.emailSender = emailSender;

            userName = currentUser.GetCurrentUser();
        }
 public RealSpaceEngineers(
     IObserver observer,
     ICharacterController controller,
     ISessionController sessionController,
     IItems items,
     IDefinitions definitions
     )
 {
     Observer    = observer;
     Character   = controller;
     Session     = sessionController;
     Items       = items;
     Definitions = definitions;
 }
예제 #13
0
        /// <summary>
        /// Check the <see cref="LaunchResult"/> of a given <paramref name="controller"/> for errors and throw a <see cref="JobException"/> if any are detected.
        /// </summary>
        /// <param name="controller">The <see cref="ISessionController"/> to checkou.</param>
        /// <param name="serverName">The name of the server being checked.</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
        /// <returns>A <see cref="Task"/> representing the running operation.</returns>
        protected async Task CheckLaunchResult(ISessionController controller, string serverName, CancellationToken cancellationToken)
        {
            var launchResult = await controller.LaunchResult.WithToken(cancellationToken).ConfigureAwait(false);

            // Dead sessions won't trigger this
            if (launchResult.ExitCode.HasValue)             // you killed us ray...
            {
                throw new JobException(String.Format(CultureInfo.InvariantCulture, "{0} failed to start: {1}", serverName, launchResult));
            }
            if (!launchResult.StartupTime.HasValue)
            {
                throw new JobException(String.Format(CultureInfo.InvariantCulture, "{0} timed out on startup: {1}s", serverName, ActiveLaunchParameters.StartupTimeout.Value));
            }
        }
예제 #14
0
        /// <inheritdoc />
        protected override async Task DisposeAndNullControllersImpl()
        {
            var disposeTask = Server?.DisposeAsync();

            gracefulRebootRequired = false;
            if (!disposeTask.HasValue)
            {
                return;
            }

            await disposeTask.Value.ConfigureAwait(false);

            Server = null;
        }
예제 #15
0
        public void Start()
        {
            if (this.session != null)
            {
                throw new InvalidOperationException("The session is already started.");
            }

            RealTimeTraceCollectorInfo info = new RealTimeTraceCollectorInfo(this.name);
            info.Providers.Add(new ProviderInfo(KernelProcessProviderId) { KeywordsAll = 0x10, Level = 4 });
            this.session = info.Create();
            this.session.Start();

            IObservable<EtwNativeEvent> stream = EtwObservable.FromSession(this.name);
            this.subscription = stream.Subscribe(e => this.OnNext(e));
        }
예제 #16
0
        public void Start()
        {
            if (this.session != null)
            {
                throw new InvalidOperationException("The session is already started.");
            }

            RealTimeTraceCollectorInfo info = new RealTimeTraceCollectorInfo(this.name);

            info.Providers.Add(new ProviderInfo(KernelProcessProviderId)
            {
                KeywordsAll = 0x10, Level = 4
            });
            this.session = info.Create();
            this.session.Start();

            IObservable <EtwNativeEvent> stream = EtwObservable.FromSession(this.name);

            this.subscription = stream.Subscribe(e => this.OnNext(e));
        }
예제 #17
0
        private static void CreateRealTimeTraceCollector()
        {
            RealTimeTraceCollectorInfo info = new RealTimeTraceCollectorInfo("MyRealTimeCollector");

            // Microsoft-Windows-Kernel-Process
            Guid providerId = new Guid("{22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}");

            info.Providers.Add(new ProviderInfo(providerId)
            {
                Level = 5
            });

            ISessionController controller = info.Create();

            controller.Start();

            Thread.Sleep(5000);

            controller.Stop();
        }
예제 #18
0
        /// <inheritdoc />
        protected sealed override async Task InitControllers(Action callBeforeRecurse, Task chatTask, DualReattachInformation reattachInfo, CancellationToken cancellationToken)
        {
            var serverToReattach = reattachInfo?.Alpha ?? reattachInfo?.Bravo;
            var serverToKill     = reattachInfo?.Bravo ?? reattachInfo?.Alpha;

            // vice versa
            if (serverToKill == serverToReattach)
            {
                serverToKill = null;
            }

            if (reattachInfo?.AlphaIsActive == false)
            {
                var temp = serverToReattach;
                serverToReattach = serverToKill;
                serverToKill     = temp;
            }

            // don't need a new dmb if reattaching
            var doesntNeedNewDmb = serverToReattach != null;
            var dmbToUse         = doesntNeedNewDmb ? null : DmbFactory.LockNextDmb(1);

            // if this try catches something, both servers are killed
            bool inactiveServerWasKilled = false;

            try
            {
                // start the alpha server task, either by launch a new process or attaching to an existing one
                // The tasks returned are mainly for writing interop files to the directories among other things and should generally never fail
                // The tasks pertaining to server startup times are in the ISessionControllers
                Task <ISessionController> serverLaunchTask, inactiveReattachTask;
                if (!doesntNeedNewDmb)
                {
                    dmbToUse = await PrepServerForLaunch(dmbToUse, cancellationToken).ConfigureAwait(false);

                    serverLaunchTask = SessionControllerFactory.LaunchNew(
                        dmbToUse,
                        null,
                        ActiveLaunchParameters,
                        true,
                        true,
                        false,
                        cancellationToken);
                }
                else
                {
                    serverLaunchTask = SessionControllerFactory.Reattach(serverToReattach, cancellationToken);
                }

                bool thereIsAnInactiveServerToKill = serverToKill != null;
                if (thereIsAnInactiveServerToKill)
                {
                    inactiveReattachTask = SessionControllerFactory.Reattach(serverToKill, cancellationToken);
                }
                else
                {
                    inactiveReattachTask = Task.FromResult <ISessionController>(null);
                }

                // retrieve the session controller
                Server = await serverLaunchTask.ConfigureAwait(false);

                // failed reattaches will return null
                Server?.SetHighPriority();

                var inactiveServerController = await inactiveReattachTask.ConfigureAwait(false);

                inactiveServerController?.Dispose();
                inactiveServerWasKilled = inactiveServerController != null;

                // possiblity of null servers due to failed reattaches
                if (Server == null)
                {
                    callBeforeRecurse();
                    await NotifyOfFailedReattach(thereIsAnInactiveServerToKill && !inactiveServerWasKilled, cancellationToken).ConfigureAwait(false);

                    return;
                }

                await CheckLaunchResult(Server, "Server", cancellationToken).ConfigureAwait(false);

                Server.EnableCustomChatCommands();
            }
            catch
            {
                // kill the controllers
                bool serverWasActive = Server != null;
                DisposeAndNullControllers();

                // server didn't get control of this dmb
                if (dmbToUse != null && !serverWasActive)
                {
                    dmbToUse.Dispose();
                }

                if (serverToKill != null && !inactiveServerWasKilled)
                {
                    serverToKill.Dmb.Dispose();
                }
                throw;
            }
        }
        /// <inheritdoc />
                #pragma warning disable CA1502 // TODO: Decomplexify
        protected override async Task MonitorLifetimes(CancellationToken cancellationToken)
        {
            Logger.LogTrace("Entered MonitorLifetimes");

            // this function is responsible for calling HandlerMonitorWakeup when necessary and manitaining the MonitorState
            var iteration = 1;

            for (var monitorState = new MonitorState(); monitorState.NextAction != MonitorAction.Exit; ++iteration)
            {
                // always start out with continue
                monitorState.NextAction = MonitorAction.Continue;

                // dump some info to the logs
                Logger.LogDebug("Iteration {0} of monitor loop", iteration);
                try
                {
                    if (AlphaIsActive)
                    {
                        Logger.LogDebug("Alpha is the active server");
                    }
                    else
                    {
                        Logger.LogDebug("Bravo is the active server");
                    }

                    if (monitorState.RebootingInactiveServer)
                    {
                        Logger.LogDebug("Inactive server is rebooting");
                    }

                    // update the monitor state with the inactive/active servers
                    monitorState.ActiveServer   = AlphaIsActive ? alphaServer : bravoServer;
                    monitorState.InactiveServer = AlphaIsActive ? bravoServer : alphaServer;

                    if (monitorState.ActiveServer.ClosePortOnReboot)
                    {
                        Logger.LogDebug("Active server will close port on reboot");
                    }
                    if (monitorState.InactiveServer.ClosePortOnReboot)
                    {
                        Logger.LogDebug("Inactive server will close port on reboot");
                    }

                    Logger.LogDebug("Active server Compile Job ID: {0}", monitorState.ActiveServer.Dmb.CompileJob.Id);
                    Logger.LogDebug("Inactive server Compile Job ID: {0}", monitorState.InactiveServer.Dmb.CompileJob.Id);

                    // load the activation tasks into local variables
                    Task activeServerLifetime          = monitorState.ActiveServer.Lifetime;
                    Task inactiveServerLifetime        = monitorState.InactiveServer.Lifetime;
                    var  activeServerReboot            = monitorState.ActiveServer.OnReboot;
                    var  inactiveServerReboot          = monitorState.InactiveServer.OnReboot;
                    Task inactiveServerStartup         = monitorState.RebootingInactiveServer ? monitorState.InactiveServer.LaunchResult : null;
                    Task activeLaunchParametersChanged = ActiveParametersUpdated.Task;
                    var  newDmbAvailable = DmbFactory.OnNewerDmb;

                    // cancel waiting if requested
                    var cancelTcs = new TaskCompletionSource <object>();
                    using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
                    {
                        var toWaitOn = Task.WhenAny(activeServerLifetime, inactiveServerLifetime, activeServerReboot, inactiveServerReboot, newDmbAvailable, cancelTcs.Task, activeLaunchParametersChanged);
                        if (monitorState.RebootingInactiveServer)
                        {
                            toWaitOn = Task.WhenAny(toWaitOn, inactiveServerStartup);
                        }

                        // wait for something to happen
                        await toWaitOn.ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested();
                    }

                    var chatTask = Task.CompletedTask;
                    using (await SemaphoreSlimContext.Lock(Semaphore, cancellationToken).ConfigureAwait(false))
                    {
                        // always run HandleMonitorWakeup from the context of the semaphore lock
                        // multiple things may have happened, handle them one at a time
                        for (var moreActivationsToProcess = true; moreActivationsToProcess && (monitorState.NextAction == MonitorAction.Continue || monitorState.NextAction == MonitorAction.Skip);)
                        {
                            MonitorActivationReason activationReason = default;                             // this will always be assigned before being used

                            // process the tasks in this order and call HandlerMonitorWakup for each
                            bool CheckActivationReason(ref Task task, MonitorActivationReason testActivationReason)
                            {
                                var taskCompleted = task?.IsCompleted == true;

                                task = null;
                                if (monitorState.NextAction == MonitorAction.Skip)
                                {
                                    monitorState.NextAction = MonitorAction.Continue;
                                }
                                else if (taskCompleted)
                                {
                                    activationReason = testActivationReason;
                                    return(true);
                                }

                                return(false);
                            }

                            if (CheckActivationReason(ref activeServerLifetime, MonitorActivationReason.ActiveServerCrashed) ||
                                CheckActivationReason(ref inactiveServerLifetime, MonitorActivationReason.InactiveServerCrashed) ||
                                CheckActivationReason(ref activeServerReboot, MonitorActivationReason.ActiveServerRebooted) ||
                                CheckActivationReason(ref inactiveServerReboot, MonitorActivationReason.InactiveServerRebooted) ||
                                CheckActivationReason(ref inactiveServerStartup, MonitorActivationReason.InactiveServerStartupComplete) ||
                                CheckActivationReason(ref newDmbAvailable, MonitorActivationReason.NewDmbAvailable) ||
                                CheckActivationReason(ref activeLaunchParametersChanged, MonitorActivationReason.ActiveLaunchParametersUpdated))
                            {
                                Logger.LogTrace("Monitor activation: {0}", activationReason);
                                await HandlerMonitorWakeup(activationReason, monitorState, cancellationToken).ConfigureAwait(false);
                            }
                            else
                            {
                                moreActivationsToProcess = false;
                            }
                        }

                        // writeback alphaServer and bravoServer from monitor state in case they changesd
                        alphaServer = AlphaIsActive ? monitorState.ActiveServer : monitorState.InactiveServer;
                        bravoServer = !AlphaIsActive ? monitorState.ActiveServer : monitorState.InactiveServer;
                    }

                    // full reboot required
                    if (monitorState.NextAction == MonitorAction.Restart)
                    {
                        Logger.LogDebug("Next state action is to restart");
                        DisposeAndNullControllers();
                        chatTask = Chat.SendWatchdogMessage("Restarting entirely due to complications...", cancellationToken);

                        for (var retryAttempts = 1; monitorState.NextAction == MonitorAction.Restart; ++retryAttempts)
                        {
                            Exception launchException = null;
                            using (await SemaphoreSlimContext.Lock(Semaphore, cancellationToken).ConfigureAwait(false))
                                try
                                {
                                    // use LaunchImplNoLock without announcements or restarting the monitor
                                    await LaunchImplNoLock(false, false, null, cancellationToken).ConfigureAwait(false);

                                    if (Running)
                                    {
                                        Logger.LogDebug("Relaunch successful, resetting monitor state...");
                                        monitorState = new MonitorState();                                         // clean the slate and continue
                                    }
                                }
                                catch (OperationCanceledException)
                                {
                                    throw;
                                }
                            catch (Exception e)
                            {
                                launchException = e;
                            }

                            await chatTask.ConfigureAwait(false);

                            if (!Running)
                            {
                                if (launchException == null)
                                {
                                    Logger.LogWarning("Failed to automatically restart the watchdog! Attempt: {0}", retryAttempts);
                                }
                                else
                                {
                                    Logger.LogWarning("Failed to automatically restart the watchdog! Attempt: {0}, Exception: {1}", retryAttempts, launchException);
                                }
                                var retryDelay = Math.Min(Math.Pow(2, retryAttempts), 3600);                                 // max of one hour, increasing by a power of 2 each time
                                chatTask = Chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Failed to restart watchdog (Attempt: {0}), retrying in {1} seconds...", retryAttempts, retryDelay), cancellationToken);
                                await Task.WhenAll(AsyncDelayer.Delay(TimeSpan.FromSeconds(retryDelay), cancellationToken), chatTask).ConfigureAwait(false);
                            }
                        }
                    }
                }
                catch (OperationCanceledException)
                {
                    Logger.LogDebug("Monitor cancelled");
                    break;
                }
                catch (Exception e)
                {
                    // really, this should NEVER happen
                    Logger.LogError("Monitor crashed! Iteration: {0}, State: {1}, Exception: {2}", iteration, JsonConvert.SerializeObject(monitorState), e);
                    await Chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Monitor crashed, this should NEVER happen! Please report this, full details in logs! Restarting monitor... Error: {0}", e.Message), cancellationToken).ConfigureAwait(false);
                }
            }

            Logger.LogTrace("Monitor exiting...");
        }
                #pragma warning restore CA1502

        /// <inheritdoc />
                #pragma warning disable CA1502 // TODO: Decomplexify
        protected override async Task InitControllers(Action callBeforeRecurse, Task chatTask, WatchdogReattachInformation reattachInfo, CancellationToken cancellationToken)
        {
            Debug.Assert(alphaServer == null && bravoServer == null, "Entered LaunchNoLock with one or more of the servers not being null!");

            // don't need a new dmb if reattaching
            var doesntNeedNewDmb = reattachInfo?.Alpha != null && reattachInfo?.Bravo != null;
            var dmbToUse         = doesntNeedNewDmb ? null : DmbFactory.LockNextDmb(2);

            // if this try catches something, both servers are killed
            try
            {
                // start the alpha server task, either by launch a new process or attaching to an existing one
                // The tasks returned are mainly for writing interop files to the directories among other things and should generally never fail
                // The tasks pertaining to server startup times are in the ISessionControllers
                Task <ISessionController> alphaServerTask;
                if (!doesntNeedNewDmb)
                {
                    alphaServerTask = SessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, true, true, false, cancellationToken);
                }
                else
                {
                    alphaServerTask = SessionControllerFactory.Reattach(reattachInfo.Alpha, cancellationToken);
                }

                // retrieve the session controller
                var startTime = DateTimeOffset.Now;
                alphaServer = await alphaServerTask.ConfigureAwait(false);

                // failed reattaches will return null
                alphaServer?.SetHighPriority();

                // extra delay for total ordering
                var now   = DateTimeOffset.Now;
                var delay = now - startTime;

                // definitely not if reattaching though
                if (reattachInfo == null && delay.TotalSeconds < AlphaBravoStartupSeperationInterval)
                {
                    await AsyncDelayer.Delay(startTime.AddSeconds(AlphaBravoStartupSeperationInterval) - now, cancellationToken).ConfigureAwait(false);
                }

                // now bring bravo up
                if (!doesntNeedNewDmb)
                {
                    bravoServer = await SessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, false, false, false, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    bravoServer = await SessionControllerFactory.Reattach(reattachInfo.Bravo, cancellationToken).ConfigureAwait(false);
                }

                // failed reattaches will return null
                bravoServer?.SetHighPriority();

                // possiblity of null servers due to failed reattaches
                if (alphaServer == null || bravoServer == null)
                {
                    await chatTask.ConfigureAwait(false);

                    var bothServersDead = alphaServer == null && bravoServer == null;
                    if (bothServersDead ||
                        (alphaServer == null && reattachInfo.AlphaIsActive) ||
                        (bravoServer == null && !reattachInfo.AlphaIsActive))
                    {
                        // we lost the active server, just restart entirely
                        DisposeAndNullControllers();
                        const string FailReattachMessage = "Unable to properly reattach to active server! Restarting...";
                        Logger.LogWarning(FailReattachMessage);
                        Logger.LogDebug(bothServersDead ? "Also could not reattach to inactive server!" : "Inactive server was reattached successfully!");
                        chatTask = Chat.SendWatchdogMessage(FailReattachMessage, cancellationToken);
                        callBeforeRecurse();
                        await LaunchImplNoLock(true, false, null, cancellationToken).ConfigureAwait(false);

                        await chatTask.ConfigureAwait(false);

                        return;
                    }

                    // we still have the active server but the other one is dead to us, hand it off to the monitor to restart
                    const string InactiveReattachFailureMessage = "Unable to reattach to inactive server. Leaving for monitor to reboot...";
                    chatTask = Chat.SendWatchdogMessage(InactiveReattachFailureMessage, cancellationToken);
                    Logger.LogWarning(InactiveReattachFailureMessage);

                    if (reattachInfo.AlphaIsActive)
                    {
                        bravoServer = SessionControllerFactory.CreateDeadSession(reattachInfo.Bravo.Dmb);
                    }
                    else
                    {
                        alphaServer = SessionControllerFactory.CreateDeadSession(reattachInfo.Alpha.Dmb);
                    }
                }

                var alphaLrt = CheckLaunchResult(alphaServer, "Alpha", cancellationToken);
                var bravoLrt = CheckLaunchResult(bravoServer, "Bravo", cancellationToken);

                // this task completes when both serers have finished booting
                var allTask = Task.WhenAll(alphaLrt, bravoLrt);

                await allTask.ConfigureAwait(false);

                // both servers are now running, alpha is the active server(unless reattach), huzzah
                alphaIsActive = reattachInfo?.AlphaIsActive ?? true;

                var activeServer = AlphaIsActive ? alphaServer : bravoServer;
                activeServer.EnableCustomChatCommands();
                activeServer.ClosePortOnReboot = true;
            }
            catch
            {
                if (dmbToUse != null)
                {
                    // we locked 2 dmbs
                    if (bravoServer == null)
                    {
                        // bravo didn't get control of his
                        dmbToUse.Dispose();
                        if (alphaServer == null)
                        {
                            dmbToUse.Dispose();                             // alpha didn't get control of his
                        }
                    }
                }
                else if (doesntNeedNewDmb)                 // we have reattachInfo
                {
                    if (bravoServer == null)
                    {
                        // bravo didn't get control of his
                        reattachInfo.Bravo?.Dmb.Dispose();
                        if (alphaServer == null)
                        {
                            reattachInfo.Alpha?.Dmb.Dispose();                             // alpha didn't get control of his
                        }
                    }
                }

                // kill the controllers
                DisposeAndNullControllers();
                throw;
            }
        }
예제 #21
0
 public GameModel(ISessionController <LogicGame> logicgame)
 {
     this.logic     = logicgame.LoadOrCreate(LogicGameKey.Key);
     this.logicgame = logicgame;
     refreshOptionsList();
 }
예제 #22
0
        async Task <WatchdogLaunchResult> LaunchNoLock(bool startMonitor, bool announce, bool doReattach, CancellationToken cancellationToken)
        {
            logger.LogTrace("Begin LaunchNoLock");
            using (var alphaStartCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
            {
                if (Running)
                {
                    logger.LogTrace("Aborted due to already running!");
                    return(null);
                }

                Task chatTask;
                //this is necessary, the monitor could be in it's sleep loop trying to restart
                if (startMonitor && await StopMonitor().ConfigureAwait(false))
                {
                    chatTask = chat.SendWatchdogMessage("Automatic retry sequence cancelled by manual launch. Restarting...", cancellationToken);
                }
                else if (announce)
                {
                    chatTask = chat.SendWatchdogMessage("Starting...", cancellationToken);
                }
                else
                {
                    chatTask = Task.CompletedTask;
                }
                //start both servers
                LastLaunchParameters = ActiveLaunchParameters;
                try
                {
                    //good ole sanity
                    if (alphaServer != null || bravoServer != null)
                    {
                        throw new InvalidOperationException("Entered LaunchNoLock with one or more of the servers not being null!");
                    }

                    var reattachInfo = doReattach ? await reattachInfoHandler.Load(cancellationToken).ConfigureAwait(false) : null;

                    var doesntNeedNewDmb = doReattach && reattachInfo.Alpha != null && reattachInfo.Bravo != null;
                    var dmbToUse         = doesntNeedNewDmb ? null : dmbFactory.LockNextDmb(2);

                    try
                    {
                        Task <ISessionController> alphaServerTask;
                        if (!doesntNeedNewDmb)
                        {
                            alphaServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, true, true, false, alphaStartCts.Token);
                        }
                        else
                        {
                            alphaServerTask = sessionControllerFactory.Reattach(reattachInfo.Alpha, cancellationToken);
                        }

                        //wait until this boy officially starts so as not to confuse the servers as to who came first
                        var startTime = DateTimeOffset.Now;
                        alphaServer = await alphaServerTask.ConfigureAwait(false);

                        alphaServer.SetHighPriority();

                        //extra delay for total ordering
                        var now   = DateTimeOffset.Now;
                        var delay = now - startTime;

                        if (delay.TotalSeconds < AlphaBravoStartupSeperationInterval)
                        {
                            await Task.Delay(startTime.AddSeconds(AlphaBravoStartupSeperationInterval) - now, cancellationToken).ConfigureAwait(false);
                        }

                        Task <ISessionController> bravoServerTask;
                        if (!doesntNeedNewDmb)
                        {
                            bravoServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, false, false, false, cancellationToken);
                        }
                        else
                        {
                            bravoServerTask = sessionControllerFactory.Reattach(reattachInfo.Bravo, cancellationToken);
                        }

                        bravoServer = await bravoServerTask.ConfigureAwait(false);

                        bravoServer.SetHighPriority();

                        async Task <LaunchResult> CheckLaunch(ISessionController controller, string serverName)
                        {
                            var launch = await controller.LaunchResult.ConfigureAwait(false);

                            if (launch.ExitCode.HasValue)
                            {
                                //you killed us ray...
                                throw new JobException(String.Format(CultureInfo.InvariantCulture, "{1} server failed to start: {0}", launch.ToString(), serverName));
                            }
                            if (!launch.StartupTime.HasValue)
                            {
                                throw new JobException(String.Format(CultureInfo.InvariantCulture, "{1} server timed out on startup: {0}s", launch.ToString(), ActiveLaunchParameters.StartupTimeout.Value));
                            }
                            return(launch);
                        }

                        var alphaLrt = CheckLaunch(alphaServer, "Alpha");
                        var bravoLrt = CheckLaunch(bravoServer, "Bravo");
                        //now we have two booting servers, get them up and running
                        var allTask = Task.WhenAll(alphaLrt, bravoLrt);

                        //don't forget about the cancelationToken
                        var cancelTcs = new TaskCompletionSource <object>();
                        using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
                            await Task.WhenAny(allTask, cancelTcs.Task).ConfigureAwait(false);
                        cancellationToken.ThrowIfCancellationRequested();

                        //both servers are now running, alpha is the active server, huzzah
                        AlphaIsActive    = doReattach ? reattachInfo.AlphaIsActive : true;
                        LastLaunchResult = alphaLrt.Result;
                        logger.LogInformation("Launched servers successfully");
                        Running = true;

                        if (startMonitor)
                        {
                            await StopMonitor().ConfigureAwait(false);

                            monitorCts  = new CancellationTokenSource();
                            monitorTask = MonitorLifetimes(monitorCts.Token);
                        }
                        return(new WatchdogLaunchResult
                        {
                            Alpha = alphaLrt.Result,
                            Bravo = bravoLrt.Result
                        });
                    }
                    catch
                    {
                        if (!doesntNeedNewDmb && (alphaServer == null && bravoServer == null))
                        {
                            dmbToUse.Dispose();                             //guaranteed to not be null here
                            dmbToUse.Dispose();                             //yes, dispose it twice. See the definition of IDmbFactory.LockNextDmb(), we called it with 2 locks
                        }
                        DisposeAndNullControllers();
                        throw;
                    }
                }
                catch (Exception e)
                {
                    logger.LogWarning("Failed to start watchdog: {0}", e.ToString());
                    throw;
                }
                finally
                {
                    try
                    {
                        await chatTask.ConfigureAwait(false);
                    }
                    catch (OperationCanceledException) { }
                }
            }
        }
예제 #23
0
        /// <summary>
        /// The loop that watches the watchdog
        /// </summary>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
        /// <returns>A <see cref="Task"/> representing the running operation</returns>
        async Task MonitorLifetimes(CancellationToken cancellationToken)
        {
            logger.LogTrace("Entered MonitorLifetimes");
            var iteration = 1;

            for (var monitorState = new MonitorState(); monitorState.NextAction != MonitorAction.Exit; ++iteration)
            {
                monitorState.NextAction = MonitorAction.Continue;
                logger.LogDebug("Iteration {0} of monitor loop", iteration);
                try
                {
                    if (AlphaIsActive)
                    {
                        logger.LogDebug("Alpha is the active server");
                    }
                    else
                    {
                        logger.LogDebug("Bravo is the active server");
                    }

                    if (monitorState.InactiveServerHasStagedDmb)
                    {
                        logger.LogDebug("Inactive server has staged .dmb");
                    }
                    if (monitorState.RebootingInactiveServer)
                    {
                        logger.LogDebug("Inactive server is rebooting");
                    }

                    monitorState.ActiveServer   = AlphaIsActive ? alphaServer : bravoServer;
                    monitorState.InactiveServer = AlphaIsActive ? bravoServer : alphaServer;

                    if (monitorState.ActiveServer.ClosePortOnReboot)
                    {
                        logger.LogDebug("Active server will close port on reboot");
                    }
                    if (monitorState.InactiveServer.ClosePortOnReboot)
                    {
                        logger.LogDebug("Inactive server will close port on reboot");
                    }

                    var activeServerLifetime          = monitorState.ActiveServer.Lifetime;
                    var inactiveServerLifetime        = monitorState.InactiveServer.Lifetime;
                    var activeServerReboot            = monitorState.ActiveServer.OnReboot;
                    var inactiveServerReboot          = monitorState.InactiveServer.OnReboot;
                    var inactiveServerStartup         = monitorState.RebootingInactiveServer ? monitorState.InactiveServer.LaunchResult : null;
                    var activeLaunchParametersChanged = activeParametersUpdated.Task;
                    var newDmbAvailable = dmbFactory.OnNewerDmb;

                    var cancelTcs = new TaskCompletionSource <object>();
                    using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
                    {
                        var toWaitOn = Task.WhenAny(activeServerLifetime, inactiveServerLifetime, activeServerReboot, inactiveServerReboot, newDmbAvailable, cancelTcs.Task, activeLaunchParametersChanged);
                        if (monitorState.RebootingInactiveServer)
                        {
                            toWaitOn = Task.WhenAny(toWaitOn, inactiveServerStartup);
                        }
                        await toWaitOn.ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested();
                    }

                    var chatTask = Task.CompletedTask;
                    using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
                    {
                        MonitorActivationReason activationReason = default;
                        //multiple things may have happened, handle them one at a time
                        for (var moreActivationsToProcess = true; moreActivationsToProcess && monitorState.NextAction == MonitorAction.Continue;)
                        {
                            if (activeServerLifetime?.IsCompleted == true)
                            {
                                activationReason     = MonitorActivationReason.ActiveServerCrashed;
                                activeServerLifetime = null;
                            }
                            else if (inactiveServerLifetime?.IsCompleted == true)
                            {
                                activationReason       = MonitorActivationReason.InactiveServerCrashed;
                                inactiveServerLifetime = null;
                            }
                            else if (activeServerReboot?.IsCompleted == true)
                            {
                                activationReason   = MonitorActivationReason.ActiveServerRebooted;
                                activeServerReboot = null;
                            }
                            else if (inactiveServerReboot?.IsCompleted == true)
                            {
                                activationReason     = MonitorActivationReason.InactiveServerRebooted;
                                inactiveServerReboot = null;
                            }
                            else if (inactiveServerStartup?.IsCompleted == true)
                            {
                                activationReason      = MonitorActivationReason.InactiveServerStartupComplete;
                                inactiveServerStartup = null;
                            }
                            else if (newDmbAvailable?.IsCompleted == true)
                            {
                                activationReason = MonitorActivationReason.NewDmbAvailable;
                                newDmbAvailable  = null;
                            }
                            else if (activeLaunchParametersChanged?.IsCompleted == true)
                            {
                                activationReason = MonitorActivationReason.ActiveLaunchParametersUpdated;
                                activeLaunchParametersChanged = null;
                            }
                            else
                            {
                                moreActivationsToProcess = false;
                            }

                            if (moreActivationsToProcess)
                            {
                                await HandlerMonitorWakeup(activationReason, monitorState, cancellationToken).ConfigureAwait(false);
                            }
                        }

                        //writeback alphaServer and bravoServer
                        alphaServer = AlphaIsActive ? monitorState.ActiveServer : monitorState.InactiveServer;
                        bravoServer = !AlphaIsActive ? monitorState.ActiveServer : monitorState.InactiveServer;
                    }

                    //full reboot required
                    if (monitorState.NextAction == MonitorAction.Restart)
                    {
                        logger.LogDebug("Next state action is to restart");
                        DisposeAndNullControllers();
                        chatTask = chat.SendWatchdogMessage("Restarting entirely due to complications...", cancellationToken);

                        for (var retryAttempts = 1; monitorState.NextAction == MonitorAction.Restart; ++retryAttempts)
                        {
                            WatchdogLaunchResult result;
                            using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
                            {
                                result = await LaunchNoLock(false, false, false, cancellationToken).ConfigureAwait(false);

                                if (Running)
                                {
                                    logger.LogDebug("Relaunch successful, resetting monitor state...");
                                    monitorState = new MonitorState();                                      //clean the slate
                                }
                            }

                            await chatTask.ConfigureAwait(false);

                            if (!Running)
                            {
                                logger.LogWarning("Failed to automatically restart the watchdog! Alpha: {0}; Bravo: {1}", result.Alpha.ToString(), result.Bravo.ToString());
                                var retryDelay = Math.Min(Math.Pow(2, retryAttempts), 3600);                                 //max of one hour
                                chatTask = chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Failed to restart watchdog (Attempt: {0}), retrying in {1} seconds...", retryAttempts, retryDelay), cancellationToken);
                                await Task.WhenAll(Task.Delay((int)retryDelay, cancellationToken), chatTask).ConfigureAwait(false);
                            }
                        }
                    }
                }
                catch (OperationCanceledException)
                {
                    logger.LogDebug("Monitor cancelled");
                    break;
                }
                catch (Exception e)
                {
                    logger.LogError("Monitor crashed! Iteration: {0}, State: {1}", iteration, JsonConvert.SerializeObject(monitorState));
                    await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Monitor crashed, this should NEVER happen! Please report this, full details in logs! Restarting monitor... Error: {0}", e.Message), cancellationToken).ConfigureAwait(false);
                }
            }
        }
예제 #24
0
 /// <summary>
 /// Initialises a new instance of the <see cref="CurrentUser"/> class. 
 /// </summary>
 /// <param name="log">Logging module</param>
 /// <param name="sessionController">Session controller</param>
 public CurrentUser(ILogger log, ISessionController sessionController)
 {
     this.log = log;
     this.sessionController = sessionController;
 }
 /// <inheritdoc />
 protected override void DisposeAndNullControllers()
 {
     Server?.Dispose();
     Server  = null;
     Running = false;
 }
예제 #26
0
 public SessionDispatcher(ISessionController sessionController)
 {
     m_sessionController = sessionController;
 }
예제 #27
0
 public IndexModel(ISessionController <LogicGame> logicgame)
 {
     this.logic     = logicgame.LoadOrCreate(LogicGameKey.Key);
     this.logicgame = logicgame;
 }
 static string ExitWord(ISessionController controller) => controller.TerminationWasRequested ? "exited" : "crashed";