Exemple #1
0
        /// <summary>
        /// Clears the registry of DLL data, and refreshes it by scanning GameData.
        /// This operates as a transaction.
        /// This *saves* the registry upon completion.
        /// </summary>
        // TODO: This would likely be better in the Registry class itself.
        public void ScanGameData()
        {
            using (TransactionScope tx = CkanTransaction.CreateTransactionScope())
            {
                Registry.ClearDlls();

                // TODO: It would be great to optimise this to skip .git directories and the like.
                // Yes, I keep my GameData in git.

                // Alas, EnumerateFiles is *case-sensitive* in its pattern, which causes
                // DLL files to be missed under Linux; we have to pick .dll, .DLL, or scanning
                // GameData *twice*.
                //
                // The least evil is to walk it once, and filter it ourselves.
                IEnumerable <string> files = Directory.EnumerateFiles(
                    GameData(),
                    "*",
                    SearchOption.AllDirectories
                    );

                files = files.Where(file => Regex.IsMatch(file, @"\.dll$", RegexOptions.IgnoreCase));

                foreach (string dll in files.Select(KSPPathUtils.NormalizePath))
                {
                    Registry.RegisterDll(this, dll);
                }

                tx.Complete();
            }
            RegistryManager.Save();
        }
Exemple #2
0
 /// <summary>
 /// Copies a directory and optionally its subdirectories as a transaction.
 /// </summary>
 /// <param name="sourceDirPath">Source directory path.</param>
 /// <param name="destDirPath">Destination directory path.</param>
 /// <param name="copySubDirs">Copy sub dirs recursively if set to <c>true</c>.</param>
 public static void CopyDirectory(string sourceDirPath, string destDirPath, bool copySubDirs)
 {
     using (TransactionScope transaction = CkanTransaction.CreateTransactionScope())
     {
         _CopyDirectory(sourceDirPath, destDirPath, copySubDirs);
         transaction.Complete();
     }
 }
Exemple #3
0
 /// <summary>
 /// Copies a directory and optionally its subdirectories as a transaction.
 /// </summary>
 /// <param name="sourceDirPath">Source directory path.</param>
 /// <param name="destDirPath">Destination directory path.</param>
 /// <param name="copySubDirs">Copy sub dirs recursively if set to <c>true</c>.</param>
 public static void CopyDirectory(string sourceDirPath, string destDirPath, bool copySubDirs)
 {
     TxFileManager file_transaction = new TxFileManager();
     using (TransactionScope transaction = CkanTransaction.CreateTransactionScope())
     {
         _CopyDirectory(sourceDirPath, destDirPath, copySubDirs, file_transaction);
         transaction.Complete();
     }
 }
Exemple #4
0
        /// <summary>
        /// Uninstalls all the mods provided, including things which depend upon them.
        /// This *DOES* save the registry.
        /// Preferred over Uninstall.
        /// </summary>
        public void UninstallList(IEnumerable <string> mods)
        {
            // Pre-check, have they even asked for things which are installed?

            foreach (string mod in mods.Where(mod => registry_manager.registry.InstalledModule(mod) == null))
            {
                throw new ModNotInstalledKraken(mod);
            }

            // Find all the things which need uninstalling.
            IEnumerable <string> goners = registry_manager.registry.FindReverseDependencies(mods);

            // If there us nothing to uninstall, skip out.
            if (!goners.Any())
            {
                return;
            }

            User.RaiseMessage("About to remove:\n");

            foreach (string mod in goners)
            {
                InstalledModule module = registry_manager.registry.InstalledModule(mod);
                User.RaiseMessage(" * {0} {1}", module.Module.identifier, module.Module.modVersion);
            }

            bool ok = User.RaiseYesNoDialog("\nContinue?");

            if (!ok)
            {
                User.RaiseMessage("Mod removal aborted at user request.");
                return;
            }

            using (var transaction = CkanTransaction.CreateTransactionScope())
            {
                foreach (string mod in goners)
                {
                    User.RaiseMessage("Removing {0}...", mod);
                    Uninstall(mod);
                }

                registry_manager.Save();
                ksp.RebuildFactorioModlist();

                transaction.Complete();
            }

            User.RaiseMessage("Done!\n");
        }
Exemple #5
0
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            // Only close the window, when the user has access to the "Exit" of the menu.
            if (!menuStrip1.Enabled)
            {
                e.Cancel = true;
                return;
            }

            if (!ManageMods.AllowClose())
            {
                e.Cancel = true;
                return;
            }

            // Copy window location to app settings
            configuration.WindowLoc = WindowState == FormWindowState.Normal ? Location : RestoreBounds.Location;

            // Copy window size to app settings if not maximized
            configuration.WindowSize = WindowState == FormWindowState.Normal ? Size : RestoreBounds.Size;

            //copy window maximized state to app settings
            configuration.IsWindowMaximised = WindowState == FormWindowState.Maximized;

            // Copy panel position to app settings
            configuration.PanelPosition = splitContainer1.SplitterDistance;

            // Copy metadata panel split height to app settings
            configuration.ModInfoPosition = ModInfo.ModMetaSplitPosition;

            // Save the active filter
            configuration.ActiveFilter      = (int)ManageMods.mainModList.ModFilter;
            configuration.CustomLabelFilter = ManageMods.mainModList.CustomLabelFilter?.Name;

            // Save settings.
            configuration.Save();

            if (needRegistrySave)
            {
                using (var transaction = CkanTransaction.CreateTransactionScope())
                {
                    // Save registry
                    RegistryManager.Instance(CurrentInstance).Save(false);
                    transaction.Complete();
                }
            }

            base.OnFormClosing(e);
        }
