/// <summary> /// Check if the given module is a metapackage: /// if it is, throws a BadCommandKraken. /// </summary> private static void CheckMetapackageInstallationKraken(CfanModule module) { if (module.isMetapackage) { throw new BadCommandKraken("Metapackages can not be installed!"); } }
/// <summary> /// Installs the module from the zipfile provided. /// Returns a list of files installed. /// Propagates a BadMetadataKraken if our install metadata is bad. /// Propagates a FileExistsKraken if we were going to overwrite a file. /// </summary> private IEnumerable <string> InstallModule(CfanModule module, string zip_filename) { CheckMetapackageInstallationKraken(module); IEnumerable <IInstallable> files = FindInstallableFiles(module, zip_filename, ksp); try { foreach (IInstallable file in files) { log.InfoFormat("Copying {0}", file.Name); CopyZipEntry(ksp.getModTypeRootDirectory(module.kind), file); } } catch (FileExistsKraken kraken) { // Decorate the kraken with our module and re-throw kraken.filename = ksp.ToRelativeGameDataDir(kraken.filename); kraken.installing_module = module; kraken.owning_module = registry_manager.registry.FileOwner(kraken.filename); throw; } return(files.Select(x => Path.Combine(ksp.getModTypeRootDirectory(module.kind), x.Destination))); }
private void _PostModCaching(CfanModule module) { HideWaitDialog(true); UpdateModContentsTree(module, true); RecreateDialogs(); }
private void UpdateModInfo(GUIMod gui_module) { CfanModule module = gui_module.ToModule(); Util.Invoke(MetadataModuleNameLabel, () => MetadataModuleNameLabel.Text = gui_module.Name); Util.Invoke(MetadataModuleVersionLabel, () => MetadataModuleVersionLabel.Text = gui_module.LatestVersion.ToString()); Util.Invoke(MetadataModuleLicenseLabel, () => MetadataModuleLicenseLabel.Text = ""); Util.Invoke(MetadataModuleAuthorLabel, () => MetadataModuleAuthorLabel.Text = gui_module.Authors); Util.Invoke(MetadataModuleAbstractLabel, () => MetadataModuleAbstractLabel.Text = module.@abstract); Util.Invoke(MetadataIdentifierLabel, () => MetadataIdentifierLabel.Text = module.identifier); // If we have homepage provided use that, otherwise use the spacedock page or the github repo so that users have somewhere to get more info than just the abstract. Util.Invoke(MetadataModuleHomePageLinkLabel, () => MetadataModuleHomePageLinkLabel.Text = gui_module.Homepage.ToString()); if (string.IsNullOrEmpty(gui_module.Homepage)) { Util.Invoke(MetadataModuleGitHubLinkLabel, () => MetadataModuleGitHubLinkLabel.Text = "N/A"); } if (module.release_status != null) { Util.Invoke(MetadataModuleReleaseStatusLabel, () => MetadataModuleReleaseStatusLabel.Text = module.release_status.ToString()); } Util.Invoke(MetadataModuleKSPCompatibilityLabel, () => MetadataModuleKSPCompatibilityLabel.Text = gui_module.KSPCompatibilityLong); }
public void AutodetectedCanSatisfyRelationships() { using (var ksp = new DisposableKSP()) { registry.RegisterPreexistingModule(ksp.KSP, Path.Combine(ksp.KSP.GameData(), "ModuleManager.dll"), new ModInfoJson() { name = "ModuleManager", version = new ModVersion("0.2.3") }); var depends = new List <ModDependency>() { new ModDependency("ModuleManager") }; CfanModule mod = generator.GeneratorRandomModule(depends: depends); new RelationshipResolver( new CfanModule[] { mod }, RelationshipResolver.DefaultOpts(), registry, new FactorioVersion("1.0.0") ); } }
/// <summary> /// Tests that a module might be able to be installed via checking if dependencies /// exist for current version. /// </summary> /// <param name="module">The module to consider</param> /// <param name="compatible">For internal use</param> /// <returns>If it has dependencies compatible for the current version</returns> private bool MightBeInstallable(CfanModule module, List <string> compatible = null) { if (module.depends == null) { return(true); } if (compatible == null) { compatible = new List <string>(); } else if (compatible.Contains(module.identifier)) { return(true); } //When checking the dependencies we assume that this module is installable // in case a dependent depends on it compatible.Add(module.identifier); var needed = module.depends.Select(depend => registry.LatestAvailableWithProvides(depend.modName, kspversion)); //We need every dependency to have at least one possible module var installable = needed.All(need => need.Any(mod => MightBeInstallable(mod, compatible))); compatible.Remove(module.identifier); return(installable); }
public string getCachedOrDownloadFile(IUser user, string url, string expectedFilename) { string cachePath = getPathToCachedOrDownloadedFile(user, url, expectedFilename); string firstCharacters = head(cachePath, 100).TrimStart(); if (firstCharacters.StartsWith("<!DOCTYPE")) { throw new HtmlInsteadOfModDownloadedKraken("Downloaded some kind of html."); } // the json string with error usually is from github if (firstCharacters.StartsWith("{\"error\":")) { throw new HtmlInsteadOfModDownloadedKraken("Downloaded some kind of json error."); } string temporaryFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); File.Copy(cachePath, temporaryFile); modNormalizer.normalizeModFile(temporaryFile, Path.GetFileNameWithoutExtension(expectedFilename)); // move to target place ModInfoJson modInfo = FactorioModParser.parseMod(temporaryFile); expectedFilename = CfanModule.createStandardFileName(modInfo.name, modInfo.version.ToString()); // normalize again, this time with real filename (in order to fix directory name in zip file) modNormalizer.normalizeModFile(temporaryFile, expectedFilename); string fmmModFile = Path.Combine(RepoModsDirectoryPath, expectedFilename) + ".zip"; if (File.Exists(fmmModFile)) { File.Delete(fmmModFile); } File.Move(temporaryFile, fmmModFile); return(fmmModFile); }
public void SingleDownload() { // Force log4net on. // BasicConfigurator.Configure(); // LogManager.GetRepository().Threshold = Level.Debug; log.Info("Performing single download test."); // We know kOS is in the TestKAN data, and hosted in KS. Let's get it. var modules = new List <CfanModule>(); CfanModule kOS = registry.LatestAvailable("kOS", null); Assert.IsNotNull(kOS); modules.Add(kOS); // Make sure we don't alread have kOS somehow. Assert.IsFalse(cache.IsCached(kOS.download)); // log.InfoFormat("Downloading kOS from {0}", kOS.download); // Download our module. async.DownloadModules( ksp.KSP.Cache, modules ); // Assert that we have it, and it passes zip validation. Assert.IsTrue(cache.IsCachedZip(kOS.download)); }
public void LatestAvailable() { CfanModule module = registry.LatestAvailable("FARL", temp_ksp.KSP.Version()); Assert.AreEqual("FARL", module.identifier); Assert.AreEqual("0.5.24", module.modVersion.ToString()); }
public Depends(CfanModule module) { if (module == null) { throw new ArgumentNullException(); } Parent = module; }
public Suggested(CfanModule module) { if (module == null) { throw new ArgumentNullException(); } Parent = module; }
public Recommended(CfanModule module) { if (module == null) { throw new ArgumentNullException(); } Parent = module; }
public void UpdateRegistryZip() { CKAN.Repo.UpdateRegistry(TestData.TestKANZip(), registry, ksp.KSP, new NullUser()); // Test we've got an expected module. CfanModule far = registry.LatestAvailable("FARL", new FactorioVersion("0.12.99")); Assert.AreEqual("0.5.25", far.modVersion.ToString()); }
/// <summary> /// <see cref="IRegistryQuerier.Available"/> /// </summary> public List <CfanModule> Available(FactorioVersion ksp_version) { var candidates = new List <string>(available_modules.Keys); var compatible = new List <CfanModule>(); // It's nice to see things in alphabetical order, so sort our keys first. candidates.Sort(); //Cache CfanModule[] modules_for_current_version = available_modules.Values.Select(pair => pair.Latest(ksp_version)).Where(mod => mod != null).ToArray(); // Now find what we can give our user. foreach (string candidate in candidates) { CfanModule available = LatestAvailable(candidate, ksp_version); if (available != null) { // we need to check that we can get everything we depend on bool failedDepedency = false; if (available.depends != null) { foreach (ModDependency dependency in available.depends) { try { if (!LatestAvailableWithProvides(dependency.modName, ksp_version, modules_for_current_version).Any()) { log.InfoFormat("Cannot find available version with provides for {0} in registry required by {1}", dependency.modName, candidate); failedDepedency = true; break; } } catch (KeyNotFoundException) { log.ErrorFormat("Cannot find available version with provides for {0} in registry", dependency.modName); throw; } catch (ModuleNotFoundKraken) { log.InfoFormat("Cannot find available version with provides for {0} in registry required by {1}", dependency.modName, candidate); failedDepedency = true; break; } } } if (!failedDepedency) { compatible.Add(available); } } } return(compatible); }
private void UpdateModContentsTree(CfanModule module, bool force = false) { ModInfoTabControl.Tag = module ?? ModInfoTabControl.Tag; //Can be costly. For now only update when visible. if (ModInfoTabControl.SelectedIndex != ContentTabPage.TabIndex && !force) { return; } Util.Invoke(ContentsPreviewTree, () => _UpdateModContentsTree(force)); }
/// <summary> /// Displays a user readable string explaining why the mod was chosen. /// </summary> /// <param name="mod">A Mod in the resolvers modlist. Must not be null</param> /// <returns></returns> public string ReasonStringFor(CfanModule mod) { var reason = ReasonFor(mod); var is_root_type = reason.GetType() == typeof(SelectionReason.UserRequested) || reason.GetType() == typeof(SelectionReason.Installed); return(is_root_type ? reason.Reason : reason.Reason + ReasonStringFor(reason.Parent)); }
/// <summary> /// Record the given module version as being available. /// </summary> public void Add(CfanModule module) { if (!module.identifier.Equals(identifier)) { throw new ArgumentException($"This AvailableModule is for tracking {identifier} not {module.identifier}"); } log.DebugFormat("Adding {0}", module); module_version[module.modVersion] = module.cfanJson; }
private void UpdateModDependencyGraph(CfanModule module) { ModInfoTabControl.Tag = module ?? ModInfoTabControl.Tag; //Can be costly. For now only update when visible. if (ModInfoTabControl.SelectedIndex != RelationshipTabPage.TabIndex) { return; } Util.Invoke(DependsGraphTree, _UpdateModDependencyGraph); }
public void Incompatible(Type type, bool expected) { var comparator = (CKAN.IGameComparator)Activator.CreateInstance(type); gameMod = RandomModuleGenerator.GeneratorRandomModule(depends: new List <ModDependency> { new ModDependency("something") }); // The mod without any version restriction is compatible with everything Assert.AreEqual(expected, comparator.Compatible(GameVersion, gameMod)); }
public void GenerallySafeStrict(Type type, bool expected) { var comparator = (CKAN.IGameComparator)Activator.CreateInstance(type); gameMod = RandomModuleGenerator.GeneratorRandomModule(depends: new List <ModDependency> { new ModDependency("base==0.12.4") }); // Now test! Assert.AreEqual(expected, comparator.Compatible(GameVersion, gameMod)); }
/// <summary> /// Given a module and a path to a zipfile, returns all the files that would be installed /// from that zip for this module. /// /// This *will* throw an exception if the file does not exist. /// /// Throws a BadMetadataKraken if the stanza resulted in no files being returned. /// /// If a KSP instance is provided, it will be used to generate output paths, otherwise these will be null. /// </summary> // TODO: Document which exception! public static List <IInstallable> FindInstallableFiles(CfanModule module, string zip_filename, KSP ksp) { if (module.kind == CfanJson.CfanModType.META) { return(new List <IInstallable>()); } if (module.kind != CfanJson.CfanModType.MOD) { throw new NotImplementedException("Only regular mod is implemented."); } return(new List <IInstallable>(new IInstallable[] { new InstallableFile(zip_filename, module.standardFileName + ".zip") })); }
/// <summary> /// Returns the module contents if and only if we have it /// available in our cache. Returns null, otherwise. /// /// Intended for previews. /// </summary> // TODO: Return files relative to GameRoot public IEnumerable <string> GetModuleContentsList(CfanModule module) { string filename = ksp.Cache.GetCachedZip(module.download); if (filename == null) { return(null); } return(FindInstallableFiles(module, filename, ksp) .Select(x => KSPPathUtils.NormalizePath(x.Destination))); }
/// <summary> /// Upgrades or installs the mods listed to the specified versions for the user's KSP. /// Will *re-install* or *downgrade* (with a warning) as well as upgrade. /// Throws ModuleNotFoundKraken if a module is not installed. /// </summary> public void Upgrade(IEnumerable <CfanModule> modules, IDownloader netAsyncDownloader) { // Start by making sure we've downloaded everything. DownloadModules(modules, netAsyncDownloader); // Our upgrade involves removing everything that's currently installed, then // adding everything that needs installing (which may involve new mods to // satisfy dependencies). We always know the list passed in is what we need to // install, but we need to calculate what needs to be removed. var to_remove = new List <string>(); // Let's discover what we need to do with each module! foreach (CfanModule module in modules) { string ident = module.identifier; InstalledModule installed_mod = registry_manager.registry.InstalledModule(ident); if (installed_mod == null) { //Maybe ModuleNotInstalled ? if (registry_manager.registry.IsAutodetected(ident)) { throw new ModuleNotFoundKraken(ident, module.modVersion.ToString(), String.Format("Can't upgrade {0} as it was not installed by CFAN. \n Please remove manually before trying to install it.", ident)); } User.RaiseMessage("Installing previously uninstalled mod {0}", ident); } else { // Module already installed. We'll need to remove it first. to_remove.Add(module.identifier); CfanModule installed = installed_mod.Module; if (installed.modVersion.Equals(module.modVersion)) { log.WarnFormat("{0} is already at the latest version, reinstalling", installed.identifier); } else if (installed.modVersion.IsGreaterThan(module.modVersion)) { log.WarnFormat("Downgrading {0} from {1} to {2}", ident, installed.modVersion, module.modVersion); } else { log.InfoFormat("Upgrading {0} to {1}", ident, module.modVersion); } } } AddRemove( modules, to_remove ); }
public bool Compatible(FactorioVersion gameVersion, CfanModule module) { // If it's strictly compatible, then it's compatible. if (strict.Compatible(gameVersion, module)) { return(true); } // if we will add something like "maximum game version" for a mod, then it might make sense to add something here (see summary of the class) return(false); }
/// <summary> /// Register the supplied module as having been installed, thereby keeping /// track of its metadata and files. /// </summary> public void RegisterModule(CfanModule mod, IEnumerable <string> absolute_files, KSP ksp) { SealionTransaction(); // But we also want to keep track of all its files. // We start by checking to see if any files are owned by another mod, // if so, we abort with a list of errors. var inconsistencies = new List <string>(); // We always work with relative files, so let's get some! IEnumerable <string> relative_files = absolute_files.Select(x => ksp.ToRelativeGameDataDir(x)); // For now, it's always cool if a module wants to register a directory. // We have to flip back to absolute paths to actually test this. foreach (string file in relative_files.Where(file => !Directory.Exists(ksp.ToAbsoluteGameDataDir(file)))) { string owner; if (installed_files.TryGetValue(file, out owner)) { // Woah! Registering an already owned file? Not cool! // (Although if it existed, we should have thrown a kraken well before this.) inconsistencies.Add( string.Format("{0} wishes to install {1}, but this file is registered to {2}", mod.identifier, file, owner )); } } if (inconsistencies.Count > 0) { throw new InconsistentKraken(inconsistencies); } // If everything is fine, then we copy our files across. By not doing this // in the loop above, we make sure we don't have a half-registered module // when we throw our exceptinon. // This *will* result in us overwriting who owns a directory, and that's cool, // directories aren't really owned like files are. However because each mod maintains // its own list of files, we'll remove directories when the last mod using them // is uninstalled. foreach (string file in relative_files) { installed_files[file] = mod.identifier; } // Finally, register our module proper. var installed = new InstalledModule(ksp, mod, relative_files); installed_modules.Add(mod.identifier, installed); }
public void Setup() { // By setting these for every test, we can make sure our tests can change // them any way they like without harming other tests. dogezip = TestData.DogeCoinFlagZip(); dogemod = TestData.DogeCoinFlag_101_module(); mm_zip = TestData.ModuleManagerZip(); mm_mod = TestData.ModuleManagerModule(); ksp = new DisposableKSP(); }
public SelectionReason ReasonFor(CfanModule mod) { if (mod == null) { throw new ArgumentNullException(); } if (!ModList().Contains(mod)) { throw new ArgumentException("Mod " + mod.identifier + " is not in the list"); } return(reasons[mod]); }
/// <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(CfanModule module, string filename = null) { CheckMetapackageInstallationKraken(module); AbstractVersion 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); } }
internal static CfanModule LoadCkanFromFile(CKAN.KSP current_instance, string ckan_file) { CfanModule module = CfanFileManager.fromCfanFile(ckan_file); // We'll need to make some registry changes to do this. RegistryManager registry_manager = RegistryManager.Instance(current_instance); // Remove this version of the module in the registry, if it exists. registry_manager.registry.RemoveAvailable(module); // Sneakily add our version in... registry_manager.registry.AddAvailable(module); return(module); }
private void _UpdateModContentsTree(bool force = false) { GUIMod guiMod = GetSelectedModule(); if (!guiMod.IsCKAN) { return; } CfanModule module = guiMod.ToCkanModule(); if (Equals(module, current_mod_contents_module) && !force) { return; } else { current_mod_contents_module = module; } if (!guiMod.IsCached) { NotCachedLabel.Text = "This mod is not in the cache, click 'Download' to preview contents"; ContentsDownloadButton.Enabled = true; ContentsPreviewTree.Enabled = false; } else { NotCachedLabel.Text = "Module is cached, preview available"; ContentsDownloadButton.Enabled = false; ContentsPreviewTree.Enabled = true; } ContentsPreviewTree.Nodes.Clear(); ContentsPreviewTree.Nodes.Add(module.title); IEnumerable <string> contents = ModuleInstaller.GetInstance(manager.CurrentInstance, GUI.user).GetModuleContentsList(module); if (contents == null) { return; } foreach (string item in contents) { ContentsPreviewTree.Nodes[0].Nodes.Add(item); } ContentsPreviewTree.Nodes[0].ExpandAll(); }