public async Task <Process> StartGameAndAddon(Launcher.LoginResult loginResult, bool isSteam, bool forceNoDalamud)
    {
        var dalamudOk = false;

        IDalamudRunner             dalamudRunner;
        IDalamudCompatibilityCheck dalamudCompatCheck;

        switch (Environment.OSVersion.Platform)
        {
        case PlatformID.Win32NT:
            dalamudRunner      = new WindowsDalamudRunner();
            dalamudCompatCheck = new WindowsDalamudCompatibilityCheck();
            break;

        case PlatformID.Unix:
            dalamudRunner      = new UnixDalamudRunner(Program.CompatibilityTools, Program.DotnetRuntime);
            dalamudCompatCheck = new UnixDalamudCompatibilityCheck();
            break;

        default:
            throw new NotImplementedException();
        }

        var dalamudLauncher = new DalamudLauncher(dalamudRunner, Program.DalamudUpdater, App.Settings.DalamudLoadMethod.GetValueOrDefault(DalamudLoadMethod.DllInject),
                                                  App.Settings.GamePath, App.Storage.Root, App.Settings.ClientLanguage ?? ClientLanguage.English, App.Settings.DalamudLoadDelay);

        try
        {
            dalamudCompatCheck.EnsureCompatibility();
        }
        catch (IDalamudCompatibilityCheck.NoRedistsException ex)
        {
            Log.Error(ex, "No Dalamud Redists found");

            throw;

            /*
             * CustomMessageBox.Show(
             *  Loc.Localize("DalamudVc2019RedistError",
             *      "The XIVLauncher in-game addon needs the Microsoft Visual C++ 2015-2019 redistributable to be installed to continue. Please install it from the Microsoft homepage."),
             *  "XIVLauncher", MessageBoxButton.OK, MessageBoxImage.Exclamation, parentWindow: _window);
             */
        }
        catch (IDalamudCompatibilityCheck.ArchitectureNotSupportedException ex)
        {
            Log.Error(ex, "Architecture not supported");

            throw;

            /*
             * CustomMessageBox.Show(
             *  Loc.Localize("DalamudArchError",
             *      "Dalamud cannot run your computer's architecture. Please make sure that you are running a 64-bit version of Windows.\nIf you are using Windows on ARM, please make sure that x64-Emulation is enabled for XIVLauncher."),
             *  "XIVLauncher", MessageBoxButton.OK, MessageBoxImage.Exclamation, parentWindow: _window);
             */
        }

        if (App.Settings.DalamudEnabled.GetValueOrDefault(true) && !forceNoDalamud && App.Settings.IsDx11.GetValueOrDefault(true))
        {
            try
            {
                dalamudOk = dalamudLauncher.HoldForUpdate(App.Settings.GamePath);
            }
            catch (DalamudRunnerException ex)
            {
                Log.Error(ex, "Couldn't ensure Dalamud runner");

                var runnerErrorMessage = Loc.Localize("DalamudRunnerError",
                                                      "Could not launch Dalamud successfully. This might be caused by your antivirus.\nTo prevent this, please add an exception for the folder \"%AppData%\\XIVLauncher\\addons\".");

                throw;

                /*
                 * CustomMessageBox.Builder
                 *              .NewFrom(runnerErrorMessage)
                 *              .WithImage(MessageBoxImage.Error)
                 *              .WithButtons(MessageBoxButton.OK)
                 *              .WithShowHelpLinks()
                 *              .WithParentWindow(_window)
                 *              .Show();
                 */
            }
        }

        IGameRunner runner;

        var gameArgs = App.Settings.AdditionalArgs ?? string.Empty;

        if (Environment.OSVersion.Platform == PlatformID.Win32NT)
        {
            runner = new WindowsGameRunner(dalamudLauncher, dalamudOk, App.Settings.DalamudLoadMethod.GetValueOrDefault(DalamudLoadMethod.DllInject));
        }
        else if (Environment.OSVersion.Platform == PlatformID.Unix)
        {
            if (App.Settings.WineStartupType == WineStartupType.Custom && App.Settings.WineBinaryPath == null)
            {
                throw new Exception("Custom wine binary path wasn't set.");
            }

            var signal   = new ManualResetEvent(false);
            var isFailed = false;

            if (App.Settings.WineStartupType == WineStartupType.Managed)
            {
                var _ = Task.Run(async() =>
                {
                    await Program.CompatibilityTools.EnsureTool().ConfigureAwait(false);
                    Program.CompatibilityTools.EnsureGameFixes(Program.Config.GameConfigPath);
                }).ContinueWith(t =>
                {
                    isFailed = t.IsFaulted || t.IsCanceled;

                    if (isFailed)
                    {
                        Log.Error(t.Exception, "Couldn't ensure compatibility tool");
                    }

                    signal.Set();
                });

                App.StartLoading("Ensuring compatibility tool...");
                signal.WaitOne();
                signal.Dispose();

                if (isFailed)
                {
                    return(null);
                }
            }

            runner = new UnixGameRunner(Program.CompatibilityTools, dalamudLauncher, dalamudOk, App.Settings.DalamudLoadMethod, Program.DotnetRuntime, App.Storage);

            gameArgs += $" UserPath={Program.CompatibilityTools.UnixToWinePath(App.Settings.GameConfigPath.FullName)}";
            gameArgs  = gameArgs.Trim();
        }
        else
        {
            throw new NotImplementedException();
        }

        Hide();

        // We won't do any sanity checks here anymore, since that should be handled in StartLogin
        var launched = App.Launcher.LaunchGame(runner,
                                               loginResult.UniqueId,
                                               loginResult.OauthLogin.Region,
                                               loginResult.OauthLogin.MaxExpansion,
                                               isSteam,
                                               gameArgs,
                                               App.Settings.GamePath,
                                               App.Settings.IsDx11 ?? true,
                                               App.Settings.ClientLanguage.GetValueOrDefault(ClientLanguage.English),
                                               App.Settings.IsEncryptArgs.GetValueOrDefault(true),
                                               App.Settings.DpiAwareness.GetValueOrDefault(DpiAwareness.Unaware));

        if (launched == null)
        {
            Log.Information("GameProcess was null...");
            IsLoggingIn = false;
            return(null);
        }

        // This is a Windows process handle on Windows, a Wine pid on Unix-like systems
        var     gamePid = 0;
        Process?process = null;

        if (Environment.OSVersion.Platform == PlatformID.Win32NT)
        {
            process = launched as Process;
            gamePid = process !.Id;
        }
        else if (Environment.OSVersion.Platform == PlatformID.Unix)
        {
            gamePid = (int)launched;
        }

        var addonMgr = new AddonManager();

        try
        {
            App.Settings.Addons ??= new List <AddonEntry>();

            var addons = App.Settings.Addons.Where(x => x.IsEnabled).Select(x => x.Addon).Cast <IAddon>().ToList();

            addonMgr.RunAddons(gamePid, addons);
        }
        catch (Exception ex)
        {
            /*
             * CustomMessageBox.Builder
             *              .NewFrom(ex, "Addons")
             *              .WithAppendText("\n\n")
             *              .WithAppendText(Loc.Localize("AddonLoadError",
             *                  "This could be caused by your antivirus, please check its logs and add any needed exclusions."))
             *              .WithParentWindow(_window)
             *              .Show();
             */

            IsLoggingIn = false;

            addonMgr.StopAddons();
            throw;
        }

        Log.Debug("Waiting for game to exit");

        if (Environment.OSVersion.Platform == PlatformID.Win32NT)
        {
            await Task.Run(() => process !.WaitForExit()).ConfigureAwait(false);
        }
        else if (Environment.OSVersion.Platform == PlatformID.Unix)
        {
            Int32 unixPid = Program.CompatibilityTools.GetUnixProcessId(gamePid);
            if (unixPid == 0)
            {
                Log.Error("Could not retrive Unix process ID, this feature currently requires a patched wine version");
                while (Program.CompatibilityTools.GetProcessIds("ffxiv_dx11.exe").Contains(gamePid))
                {
                    Thread.Sleep(5000);
                }
            }
            else
            {
                process = Process.GetProcessById(unixPid);
                var handle = process.Handle;
                await Task.Run(() => process !.WaitForExit()).ConfigureAwait(false);
            }

            UnixGameRunner.RunningPids.Remove(gamePid);
        }
        else
        {
            Environment.Exit(0);
            return(null);
        }

        Log.Verbose("Game has exited");

        if (addonMgr.IsRunning)
        {
            addonMgr.StopAddons();
        }

        try
        {
            if (App.Steam.IsValid)
            {
                App.Steam.Shutdown();
            }
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Could not shut down Steam");
        }

        return(process !);
    }
    private async Task <bool> TryProcessLoginResult(Launcher.LoginResult loginResult, bool isSteam, LoginAction action)
    {
        if (loginResult.State == Launcher.LoginState.NoService)
        {
            /*
             * CustomMessageBox.Show(
             *  Loc.Localize("LoginNoServiceMessage",
             *      "This Square Enix account cannot play FINAL FANTASY XIV. Please make sure that you have an active subscription and that it is paid up.\n\nIf you bought FINAL FANTASY XIV on Steam, make sure to check the \"Use Steam service account\" checkbox while logging in.\nIf Auto-Login is enabled, hold shift while starting to access settings."),
             *  "Error",
             *  MessageBoxButton.OK, MessageBoxImage.Error, showHelpLinks: false, showDiscordLink: false, parentWindow: _window);
             */

            throw new Exception("No service account or subscription");

            return(false);
        }

        if (loginResult.State == Launcher.LoginState.NoTerms)
        {
            /*
             * CustomMessageBox.Show(
             *  Loc.Localize("LoginAcceptTermsMessage",
             *      "Please accept the FINAL FANTASY XIV Terms of Use in the official launcher."),
             *  "Error", MessageBoxButton.OK, MessageBoxImage.Error, showOfficialLauncher: true, parentWindow: _window);
             */

            throw new Exception("Need to accept terms of use");

            return(false);
        }

        /*
         * The server requested us to patch Boot, even though in order to get to this code, we just checked for boot patches.
         *
         * This means that something or someone modified boot binaries without our involvement.
         * We have no way to go back to a "known" good state other than to do a full reinstall.
         *
         * This has happened multiple times with users that have viruses that infect other EXEs and change their hashes, causing the update
         * server to reject our boot hashes.
         *
         * In the future we may be able to just delete /boot and run boot patches again, but this doesn't happen often enough to warrant the
         * complexity and if boot is f****d game probably is too.
         */
        if (loginResult.State == Launcher.LoginState.NeedsPatchBoot)
        {
            /*
             * CustomMessageBox.Show(
             *  Loc.Localize("EverythingIsFuckedMessage",
             *      "Certain essential game files were modified/broken by a third party and the game can neither update nor start.\nYou have to reinstall the game to continue.\n\nIf this keeps happening, please contact us via Discord."),
             *  "Error", MessageBoxButton.OK, MessageBoxImage.Error, parentWindow: _window);
             */

            throw new Exception("Boot conflict, need reinstall");

            return(false);
        }

        if (action == LoginAction.Repair)
        {
            try
            {
                if (loginResult.State == Launcher.LoginState.NeedsPatchGame)
                {
                    if (!await RepairGame(loginResult).ConfigureAwait(false))
                    {
                        return(false);
                    }

                    loginResult.State = Launcher.LoginState.Ok;
                    action            = LoginAction.Game;
                }
                else
                {
                    /*
                     * CustomMessageBox.Show(
                     *  Loc.Localize("LoginRepairResponseIsNotNeedsPatchGame",
                     *      "The server sent an incorrect response - the repair cannot proceed."),
                     *  "Error", MessageBoxButton.OK, MessageBoxImage.Error, parentWindow: _window);
                     */
                    throw new Exception("Repair login state not NeedsPatchGame");

                    return(false);
                }
            }
            catch (Exception ex)
            {
                /*
                 * We should never reach here.
                 * If server responds badly, then it should not even have reached this point, as error cases should have been handled before.
                 * If RepairGame was unsuccessful, then it should have handled all of its possible errors, instead of propagating it upwards.
                 */
                //CustomMessageBox.Builder.NewFrom(ex, "TryProcessLoginResult/Repair").WithParentWindow(_window).Show();
                throw;

                return(false);
            }
        }

        if (loginResult.State == Launcher.LoginState.NeedsPatchGame)
        {
            if (!await InstallGamePatch(loginResult).ConfigureAwait(false))
            {
                Log.Error("patchSuccess != true");
                return(false);
            }

            loginResult.State = Launcher.LoginState.Ok;
            action            = LoginAction.Game;
        }

        if (action == LoginAction.GameNoLaunch)
        {
            App.ShowMessageBlocking(
                Loc.Localize("LoginNoStartOk",
                             "An update check was executed and any pending updates were installed."), "XIVLauncher");

            return(false);
        }

        Debug.Assert(loginResult.State == Launcher.LoginState.Ok);

        while (true)
        {
            List <Exception> exceptions = new();

            try
            {
                using var process = await StartGameAndAddon(loginResult, isSteam, action == LoginAction.GameNoDalamud).ConfigureAwait(false);

                if (process is null)
                {
                    throw new Exception("Could not obtain Process Handle");
                }

                if (process.ExitCode != 0 && (App.Settings.TreatNonZeroExitCodeAsFailure ?? false))
                {
                    throw new Exception("Game exited with non-zero exit code");

                    /*
                     * switch (new CustomMessageBox.Builder()
                     *      .WithTextFormatted(
                     *          Loc.Localize("LaunchGameNonZeroExitCode",
                     *              "It looks like the game has exited with a fatal error. Do you want to relaunch the game?\n\nExit code: 0x{0:X8}"),
                     *          (uint)process.ExitCode)
                     *      .WithImage(MessageBoxImage.Exclamation)
                     *      .WithShowHelpLinks(true)
                     *      .WithShowDiscordLink(true)
                     *      .WithShowNewGitHubIssue(true)
                     *      .WithButtons(MessageBoxButton.YesNoCancel)
                     *      .WithDefaultResult(MessageBoxResult.Yes)
                     *      .WithCancelResult(MessageBoxResult.No)
                     *      .WithYesButtonText(Loc.Localize("LaunchGameRelaunch", "_Relaunch"))
                     *      .WithNoButtonText(Loc.Localize("LaunchGameClose", "_Close"))
                     *      .WithCancelButtonText(Loc.Localize("LaunchGameDoNotAskAgain", "_Don't ask again"))
                     *      .WithParentWindow(_window)
                     *      .Show())
                     * {
                     *  case MessageBoxResult.Yes:
                     *      continue;
                     *
                     *  case MessageBoxResult.No:
                     *      return true;
                     *
                     *  case MessageBoxResult.Cancel:
                     *      App.Settings.TreatNonZeroExitCodeAsFailure = false;
                     *      return true;
                     * }
                     */
                }

                return(true);
            }

            /*
             * catch (AggregateException ex)
             * {
             *  Log.Error(ex, "StartGameAndError resulted in one or more exceptions.");
             *
             *  exceptions.Add(ex.Flatten().InnerException);
             * }
             */
            catch (Exception ex)
            {
                Log.Error(ex, "StartGameAndError resulted in an exception.");

                exceptions.Add(ex);
                throw;
            }

            /*
             * var builder = new CustomMessageBox.Builder()
             *            .WithImage(MessageBoxImage.Error)
             *            .WithShowHelpLinks(true)
             *            .WithShowDiscordLink(true)
             *            .WithShowNewGitHubIssue(true)
             *            .WithButtons(MessageBoxButton.YesNo)
             *            .WithDefaultResult(MessageBoxResult.No)
             *            .WithCancelResult(MessageBoxResult.No)
             *            .WithYesButtonText(Loc.Localize("LaunchGameRetry", "_Try again"))
             *            .WithNoButtonText(Loc.Localize("LaunchGameClose", "_Close"))
             *            .WithParentWindow(_window);
             *
             * //NOTE(goat): This HAS to handle all possible exceptions from StartGameAndAddon!!!!!
             * List<string> summaries = new();
             * List<string> actionables = new();
             * List<string> descriptions = new();
             *
             * foreach (var exception in exceptions)
             * {
             *  switch (exception)
             *  {
             *      case GameExitedException:
             *          var count = 0;
             *
             *          foreach (var processName in new string[] { "ffxiv_dx11", "ffxiv" })
             *          {
             *              foreach (var process in Process.GetProcessesByName(processName))
             *              {
             *                  count++;
             *                  process.Dispose();
             *              }
             *          }
             *
             *          if (count >= 2)
             *          {
             *              summaries.Add(Loc.Localize("MultiboxDeniedWarningSummary",
             *                  "You can't launch more than two instances of the game by default."));
             *              actionables.Add(string.Format(
             *                  Loc.Localize("MultiboxDeniedWarningActionable",
             *                      "Please check if there is an instance of the game that did not close correctly. (Detected: {0})"),
             *                  count));
             *              descriptions.Add(null);
             *
             *              builder.WithButtons(MessageBoxButton.YesNoCancel)
             *                     .WithDefaultResult(MessageBoxResult.Yes)
             *                     .WithCancelButtonText(Loc.Localize("LaunchGameKillThenRetry", "_Kill then try again"));
             *          }
             *          else
             *          {
             *              summaries.Add(Loc.Localize("GameExitedPrematurelyErrorSummary",
             *                  "XIVLauncher could not detect that the game started correctly."));
             *              actionables.Add(Loc.Localize("GameExitedPrematurelyErrorActionable",
             *                  "This may be a temporary issue. Please try restarting your PC. It is possible that your game installation is not valid."));
             *              descriptions.Add(null);
             *          }
             *
             *          break;
             *
             *      case BinaryNotPresentException:
             *          summaries.Add(Loc.Localize("BinaryNotPresentErrorSummary",
             *              "Could not find the game executable."));
             *          actionables.Add(Loc.Localize("BinaryNotPresentErrorActionable",
             *              "This might be caused by your antivirus. You may have to reinstall the game."));
             *          descriptions.Add(null);
             *          break;
             *
             *      case IOException:
             *          summaries.Add(Loc.Localize("LoginIoErrorSummary",
             *              "Could not locate game data files."));
             *          summaries.Add(Loc.Localize("LoginIoErrorActionable",
             *              "This may mean that the game path set in XIVLauncher isn't preset, e.g. on a disconnected drive or network storage. Please check the game path in the XIVLauncher settings."));
             *          descriptions.Add(exception.ToString());
             *          break;
             *
             *      case Win32Exception win32Exception:
             *          summaries.Add(string.Format(
             *              Loc.Localize("UnexpectedErrorSummary",
             *                  "Unexpected error has occurred. ({0})"),
             *              $"0x{(uint)win32Exception.HResult:X8}: {win32Exception.Message}"));
             *          actionables.Add(Loc.Localize("UnexpectedErrorActionable",
             *              "Please report this error."));
             *          descriptions.Add(exception.ToString());
             *          break;
             *
             *      default:
             *          summaries.Add(string.Format(
             *              Loc.Localize("UnexpectedErrorSummary",
             *                  "Unexpected error has occurred. ({0})"),
             *              exception.Message));
             *          actionables.Add(Loc.Localize("UnexpectedErrorActionable",
             *              "Please report this error."));
             *          descriptions.Add(exception.ToString());
             *          break;
             *  }
             * }
             *
             * if (exceptions.Count == 1)
             * {
             *  builder.WithText($"{summaries[0]}\n\n{actionables[0]}")
             *         .WithDescription(descriptions[0]);
             * }
             * else
             * {
             *  builder.WithText(Loc.Localize("MultipleErrors", "Multiple errors have occurred."));
             *
             *  for (var i = 0; i < summaries.Count; i++)
             *  {
             *      builder.WithAppendText($"\n{i + 1}. {summaries[i]}\n    => {actionables[i]}");
             *      if (string.IsNullOrWhiteSpace(descriptions[i]))
             *          continue;
             *      builder.WithAppendDescription($"########## Exception {i + 1} ##########\n{descriptions[i]}\n\n");
             *  }
             * }
             *
             * if (descriptions.Any(x => x != null))
             *  builder.WithAppendSettingsDescription("Login");
             *
             *
             * switch (builder.Show())
             * {
             *  case MessageBoxResult.Yes:
             *      continue;
             *
             *  case MessageBoxResult.No:
             *      return false;
             *
             *  case MessageBoxResult.Cancel:
             *      for (var pass = 0; pass < 8; pass++)
             *      {
             *          var allKilled = true;
             *
             *          foreach (var processName in new string[] { "ffxiv_dx11", "ffxiv" })
             *          {
             *              foreach (var process in Process.GetProcessesByName(processName))
             *              {
             *                  allKilled = false;
             *
             *                  try
             *                  {
             *                      process.Kill();
             *                  }
             *                  catch (Exception ex2)
             *                  {
             *                      Log.Warning(ex2, "Could not kill process (PID={0}, name={1})", process.Id, process.ProcessName);
             *                  }
             *                  finally
             *                  {
             *                      process.Dispose();
             *                  }
             *              }
             *          }
             *
             *          if (allKilled)
             *              break;
             *      }
             *
             *      Task.Delay(1000).Wait();
             *      continue;
             * }
             */
        }
    }