Exemple #6
0
        /// <summary>
        ///     Install our mod from the filename supplied.
        ///     If no file is supplied, we will check the cache or throw FileNotFoundKraken.
        ///     Does *not* resolve dependencies; this actually does the heavy listing.
        ///     Does *not* save the registry.
        ///     Do *not* call this directly, use InstallList() instead.
        ///
        /// Propagates a BadMetadataKraken if our install metadata is bad.
        /// Propagates a FileExistsKraken if we were going to overwrite a file.
        /// Throws a FileNotFoundKraken if we can't find the downloaded module.
        ///
        /// </summary>
        //
        // TODO: The name of this and InstallModule() need to be made more distinctive.

        private void Install(CkanModule module, string filename = null)
        {
            CheckMetapackageInstallationKraken(module);

            Version version = registry_manager.registry.InstalledVersion(module.identifier);

            // TODO: This really should be handled by higher-up code.
            if (version != null)
            {
                User.RaiseMessage("    {0} {1} already installed, skipped", module.identifier, version);
                return;
            }

            // Find our in the cache if we don't already have it.
            filename = filename ?? Cache.GetCachedZip(module.download, true);

            // If we *still* don't have a file, then kraken bitterly.
            if (filename == null)
            {
                throw new FileNotFoundKraken(
                          null,
                          String.Format("Trying to install {0}, but it's not downloaded or download is corrupted", module)
                          );
            }

            // We'll need our registry to record which files we've installed.
            Registry registry = registry_manager.registry;

            using (var transaction = CkanTransaction.CreateTransactionScope())
            {
                // Install all the things!
                IEnumerable <string> files = InstallModule(module, filename);

                // Register our module and its files.
                registry.RegisterModule(module, files, ksp);

                // Finish our transaction, but *don't* save the registry; we may be in an
                // intermediate, inconsistent state.
                // This is fine from a transaction standpoint, as we may not have an enclosing
                // transaction, and if we do, they can always roll us back.
                transaction.Complete();
            }

            // Fire our callback that we've installed a module, if we have one.
            if (onReportModInstalled != null)
            {
                onReportModInstalled(module);
            }
        }
Exemple #7
0
        /// <summary>
        /// Clears the registry of DLL data, and refreshes it by scanning GameData.
        /// This operates as a transaction.
        /// This *saves* the registry upon completion.
        /// TODO: This would likely be better in the Registry class itself.
        /// </summary>
        /// <returns>
        /// True if found anything different, false if same as before
        /// </returns>
        public bool Scan()
        {
            if (Directory.Exists(game.PrimaryModDirectory(this)))
            {
                var manager = RegistryManager.Instance(this);
                using (TransactionScope tx = CkanTransaction.CreateTransactionScope())
                {
                    log.DebugFormat("Scanning for DLLs in {0}",
                                    game.PrimaryModDirectory(this));
                    var oldDlls = manager.registry.InstalledDlls.ToHashSet();
                    manager.registry.ClearDlls();

                    // TODO: It would be great to optimise this to skip .git directories and the like.
                    // Yes, I keep my GameData in git.

                    // Alas, EnumerateFiles is *case-sensitive* in its pattern, which causes
                    // DLL files to be missed under Linux; we have to pick .dll, .DLL, or scanning
                    // GameData *twice*.
                    //
                    // The least evil is to walk it once, and filter it ourselves.
                    IEnumerable <string> files = Directory
                                                 .EnumerateFiles(game.PrimaryModDirectory(this), "*", SearchOption.AllDirectories)
                                                 .Where(file => file.EndsWith(".dll", StringComparison.CurrentCultureIgnoreCase))
                                                 .Select(CKANPathUtils.NormalizePath)
                                                 .Where(absPath => !game.StockFolders.Any(f =>
                                                                                          ToRelativeGameDir(absPath).StartsWith($"{f}/")));

                    foreach (string dll in files)
                    {
                        manager.registry.RegisterDll(this, dll);
                    }
                    var  newDlls    = manager.registry.InstalledDlls.ToHashSet();
                    bool dllChanged = !oldDlls.SetEquals(newDlls);
                    bool dlcChanged = manager.ScanDlc();

                    if (dllChanged || dlcChanged)
                    {
                        manager.Save(false);
                    }

                    log.Debug("Scan completed, committing transaction");
                    tx.Complete();

                    return(dllChanged || dlcChanged);
                }
            }
            return(false);
        }
Exemple #8
0
        /// <summary>
        /// Uninstalls all the mods provided, including things which depend upon them.
        /// This *DOES* save the registry.
        /// Preferred over Uninstall.
        /// </summary>
        public void UninstallList(IEnumerable <string> mods)
        {
            // Pre-check, have they even asked for things which are installed?

            foreach (string mod in mods.Where(mod => registry_manager.registry.InstalledModule(mod) == null))
            {
                throw new ModNotInstalledKraken(mod);
            }

            // Find all the things which need uninstalling.
            IEnumerable <string> goners = registry_manager.registry.FindReverseDependencies(mods);

            User.RaiseMessage("About to remove:\n");

            foreach (string mod in goners)
            {
                User.RaiseMessage(" * {0}", mod);
            }

            bool ok = User.RaiseYesNoDialog("\nContinue?");

            if (!ok)
            {
                User.RaiseMessage("Mod removal aborted at user request.");
                return;
            }

            using (var transaction = new CkanTransaction())
            {
                foreach (string mod in goners)
                {
                    User.RaiseMessage("Removing {0}...", mod);
                    Uninstall(mod);
                }

                registry_manager.Save();

                transaction.Complete();
            }

            User.RaiseMessage("Done!");
        }
Exemple #9
0
        /// <summary>
        /// Clears the registry of DLL data, and refreshes it by scanning GameData.
        /// This operates as a transaction.
        /// This *saves* the registry upon completion.
        /// TODO: This would likely be better in the Registry class itself.
        /// </summary>
        /// <returns>
        /// True if found anything different, false if same as before
        /// </returns>
        public bool ScanGameData()
        {
            var manager = RegistryManager.Instance(this);

            using (TransactionScope tx = CkanTransaction.CreateTransactionScope())
            {
                var oldDlls = new HashSet <string>(manager.registry.InstalledDlls);
                manager.registry.ClearDlls();

                // TODO: It would be great to optimise this to skip .git directories and the like.
                // Yes, I keep my GameData in git.

                // Alas, EnumerateFiles is *case-sensitive* in its pattern, which causes
                // DLL files to be missed under Linux; we have to pick .dll, .DLL, or scanning
                // GameData *twice*.
                //
                // The least evil is to walk it once, and filter it ourselves.
                IEnumerable <string> files = Directory
                                             .EnumerateFiles(GameData(), "*", SearchOption.AllDirectories)
                                             .Where(file => dllRegex.IsMatch(file))
                                             .Select(KSPPathUtils.NormalizePath)
                                             .Where(absPath => !DllIgnoreList.Contains(ToRelativeGameDir(absPath)));

                foreach (string dll in files)
                {
                    manager.registry.RegisterDll(this, dll);
                }
                var  newDlls    = new HashSet <string>(manager.registry.InstalledDlls);
                bool dllChanged = !oldDlls.SetEquals(newDlls);
                bool dlcChanged = manager.ScanDlc();

                if (dllChanged || dlcChanged)
                {
                    manager.Save(false);
                }

                tx.Complete();

                return(dllChanged || dlcChanged);
            }
        }
Exemple #10
0
        /// <summary>
        /// Adds and removes the listed modules as a single transaction.
        /// No relationships will be processed.
        /// This *will* save the registry.
        /// </summary>
        /// <param name="add">Add.</param>
        /// <param name="remove">Remove.</param>
        public void AddRemove(IEnumerable <CkanModule> add = null, IEnumerable <string> remove = null, bool enforceConsistency = true)
        {
            // TODO: We should do a consistency check up-front, rather than relying
            // upon our registry catching inconsistencies at the end.

            using (var tx = CkanTransaction.CreateTransactionScope())
            {
                foreach (string identifier in remove)
                {
                    Uninstall(identifier);
                }

                foreach (CkanModule module in add)
                {
                    Install(module);
                }

                registry_manager.Save(enforceConsistency);

                tx.Complete();
            }
        }
Exemple #11
0
        public void InstallList(ModuleResolution modules, RelationshipResolverOptions options)
        {
            // We're about to install all our mods; so begin our transaction.
            using (TransactionScope transaction = CkanTransaction.CreateTransactionScope())
            {
                var enumeratedMods = modules.Select((m, i) => new { Idx = i, Module = m });
                foreach (var item in enumeratedMods)
                {
                    var percentComplete = (item.Idx * 100) / modules.Count;
                    User.RaiseProgress(string.Format("Installing mod \"{0}\"", item.Module), percentComplete);
                    Install(item.Module);
                }

                User.RaiseProgress("Updating registry", 70);

                registry_manager.Save(!options.without_enforce_consistency);

                User.RaiseProgress("Commiting filesystem changes", 80);

                transaction.Complete();
            }
        }
Exemple #12
0
        /// <summary>
        /// Adds and removes the listed modules as a single transaction.
        /// No relationships will be processed.
        /// This *will* save the registry.
        /// </summary>
        /// <param name="add">Add.</param>
        /// <param name="remove">Remove.</param>
        public void AddRemove(IEnumerable <CkanModule> add = null, IEnumerable <string> remove = null)
        {
            // TODO: We should do a consistency check up-front, rather than relying
            // upon our registry catching inconsistencies at the end.

            // TODO: Download our files.

            using (var tx = new CkanTransaction())
            {
                foreach (string identifier in remove)
                {
                    Uninstall(identifier);
                }

                foreach (CkanModule module in add)
                {
                    Install(module);
                }

                registry_manager.Save();

                tx.Complete();
            }
        }
Exemple #13
0
        /// <summary>
        /// Uninstall the module provided. For internal use only.
        /// Use UninstallList for user queries, it also does dependency handling.
        /// This does *NOT* save the registry.
        /// </summary>

        private void Uninstall(string modName)
        {
            using (var transaction = CkanTransaction.CreateTransactionScope())
            {
                InstalledModule mod = registry_manager.registry.InstalledModule(modName);

                if (mod == null)
                {
                    log.ErrorFormat("Trying to uninstall {0} but it's not installed", modName);
                    throw new ModNotInstalledKraken(modName);
                }

                // Walk our registry to find all files for this mod.
                IEnumerable <string> files = mod.Files;

                var directoriesToDelete = new HashSet <string>();

                foreach (string file in files)
                {
                    string path = ksp.ToAbsoluteGameDir(file);

                    try
                    {
                        FileAttributes attr = File.GetAttributes(path);

                        if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
                        {
                            directoriesToDelete.Add(path);
                        }
                        else
                        {
                            log.InfoFormat("Removing {0}", file);
                            file_transaction.Delete(path);
                        }
                    }
                    catch (Exception ex)
                    {
                        // XXX: This is terrible, we're catching all exceptions.
                        log.ErrorFormat("Failure in locating file {0} : {1}", path, ex.Message);
                    }
                }

                // Remove from registry.

                registry_manager.registry.DeregisterModule(ksp, modName);

                // Sort our directories from longest to shortest, to make sure we remove child directories
                // before parents. GH #78.
                foreach (string directory in directoriesToDelete.OrderBy(dir => dir.Length).Reverse())
                {
                    if (!Directory.EnumerateFileSystemEntries(directory).Any())
                    {
                        // Skip Ships/VAB ans Ships/SPH
                        if (directory == KSPPathUtils.ToAbsolute("VAB", ksp.Ships()) ||
                            directory == KSPPathUtils.ToAbsolute("SPH", ksp.Ships()))
                        {
                            continue;
                        }

                        // We *don't* use our file_transaction to delete files here, because
                        // it fails if the system's temp directory is on a different device
                        // to KSP. However we *can* safely delete it now we know it's empty,
                        // because the TxFileMgr *will* put it back if there's a file inside that
                        // needs it.
                        //
                        // This works around GH #251.
                        // The filesystem boundry bug is described in https://transactionalfilemgr.codeplex.com/workitem/20

                        log.InfoFormat("Removing {0}", directory);
                        Directory.Delete(directory);
                    }
                    else
                    {
                        log.InfoFormat("Not removing directory {0}, it's not empty", directory);
                    }
                }
                transaction.Complete();
            }
        }