Example #3
0
        private void InstallGamePatch(Launcher.LoginResult loginResult, bool gateStatus)
        {
            var mutex = new Mutex(false, "XivLauncherIsPatching");

            if (mutex.WaitOne(0, false))
            {
                if (Util.CheckIsGameOpen())
                {
                    MessageBox.Show(
                        Loc.Localize("GameIsOpenError", "The game and/or the official launcher are open. XIVLauncher cannot patch the game if this is the case.\nPlease close the official launcher and try again."),
                        "XIVLauncher", MessageBoxButton.OK, MessageBoxImage.Exclamation);

                    this.Dispatcher.Invoke(() =>
                    {
                        this.Show();
                        _isLoggingIn = false;
                    });
                }
                else
                {
                    Debug.Assert(loginResult.State == Launcher.LoginState.NeedsPatchGame,
                                 "loginResult.State == Launcher.LoginState.NeedsPatchGame ASSERTION FAILED");

                    var patcher = new PatchManager(Repository.Ffxiv, loginResult.PendingPatches, App.Settings.GamePath, App.Settings.PatchPath, _installer);

                    var progressDialog = new PatchDownloadDialog(patcher);
                    progressDialog.Show();
                    this.Hide();

                    patcher.OnFinish += async(sender, args) =>
                    {
                        progressDialog.Dispatcher.Invoke(() => progressDialog.Close());

                        if (args)
                        {
                            await this.Dispatcher.Invoke(() => StartGameAndAddon(loginResult, gateStatus));

                            _installer.Stop();

                            // Need to wait for installer to exit and release lock on patch files TODO bother winter why is this happening
                            Thread.Sleep(5000);

                            try
                            {
                                DeleteOldPatches();
                            }
                            catch (Exception ex)
                            {
                                Log.Error(ex, "Could not delete old patches.");
                            }
                        }
                        else
                        {
                            this.Dispatcher.Invoke(() =>
                            {
                                this.Show();
                                _isLoggingIn = false;
                            });
                        }

                        mutex.Close();
                        mutex = null;
                    };

                    patcher.Start();
                }
            }
            else
            {
                MessageBox.Show(Loc.Localize("PatcherAlreadyInProgress", "XIVLauncher is already patching your game in another instance. Please check if XIVLauncher is still open."), "XIVLauncher", MessageBoxButton.OK, MessageBoxImage.Error);
                Environment.Exit(0);
            }
        }