Exemple #14
0
        /// <summary>
        /// Clears the registry of DLL data, and refreshes it by scanning GameData.
        /// This operates as a transaction.
        /// This *saves* the registry upon completion.
        /// </summary>
        // TODO: This would likely be better in the Registry class itself.
        public void ScanGameData()
        {
            using (TransactionScope tx = CkanTransaction.CreateTransactionScope())
            {
                Registry.ClearPreexistingModules();

                var detectedModules = FactorioModDetector.findAllModsInDirectory(Path.Combine(gamedatadir, "mods"));
                foreach (var detectedModule in detectedModules)
                {
                    string      detectedModulePath = detectedModule.Key;
                    ModInfoJson detectedModInfo    = detectedModule.Value;
                    if (Registry.InstalledModules.Any(p => p.identifier == detectedModInfo.name))
                    {
                        continue;
                    }
                    AvailableModule availableModule;
                    if (Registry.available_modules.TryGetValue(detectedModInfo.name, out availableModule))
                    {
                        CfanModule availableCfan = availableModule.ByVersion(detectedModInfo.version);
                        if (availableCfan != null)
                        {
                            string expectedFilename = availableCfan.standardFileName + ".zip";
                            if (Path.GetFileName(detectedModulePath) == expectedFilename)
                            {
                                // yay, we can use this mod as installed (we will be able to update/remove it through cfan)
                                Registry.RegisterModule(availableCfan, new [] { detectedModulePath }, this);
                                continue;
                            }
                        }
                    }
                    // we only register that this module exists, but we won't be able to do anything with it
                    Registry.RegisterPreexistingModule(this, detectedModulePath, detectedModInfo);
                }

                try
                {
                    Registry.CheckSanity();
                }
                catch (InconsistentKraken e)
                {
                    User.RaiseError("Autodetected mods has unmet dependencies, they won't be managed by CFAN until you fix inconsitencies.\n{0}", e.InconsistenciesPretty);

                    var unmet = SanityChecker.FindUnmetDependencies(Registry.InstalledModules.Select(p => p.Module), Registry.InstalledPreexistingModules);
                    do
                    {
                        unmet
                        .Select(unmetDependencyKeyValue => unmetDependencyKeyValue.Value)
                        .SelectMany(p => p.Select(modWithUnmetDependencies => detectedModules.FirstOrDefault(detectedModule => detectedModule.Value.name == modWithUnmetDependencies.identifier)))
                        .Where(p => !p.Equals(default(KeyValuePair <string, ModInfoJson>)))
                        .ToList()
                        .ForEach(modWithUnmetDependencies =>
                        {
                            Registry.DeregisterModule(this, modWithUnmetDependencies.Value.name, true);
                            Registry.RegisterPreexistingModule(this, modWithUnmetDependencies.Key, modWithUnmetDependencies.Value);
                        });
                        unmet = SanityChecker.FindUnmetDependencies(Registry.InstalledModules.Select(p => p.Module), Registry.InstalledPreexistingModules);
                    } while (unmet.Any());
                }

                tx.Complete();
            }

            RegistryManager.Save();
        }
Exemple #15
0
        /// <summary>
        ///     Installs all modules given a list of identifiers as a transaction. Resolves dependencies.
        ///     This *will* save the registry at the end of operation.
        ///
        /// Propagates a BadMetadataKraken if our install metadata is bad.
        /// Propagates a FileExistsKraken if we were going to overwrite a file.
        /// Propagates a CancelledActionKraken if the user cancelled the install.
        /// </summary>
        //
        // TODO: Break this up into smaller pieces! It's huge!
        public void InstallList(
            ICollection <CkanModule> modules,
            RelationshipResolverOptions options,
            IDownloader downloader = null
            )
        {
            var resolver = new RelationshipResolver(modules, options, registry_manager.registry, ksp.Version());
            List <CkanModule> modsToInstall = resolver.ModList();
            List <CkanModule> downloads     = new List <CkanModule> ();

            // TODO: All this user-stuff should be happening in another method!
            // We should just be installing mods as a transaction.

            User.RaiseMessage("About to install...\n");

            foreach (CkanModule module in modsToInstall)
            {
                if (!ksp.Cache.IsCachedZip(module.download))
                {
                    User.RaiseMessage(" * {0}", module);
                    downloads.Add(module);
                }
                else
                {
                    User.RaiseMessage(" * {0} (cached)", module);
                }
            }

            bool ok = User.RaiseYesNoDialog("\nContinue?");

            if (!ok)
            {
                throw new CancelledActionKraken("User declined install list");
            }

            User.RaiseMessage(String.Empty); // Just to look tidy.

            if (downloads.Count > 0)
            {
                if (downloader == null)
                {
                    downloader = new NetAsyncDownloader(User);
                }

                downloader.DownloadModules(ksp.Cache, downloads);
            }

            // We're about to install all our mods; so begin our transaction.
            using (TransactionScope transaction = CkanTransaction.CreateTransactionScope())
            {
                for (int i = 0; i < modsToInstall.Count; i++)
                {
                    int percent_complete = (i * 100) / modsToInstall.Count;

                    User.RaiseProgress(String.Format("Installing mod \"{0}\"", modsToInstall[i]),
                                       percent_complete);

                    Install(modsToInstall[i]);
                }

                User.RaiseProgress("Updating registry", 70);

                registry_manager.Save(!options.without_enforce_consistency);

                User.RaiseProgress("Commiting filesystem changes", 80);

                transaction.Complete();
            }

            // We can scan GameData as a separate transaction. Installing the mods
            // leaves everything consistent, and this is just gravy. (And ScanGameData
            // acts as a Tx, anyway, so we don't need to provide our own.)

            User.RaiseProgress("Rescanning GameData", 90);

            if (!options.without_enforce_consistency)
            {
                ksp.ScanGameData();
            }

            User.RaiseProgress("Done!\n", 100);
        }
Exemple #16
0
        private void InstallMods(object sender, DoWorkEventArgs e) // this probably needs to be refactored
        {
            installCanceled = false;

            ClearLog();

            var opts =
                (KeyValuePair <ModChanges, RelationshipResolverOptions>)e.Argument;

            IRegistryQuerier registry  = RegistryManager.Instance(manager.CurrentInstance).registry;
            ModuleInstaller  installer = ModuleInstaller.GetInstance(CurrentInstance, GUI.user);

            // setup progress callback

            toInstall = new HashSet <string>();
            var toUninstall = new HashSet <string>();
            var toUpgrade   = new HashSet <string>();

            // First compose sets of what the user wants installed, upgraded, and removed.
            foreach (ModChange change in opts.Key)
            {
                switch (change.ChangeType)
                {
                case GUIModChangeType.Remove:
                    toUninstall.Add(change.Mod.Identifier);
                    break;

                case GUIModChangeType.Update:
                    toUpgrade.Add(change.Mod.Identifier);
                    break;

                case GUIModChangeType.Install:
                    toInstall.Add(change.Mod.Identifier);
                    break;
                }
            }

            // Now work on satisifying dependencies.

            var recommended = new Dictionary <string, List <string> >();
            var suggested   = new Dictionary <string, List <string> >();

            foreach (var change in opts.Key)
            {
                if (change.ChangeType == GUIModChangeType.Install)
                {
                    AddMod(change.Mod.ToModule().recommends, recommended, change.Mod.Identifier, registry);
                    AddMod(change.Mod.ToModule().suggests, suggested, change.Mod.Identifier, registry);
                }
            }

            ShowSelection(recommended);
            ShowSelection(suggested, true);

            tabController.HideTab("ChooseRecommendedModsTabPage");

            if (installCanceled)
            {
                tabController.HideTab("WaitTabPage");
                tabController.ShowTab("ManageModsTabPage");
                e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                return;
            }

            // Now let's make all our changes.

            tabController.RenameTab("WaitTabPage", "Status log");
            tabController.ShowTab("WaitTabPage");
            tabController.SetTabLock(true);


            var downloader = new NetAsyncModulesDownloader(GUI.user);

            cancelCallback = () =>
            {
                downloader.CancelDownload();
                installCanceled = true;
            };

            GUI.user.RaiseMessage("About to install...\r\n");

            // FIXME: here we should heat up the cache with any mods we're going to install, because
            // when this transaction types out it f***s up everything else.
            var resolvedMods = installer.ResolveModules(toInstall, opts.Value);

            foreach (var module in resolvedMods.CachedModules)
            {
                GUI.user.RaiseMessage(" * {0} {1}(cached)", module.name, module.version);
            }

            foreach (var module in resolvedMods.UncachedModules)
            {
                GUI.user.RaiseMessage(" * {0} {1}", module.name, module.version);
            }

            if (!GUI.user.RaiseYesNoDialog("\r\nContinue?"))
            {
                throw new CancelledActionKraken("User declined install list");
            }

            GUI.user.RaiseMessage(String.Empty); // Just to look tidy.

            installer.EnsureCache(resolvedMods.UncachedModules);

            //Transaction is needed here to revert changes when an installation is cancelled
            //TODO: Cancellation should be handelt in the ModuleInstaller
            using (var transaction = CkanTransaction.CreateTransactionScope())
            {
                //Set the result to false/failed in case we return
                e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                SetDescription("Uninstalling selected mods");
                if (!WasSuccessful(() => installer.UninstallList(toUninstall)))
                {
                    return;
                }
                if (installCanceled)
                {
                    return;
                }

                SetDescription("Updating selected mods");
                if (!WasSuccessful(() => installer.Upgrade(toUpgrade, downloader)))
                {
                    return;
                }
                if (installCanceled)
                {
                    return;
                }

                // TODO: We should be able to resolve all our provisioning conflicts
                // before we start installing anything. CKAN.SanityChecker can be used to
                // pre-check if our changes are going to be consistent.

                bool resolvedAllProvidedMods = false;

                while (!resolvedAllProvidedMods)
                {
                    if (installCanceled)
                    {
                        e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                        return;
                    }
                    var ret = InstallList(resolvedMods, opts.Value);
                    if (!ret)
                    {
                        // install failed for some reason, error message is already displayed to the user
                        e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                        return;
                    }
                    resolvedAllProvidedMods = true;
                }

                if (!installCanceled)
                {
                    transaction.Complete();
                }
            }

            e.Result = new KeyValuePair <bool, ModChanges>(!installCanceled, opts.Key);
        }
        /// <summary>
        /// Create a new fake KSP instance
        /// </summary>
        /// <param name="game">The game of the new instance.</param>
        /// <param name="newName">The name for the new instance.</param>
        /// <param name="newPath">The location of the new instance.</param>
        /// <param name="version">The version of the new instance. Should have a build number.</param>
        /// <param name="dlcs">The IDlcDetector implementations for the DLCs that should be faked and the requested dlc version as a dictionary.</param>
        /// <exception cref="InstanceNameTakenKraken">Thrown if the instance name is already in use.</exception>
        /// <exception cref="NotKSPDirKraken">Thrown by AddInstance() if created instance is not valid, e.g. if a write operation didn't complete for whatever reason.</exception>
        public void FakeInstance(IGame game, string newName, string newPath, GameVersion version, Dictionary <DLC.IDlcDetector, GameVersion> dlcs = null)
        {
            TxFileManager fileMgr = new TxFileManager();

            using (TransactionScope transaction = CkanTransaction.CreateTransactionScope())
            {
                if (HasInstance(newName))
                {
                    throw new InstanceNameTakenKraken(newName);
                }


                if (!version.InBuildMap(game))
                {
                    throw new BadGameVersionKraken(String.Format("The specified KSP version is not a known version: {0}", version.ToString()));
                }
                if (Directory.Exists(newPath) && (Directory.GetFiles(newPath).Length != 0 || Directory.GetDirectories(newPath).Length != 0))
                {
                    throw new BadInstallLocationKraken("The specified folder already exists and is not empty.");
                }

                log.DebugFormat("Creating folder structure and text files at {0} for KSP version {1}", Path.GetFullPath(newPath), version.ToString());

                // Create a KSP root directory, containing a GameData folder, a buildID.txt/buildID64.txt and a readme.txt
                fileMgr.CreateDirectory(newPath);
                fileMgr.CreateDirectory(Path.Combine(newPath, "GameData"));
                fileMgr.CreateDirectory(Path.Combine(newPath, "Ships"));
                fileMgr.CreateDirectory(Path.Combine(newPath, "Ships", "VAB"));
                fileMgr.CreateDirectory(Path.Combine(newPath, "Ships", "SPH"));
                fileMgr.CreateDirectory(Path.Combine(newPath, "Ships", "@thumbs"));
                fileMgr.CreateDirectory(Path.Combine(newPath, "Ships", "@thumbs", "VAB"));
                fileMgr.CreateDirectory(Path.Combine(newPath, "Ships", "@thumbs", "SPH"));
                fileMgr.CreateDirectory(Path.Combine(newPath, "saves"));
                fileMgr.CreateDirectory(Path.Combine(newPath, "saves", "scenarios"));
                fileMgr.CreateDirectory(Path.Combine(newPath, "saves", "training"));

                // Don't write the buildID.txts if we have no build, otherwise it would be -1.
                if (version.IsBuildDefined)
                {
                    fileMgr.WriteAllText(Path.Combine(newPath, "buildID.txt"), String.Format("build id = {0}", version.Build));
                    fileMgr.WriteAllText(Path.Combine(newPath, "buildID64.txt"), String.Format("build id = {0}", version.Build));
                }

                // Create the readme.txt WITHOUT build number.
                fileMgr.WriteAllText(Path.Combine(newPath, "readme.txt"), String.Format("Version {0}", new GameVersion(version.Major, version.Minor, version.Patch).ToString()));

                // Create the needed folder structure and the readme.txt for DLCs that should be simulated.
                if (dlcs != null)
                {
                    foreach (KeyValuePair <DLC.IDlcDetector, GameVersion> dlc in dlcs)
                    {
                        DLC.IDlcDetector dlcDetector = dlc.Key;
                        GameVersion      dlcVersion  = dlc.Value;

                        if (!dlcDetector.AllowedOnBaseVersion(version))
                        {
                            throw new WrongGameVersionKraken(
                                      version,
                                      String.Format("KSP version {0} or above is needed for {1} DLC.",
                                                    dlcDetector.ReleaseGameVersion,
                                                    dlcDetector.IdentifierBaseName
                                                    ));
                        }

                        string dlcDir = Path.Combine(newPath, dlcDetector.InstallPath());
                        fileMgr.CreateDirectory(dlcDir);
                        fileMgr.WriteAllText(
                            Path.Combine(dlcDir, "readme.txt"),
                            String.Format("Version {0}", dlcVersion));
                    }
                }

                // Add the new instance to the config
                GameInstance new_instance = new GameInstance(game, newPath, newName, User, false);
                AddInstance(new_instance);
                transaction.Complete();
            }
        }