Example #4
0
        private async Task StartGameAndAddon(Launcher.LoginResult loginResult, bool gateStatus)
        {
#if !DEBUG
            if (!gateStatus)
            {
                Log.Information("GateStatus is false.");
                MessageBox.Show(
                    Loc.Localize("MaintenanceNotice", "Maintenance seems to be in progress. The game shouldn't be launched."),
                    "XIVLauncher", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                _isLoggingIn = false;

                return;
            }
#endif

            // We won't do any sanity checks here anymore, since that should be handled in StartLogin

            var gameProcess = Launcher.LaunchGame(loginResult.UniqueId, loginResult.OauthLogin.Region,
                                                  loginResult.OauthLogin.MaxExpansion, App.Settings.SteamIntegrationEnabled,
                                                  SteamCheckBox.IsChecked == true, App.Settings.AdditionalLaunchArgs, App.Settings.GamePath, App.Settings.IsDx11, App.Settings.Language.GetValueOrDefault(ClientLanguage.English), App.Settings.EncryptArguments.GetValueOrDefault(false));

            if (gameProcess == null)
            {
                Log.Information("GameProcess was null...");
                _isLoggingIn = false;
                return;
            }

            CleanUp();

            this.Hide();

            var addonMgr = new AddonManager();

            try
            {
                var addons = App.Settings.AddonList.Where(x => x.IsEnabled).Select(x => x.Addon).Cast <IAddon>().ToList();

                if (App.Settings.InGameAddonEnabled && App.Settings.IsDx11)
                {
                    addons.Add(new DalamudLauncher());
                }

                await Task.Run(() => addonMgr.RunAddons(gameProcess, App.Settings, addons));
            }
            catch (Exception ex)
            {
                new ErrorWindow(ex,
                                "This could be caused by your antivirus, please check its logs and add any needed exclusions.",
                                "Addons").ShowDialog();
                _isLoggingIn = false;

                addonMgr.StopAddons();
            }

            var watchThread = new Thread(() =>
            {
                while (!gameProcess.HasExited)
                {
                    gameProcess.Refresh();
                    Thread.Sleep(1);
                }

                Log.Information("Game has exited.");
                addonMgr.StopAddons();

                CleanUp();

                Environment.Exit(0);
            });
            watchThread.Start();

            Log.Debug("Started WatchThread");
        }
        private void InstallGamePatch(Launcher.LoginResult loginResult, bool gateStatus, bool startGame)
        {
            var mutex = new Mutex(false, "XivLauncherIsPatching");

            if (mutex.WaitOne(0, false))
            {
                if (Util.CheckIsGameOpen())
                {
                    CustomMessageBox.Show(
                        Loc.Localize("GameIsOpenError", "The game and/or the official launcher are open. XIVLauncher cannot patch the game if this is the case.\nPlease close the official launcher and try again."),
                        "XIVLauncher", MessageBoxButton.OK, MessageBoxImage.Exclamation);

                    this.Dispatcher.Invoke(() =>
                    {
                        this.Show();
                        _isLoggingIn = false;
                    });
                }
                else
                {
                    Debug.Assert(loginResult.State == Launcher.LoginState.NeedsPatchGame,
                                 "loginResult.State == Launcher.LoginState.NeedsPatchGame ASSERTION FAILED");

                    Debug.Assert(loginResult.PendingPatches != null, "loginResult.PendingPatches != null ASSERTION FAILED");

                    var patcher = new PatchManager(loginResult.PendingPatches, App.Settings.GamePath, App.Settings.PatchPath, _installer);

                    var progressDialog = new PatchDownloadDialog(patcher);
                    progressDialog.Show();
                    this.Hide();

                    patcher.OnFinish += async(sender, args) =>
                    {
                        progressDialog.Dispatcher.Invoke(() =>
                        {
                            progressDialog.Hide();
                            progressDialog.Close();
                        });

                        if (args)
                        {
                            if (!startGame)
                            {
                                this.Dispatcher.Invoke(() =>
                                {
                                    CustomMessageBox.Show(
                                        Loc.Localize("LoginNoStartOk",
                                                     "An update check was executed and any pending updates were installed."), "XIVLauncher",
                                        MessageBoxButton.OK, MessageBoxImage.Information, false);

                                    _isLoggingIn = false;
                                    Show();
                                    Activate();
                                });
                            }
                            else
                            {
                                await this.Dispatcher.Invoke(() => StartGameAndAddon(loginResult, gateStatus));
                            }
                            _installer.Stop();
                        }
                        else
                        {
                            this.Dispatcher.Invoke(() =>
                            {
                                this.Show();
                                _isLoggingIn = false;
                            });
                        }

                        mutex.Close();
                        mutex = null;
                    };

                    patcher.Start();
                }
            }
            else
            {
                CustomMessageBox.Show(Loc.Localize("PatcherAlreadyInProgress", "XIVLauncher is already patching your game in another instance. Please check if XIVLauncher is still open."), "XIVLauncher", MessageBoxButton.OK, MessageBoxImage.Error);
                Environment.Exit(0);
            }
        }