Exemple #18
0
        /// <summary>
        /// Uninstall the module provided. For internal use only.
        /// Use UninstallList for user queries, it also does dependency handling.
        /// This does *NOT* save the registry.
        /// </summary>

        private void Uninstall(string modName)
        {
            using (var transaction = CkanTransaction.CreateTransactionScope())
            {
                InstalledModule mod = registry_manager.registry.InstalledModule(modName);

                if (mod == null)
                {
                    log.ErrorFormat("Trying to uninstall {0} but it's not installed", modName);
                    throw new ModNotInstalledKraken(modName);
                }

                // Walk our registry to find all files for this mod.
                IEnumerable <string> files = mod.Files;

                var directoriesToDelete = new HashSet <string>();

                foreach (string file in files)
                {
                    string path = ksp.ToAbsoluteGameDir(file);

                    try
                    {
                        FileAttributes attr = File.GetAttributes(path);

                        // [This is] bitwise math. Basically, attr is some binary value with one bit meaning
                        // "this is a directory". The bitwise and & operator will return a binary value where
                        // only the bits that are on (1) in both the operands are turned on. In this case
                        // doing a bitwise and operation against attr and the FileAttributes.Directory value
                        // will return the value of FileAttributes.Directory if the Directory file attribute
                        // bit is turned on. See en.wikipedia.org/wiki/Bitwise_operation for a better
                        // explanation. – Kyle Trauberman Aug 30 '12 at 21:28
                        // (https://stackoverflow.com/questions/1395205/better-way-to-check-if-path-is-a-file-or-a-directory)
                        // This is the fastest way to do this test.
                        if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
                        {
                            if (!directoriesToDelete.Contains(path))
                            {
                                directoriesToDelete.Add(path);
                            }
                        }
                        else
                        {
                            // Add this files' directory to the list for deletion if it isn't already there.
                            // Helps clean up directories when modules are uninstalled out of dependency order
                            // Since we check for directory contents when deleting, this should purge empty
                            // dirs, making less ModuleManager headaches for people.
                            var directoryName = Path.GetDirectoryName(path);
                            if (!(directoriesToDelete.Contains(directoryName)))
                            {
                                directoriesToDelete.Add(directoryName);
                            }

                            log.DebugFormat("Removing {0}", file);
                            file_transaction.Delete(path);
                        }
                    }
                    catch (Exception ex)
                    {
                        // XXX: This is terrible, we're catching all exceptions.
                        // We don't consider this problem serious enough to abort and revert,
                        // so treat it as a "--verbose" level log message.
                        log.InfoFormat("Failure in locating file {0} : {1}", path, ex.Message);
                    }
                }

                // Remove from registry.
                registry_manager.registry.DeregisterModule(ksp, modName);

                // Our collection of directories may leave empty parent directories.
                directoriesToDelete = AddParentDirectories(directoriesToDelete);

                // Sort our directories from longest to shortest, to make sure we remove child directories
                // before parents. GH #78.
                foreach (string directory in directoriesToDelete.OrderBy(dir => dir.Length).Reverse())
                {
                    if (!Directory.EnumerateFileSystemEntries(directory).Any())
                    {
                        // It is bad if any of this directories gets removed
                        // So we protect them
                        if (IsReservedDirectory(directory))
                        {
                            continue;
                        }

                        // We *don't* use our file_transaction to delete files here, because
                        // it fails if the system's temp directory is on a different device
                        // to KSP. However we *can* safely delete it now we know it's empty,
                        // because the TxFileMgr *will* put it back if there's a file inside that
                        // needs it.
                        //
                        // This works around GH #251.
                        // The filesystem boundry bug is described in https://transactionalfilemgr.codeplex.com/workitem/20

                        log.DebugFormat("Removing {0}", directory);
                        Directory.Delete(directory);
                    }
                    else
                    {
                        log.InfoFormat("Not removing directory {0}, it's not empty", directory);
                    }
                }
                log.InfoFormat("Removed {0}", modName);
                transaction.Complete();
            }
        }
Exemple #19
0
        /// <summary>
        /// Download and update the local CKAN meta-info.
        /// Optionally takes a URL to the zipfile repo to download.
        /// </summary>
        public static RepoUpdateResult UpdateAllRepositories(RegistryManager registry_manager, GameInstance ksp, NetModuleCache cache, IUser user)
        {
            SortedDictionary <string, Repository> sortedRepositories = registry_manager.registry.Repositories;

            user.RaiseProgress("Checking for updates", 0);
            if (sortedRepositories.Values.All(repo => !string.IsNullOrEmpty(repo.last_server_etag) && repo.last_server_etag == Net.CurrentETag(repo.uri)))
            {
                user.RaiseProgress("Already up to date", 100);
                user.RaiseMessage("No changes since last update");
                return(RepoUpdateResult.NoChanges);
            }
            List <CkanModule> allAvail = new List <CkanModule>();
            int index = 0;

            foreach (KeyValuePair <string, Repository> repository in sortedRepositories)
            {
                user.RaiseProgress($"Updating {repository.Value.name}",
                                   10 + 80 * index / sortedRepositories.Count);
                SortedDictionary <string, int> downloadCounts;
                string            newETag;
                List <CkanModule> avail = UpdateRegistry(repository.Value.uri, ksp, user, out downloadCounts, out newETag);
                registry_manager.registry.SetDownloadCounts(downloadCounts);
                if (avail == null)
                {
                    // Report failure if any repo fails, rather than losing half the list.
                    // UpdateRegistry will have alerted the user to specific errors already.
                    return(RepoUpdateResult.Failed);
                }
                else
                {
                    // Merge all the lists
                    allAvail.AddRange(avail);
                    repository.Value.last_server_etag = newETag;
                    user.RaiseMessage("Updated {0} ({1} modules)",
                                      repository.Value.name, avail.Count);
                }
                ++index;
            }
            // Save allAvail to the registry if we found anything
            if (allAvail.Count > 0)
            {
                user.RaiseProgress("Saving modules to registry", 90);
                using (var transaction = CkanTransaction.CreateTransactionScope())
                {
                    // Save our changes.
                    registry_manager.registry.SetAllAvailable(allAvail);
                    registry_manager.Save(enforce_consistency: false);
                    transaction.Complete();
                }

                ShowUserInconsistencies(registry_manager.registry, user);

                List <CkanModule> metadataChanges = GetChangedInstalledModules(registry_manager.registry);
                if (metadataChanges.Count > 0)
                {
                    HandleModuleChanges(metadataChanges, user, ksp, cache, registry_manager);
                }

                // Registry.CompatibleModules is slow, just return success,
                // caller can check it if it's really needed
                user.RaiseProgress("Registry saved", 100);
                user.RaiseMessage("Repositories updated");
                return(RepoUpdateResult.Updated);
            }
            else
            {
                // Return failure
                user.RaiseMessage("No modules found!");
                return(RepoUpdateResult.Failed);
            }
        }
Exemple #20
0
        private void InstallMods(object sender, DoWorkEventArgs e) // this probably needs to be refactored
        {
            installCanceled = false;

            ClearLog();

            var opts =
                (KeyValuePair <List <KeyValuePair <CkanModule, GUIModChangeType> >, RelationshipResolverOptions>)e.Argument;

            ModuleInstaller installer = ModuleInstaller.GetInstance(CurrentInstance, GUI.user);

            // setup progress callback

            toInstall = new HashSet <string>();
            var toUninstall = new HashSet <string>();
            var toUpgrade   = new HashSet <string>();

            // First compose sets of what the user wants installed, upgraded, and removed.
            foreach (KeyValuePair <CkanModule, GUIModChangeType> change in opts.Key)
            {
                switch (change.Value)
                {
                case GUIModChangeType.Remove:
                    toUninstall.Add(change.Key.identifier);
                    break;

                case GUIModChangeType.Update:
                    toUpgrade.Add(change.Key.identifier);
                    break;

                case GUIModChangeType.Install:
                    toInstall.Add(change.Key.identifier);
                    break;
                }
            }

            // Now work on satisifying dependencies.

            var recommended = new Dictionary <string, List <string> >();
            var suggested   = new Dictionary <string, List <string> >();

            foreach (var change in opts.Key)
            {
                if (change.Value == GUIModChangeType.Install)
                {
                    if (change.Key.recommends != null)
                    {
                        foreach (RelationshipDescriptor mod in change.Key.recommends)
                        {
                            try
                            {
                                // if the mod is available for the current KSP version _and_
                                // the mod is not installed _and_
                                // the mod is not already in the install list
                                if (
                                    RegistryManager.Instance(manager.CurrentInstance)
                                    .registry.LatestAvailable(mod.name, manager.CurrentInstance.Version()) !=
                                    null &&
                                    !RegistryManager.Instance(manager.CurrentInstance)
                                    .registry.IsInstalled(mod.name) &&
                                    !toInstall.Contains(mod.name))
                                {
                                    // add it to the list of recommended mods we display to the user
                                    if (recommended.ContainsKey(mod.name))
                                    {
                                        recommended[mod.name].Add(change.Key.identifier);
                                    }
                                    else
                                    {
                                        recommended.Add(mod.name, new List <string> {
                                            change.Key.identifier
                                        });
                                    }
                                }
                            }
                            // XXX - Don't ignore all krakens! Those things are important!
                            catch (Kraken)
                            {
                            }
                        }
                    }

                    if (change.Key.suggests != null)
                    {
                        foreach (RelationshipDescriptor mod in change.Key.suggests)
                        {
                            try
                            {
                                if (
                                    RegistryManager.Instance(manager.CurrentInstance).registry.LatestAvailable(mod.name, manager.CurrentInstance.Version()) != null &&
                                    !RegistryManager.Instance(manager.CurrentInstance).registry.IsInstalled(mod.name) &&
                                    !toInstall.Contains(mod.name))
                                {
                                    if (suggested.ContainsKey(mod.name))
                                    {
                                        suggested[mod.name].Add(change.Key.identifier);
                                    }
                                    else
                                    {
                                        suggested.Add(mod.name, new List <string> {
                                            change.Key.identifier
                                        });
                                    }
                                }
                            }
                            // XXX - Don't ignore all krakens! Those things are important!
                            catch (Kraken)
                            {
                            }
                        }
                    }
                }
            }

            // If we're going to install something anyway, then don't list it in the
            // recommended list, since they can't de-select it anyway.
            foreach (var item in toInstall)
            {
                recommended.Remove(item);
            }

            // If there are any mods that would be recommended, prompt the user to make
            // selections.
            if (recommended.Any())
            {
                Util.Invoke(this, () => UpdateRecommendedDialog(recommended));

                m_TabController.ShowTab("ChooseRecommendedModsTabPage", 3);
                m_TabController.RenameTab("ChooseRecommendedModsTabPage", "Choose recommended mods");
                m_TabController.SetTabLock(true);

                lock (this)
                {
                    Monitor.Wait(this);
                }

                m_TabController.SetTabLock(false);
            }

            m_TabController.HideTab("ChooseRecommendedModsTabPage");

            // And now on to suggestions. Again, we don't show anything that's scheduled to
            // be installed on our suggest list.
            foreach (var item in toInstall)
            {
                suggested.Remove(item);
            }

            if (suggested.Any())
            {
                Util.Invoke(this, () => UpdateRecommendedDialog(suggested, true));

                m_TabController.ShowTab("ChooseRecommendedModsTabPage", 3);
                m_TabController.RenameTab("ChooseRecommendedModsTabPage", "Choose suggested mods");
                m_TabController.SetTabLock(true);

                lock (this)
                {
                    Monitor.Wait(this);
                }

                m_TabController.SetTabLock(false);
            }

            m_TabController.HideTab("ChooseRecommendedModsTabPage");

            if (installCanceled)
            {
                m_TabController.HideTab("WaitTabPage");
                m_TabController.ShowTab("ManageModsTabPage");
                e.Result = new KeyValuePair <bool, List <KeyValuePair <CkanModule, GUIModChangeType> > >(false, opts.Key);
                return;
            }

            // Now let's make all our changes.

            m_TabController.RenameTab("WaitTabPage", "Installing mods");
            m_TabController.ShowTab("WaitTabPage");
            m_TabController.SetTabLock(true);

            using (var transaction = new CkanTransaction())
            {
                var downloader = new NetAsyncDownloader(GUI.user);
                cancelCallback = () =>
                {
                    downloader.CancelDownload();
                    installCanceled = true;
                };


                SetDescription("Uninstalling selected mods");
                installer.UninstallList(toUninstall);
                if (installCanceled)
                {
                    return;
                }

                SetDescription("Updating selected mods");
                installer.Upgrade(toUpgrade, downloader);


                // TODO: We should be able to resolve all our provisioning conflicts
                // before we start installing anything. CKAN.SanityChecker can be used to
                // pre-check if our changes are going to be consistent.

                bool resolvedAllProvidedMods = false;

                while (!resolvedAllProvidedMods)
                {
                    if (installCanceled)
                    {
                        e.Result = new KeyValuePair <bool, List <KeyValuePair <CkanModule, GUIModChangeType> > >(false,
                                                                                                                 opts.Key);
                        return;
                    }
                    try
                    {
                        var ret = InstallList(toInstall, opts.Value, downloader);
                        if (!ret)
                        {
                            // install failed for some reason, error message is already displayed to the user
                            e.Result = new KeyValuePair <bool, List <KeyValuePair <CkanModule, GUIModChangeType> > >(false,
                                                                                                                     opts.Key);
                            return;
                        }
                        resolvedAllProvidedMods = true;
                    }
                    catch (TooManyModsProvideKraken tooManyProvides)
                    {
                        Util.Invoke(this, () => UpdateProvidedModsDialog(tooManyProvides));

                        m_TabController.ShowTab("ChooseProvidedModsTabPage", 3);
                        m_TabController.SetTabLock(true);

                        lock (this)
                        {
                            Monitor.Wait(this);
                        }

                        m_TabController.SetTabLock(false);

                        m_TabController.HideTab("ChooseProvidedModsTabPage");

                        if (installCanceled)
                        {
                            m_TabController.HideTab("WaitTabPage");
                            m_TabController.ShowTab("ManageModsTabPage");
                            e.Result = new KeyValuePair <bool, List <KeyValuePair <CkanModule, GUIModChangeType> > >(false,
                                                                                                                     opts.Key);
                            return;
                        }

                        m_TabController.ShowTab("WaitTabPage");
                    }
                }
                if (!installCanceled)
                {
                    transaction.Complete();
                }
            }
            e.Result = new KeyValuePair <bool, List <KeyValuePair <CkanModule, GUIModChangeType> > >(true, opts.Key);
        }