From b7ea3dbb7ca0168d7b62b7484d840850c627b6e7 Mon Sep 17 00:00:00 2001 From: Salvador Cipolla Date: Thu, 25 Jan 2024 17:27:40 -0300 Subject: [PATCH 1/4] mod install/modify rework --- Knossos.NET/Models/Mod.cs | 51 +++++- .../ViewModels/Templates/TaskItemViewModel.cs | 47 +++++ .../ViewModels/Windows/ModInstallViewModel.cs | 172 +++++++++++++----- .../Views/Windows/ModInstallView.axaml | 5 +- 4 files changed, 230 insertions(+), 45 deletions(-) diff --git a/Knossos.NET/Models/Mod.cs b/Knossos.NET/Models/Mod.cs index bec20ddd..8bb55e8f 100644 --- a/Knossos.NET/Models/Mod.cs +++ b/Knossos.NET/Models/Mod.cs @@ -236,6 +236,7 @@ public List GetMissingDependenciesList(bool overrideSettings = fa missingDep.id = dep.id; missingDep.version = dep.version; missingDep.packages = missingPkg; + missingDep.originalDependency = dep; missingDependencyList.Add(missingDep); } } @@ -885,6 +886,26 @@ public static bool IsMetaUpdate(Mod modA, Mod modB) } return false; } + + /// + /// Try to track down the mod pkg that a ModDependency belongs too + /// + /// + /// Returns the modpkg or null if not found + public ModPackage? FindPackageWithDependency(ModDependency? dependency) + { + if(dependency != null && packages != null && packages.Any()) + { + foreach(var pkg in packages) + { + if(pkg.dependencies != null && pkg.dependencies.FirstOrDefault(dep => dep == dependency) != null) + { + return pkg; + } + } + } + return null; + } } public class ModMember @@ -917,10 +938,29 @@ public class ModPackage public object? checkNotes { get; set; } /* Knet Only */ + /// + /// used for pkg display in a checkbox. + /// NOT Saved in the json + /// [JsonIgnore] public bool isSelected { get; set; } = false; - + /// + /// used for display (to enable/disabled chkbox) and to enable/disable the package in devmode + /// Saved in the json + /// public bool isEnabled { get; set; } = true; + /// + /// Used to display checkbox tooltip + /// NOT saved in the Json + /// + [JsonIgnore] + public string tooltip { get; set; } = string.Empty; + /// + /// Used to indicate a pkg is needed during mod install chkbox selection + /// Used to change checkbox foreground color during mod install/modify display + /// + [JsonIgnore] + public bool isRequired { get; set; } = false; [JsonIgnore] public int buildScore { get; set; } = 0; } @@ -931,6 +971,15 @@ public class ModDependency public string? version { get; set; } // required, https://getcomposer.org/doc/01-basic-usage.md#package-versions public List? packages { get; set; } // optional, specifies which optional and recommended packages are also required + /// + /// Used to store the original dependency reference during mod.GetMissingDependencies + /// Usefull to track down the original pkg this dependency belong to with + /// mod.FindPackageWithDependency() + /// Not saved in Json + /// + [JsonIgnore] + public ModDependency? originalDependency; + /// /// Returns the best installed mod that meets this dependency by semantic version, null if none. /// Also returns null if the ID is a FSO build. diff --git a/Knossos.NET/ViewModels/Templates/TaskItemViewModel.cs b/Knossos.NET/ViewModels/Templates/TaskItemViewModel.cs index 62ce5da8..612da5f9 100644 --- a/Knossos.NET/ViewModels/Templates/TaskItemViewModel.cs +++ b/Knossos.NET/ViewModels/Templates/TaskItemViewModel.cs @@ -2988,6 +2988,7 @@ public async Task InstallMod(Mod mod, CancellationTokenSource cancelSource installed = Knossos.GetInstalledMod(mod.id, mod.version); if (installed != null) { + installed.ReLoadJson(); Name = "Modify " + mod.ToString().Replace("_", "-"); ; compressMod = installed.modSettings.isCompressed; } @@ -3058,7 +3059,52 @@ public async Task InstallMod(Mod mod, CancellationTokenSource cancelSource await Dispatcher.UIThread.InvokeAsync(() => TaskList.Insert(0, msg)); } + /* Delete pkgs */ + if (installed != null) + { + bool save = false; + foreach (var modpkg in mod.packages.ToList()) + { + var installedPkg = installed.packages.FirstOrDefault(p => p.name == modpkg.name); + if (modpkg.filelist != null && !modpkg.isSelected && installedPkg != null) + { + int delCount = 0; + var newTask = new TaskItemViewModel(); + Log.Add(Log.LogSeverity.Information, "TaskItemViewModel.InstallMod(delete mod file)", "Deleting package: " + modpkg.name + " MOD: " + mod); + Dispatcher.UIThread.Invoke(() => + { + newTask.ShowMsg("Deleting pkg: " + modpkg.name, null, Brushes.Red); + TaskList.Add(newTask); + }); + foreach (var file in modpkg.filelist) + { + try + { + if (File.Exists(installed.fullPath + Path.DirectorySeparatorChar + file.filename)) + { + File.Delete(installed.fullPath + Path.DirectorySeparatorChar + file.filename); + delCount++; + } + } + catch(Exception ex) + { + Log.Add(Log.LogSeverity.Error, "TaskItemViewModel.InstallMod(delete mod file)", ex); + } + } + Log.Add(Log.LogSeverity.Information, "TaskItemViewModel.InstallMod(delete mod file)", "Files deleted: " + delCount); + installed.packages.Remove(installedPkg); + mod.packages.Remove(modpkg); + save = true; + } + } + if(save) + { + installed.SaveJson(); + } + } + int vPExtractionNeeded = 0; + for (int i = mod.packages.Count - 1; i >= 0; i--) { bool alreadyInstalled = false; @@ -3099,6 +3145,7 @@ public async Task InstallMod(Mod mod, CancellationTokenSource cancelSource } } + /* Is there is nothing new to install just end the task */ if(files.Count == 0 && !metaUpdate) { diff --git a/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs b/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs index 5668dbc5..a5bebb16 100644 --- a/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs +++ b/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs @@ -172,12 +172,33 @@ private async void UpdateSelectedVersion() SelectedMod.fullPath = installed.fullPath; foreach (var displayPkg in SelectedMod.packages) { - displayPkg.status = "optional"; - foreach (var pkg in installed.packages) + var installedPkg = installed.packages.FirstOrDefault(ip => ip.name == displayPkg.name); + if(installedPkg != null) { - if (pkg.name == displayPkg.name) + //pkg is installed + if (displayPkg.status == "required") { - displayPkg.status = "required"; + displayPkg.isEnabled = false; + } + else + { + displayPkg.isEnabled = true; + } + displayPkg.isSelected = true; + } + else + { + //pkg not installed + if(displayPkg.status == "required") + { + //rare case + displayPkg.isEnabled = false; + displayPkg.isSelected = true; + } + else + { + displayPkg.isEnabled = true; + displayPkg.isSelected = false; } } } @@ -188,6 +209,25 @@ private async void UpdateSelectedVersion() } else { + foreach(var pkg in SelectedMod.packages) + { + switch (pkg.status) + { + case "required": + pkg.isEnabled = false; + pkg.isSelected = true; + break; + case "recommended": + pkg.isEnabled = true; + pkg.isSelected = true; + break; + case "optional": + pkg.isEnabled = true; + pkg.isSelected = false; + break; + } + } + IsInstalled = false; if(ForceDevMode) @@ -241,24 +281,89 @@ private async Task ProcessMod(Mod mod, List allMods, List? processed = var modDep = await dep.SelectModNebula(allMods); if (modDep != null) { + //Is this dependecy mod already is installed? + var modInstalled = Knossos.GetInstalledMod(modDep.id, modDep.version); //Load Nebula data first to check the packages await modDep.LoadFulLNebulaData(); //Make sure to mark all needed pkgs this mod need as required modDep.isEnabled = true; modDep.isSelected = true; - foreach(var pkg in modDep.packages) + + foreach (var pkg in modDep.packages) { if (dep != null && dep.packages != null) { - foreach (var depPkg in dep.packages) + //Auto select needed packages and inform in the tooltip and change foreground + var depPkg = dep.packages.FirstOrDefault(dp => dp == pkg.name); + if (depPkg != null && pkg.status != "required") + { + pkg.isEnabled = true; + pkg.isSelected = true; + pkg.isRequired = true; + + if (!pkg.tooltip.Contains(mod.ToString())) + { + var originalPkg = mod.FindPackageWithDependency(dep.originalDependency); + if(originalPkg != null) + { + pkg.tooltip += "\nRequired by MOD: " + mod + "\nPKG: " + originalPkg.name; + } + else + { + pkg.tooltip += "\nRequired by MOD: " + mod; + } + } + } + else { - if (depPkg == pkg.name) + switch(pkg.status) { - pkg.status = "required"; + case "required": + pkg.isEnabled = false; + pkg.isSelected = true; + break; + case "recommended": + pkg.isSelected = true; + break; + case "optional": + //No need to do anything here + break; + } + } + } + + //If mod is already installed, non-installed pkgs are all unselected + //and all installed ones are selected + if (modInstalled != null && modInstalled.packages != null) + { + var packageIsInstalled = modInstalled.packages.FirstOrDefault(m => m.name == pkg.name); + if (packageIsInstalled != null) + { + //Pkg is installed + if (pkg.status == "required") + { + pkg.isEnabled = false; + } + else + { + pkg.isEnabled = true; + } + pkg.isSelected = true; + } + else + { + //Pkg is not installed + //ONLY if the currently selected mod is also installed + //For new mod or new mod version installs only if the package is not needed + if (IsInstalled || !IsInstalled && !pkg.isRequired) + { + pkg.isEnabled = true; + pkg.isSelected = false; } } } } + //If process this depmod own dependencies if we havent done already //Otherwise re-add it to the list to enabled any potential new pkg needed if (processed.IndexOf(modDep) == -1) @@ -285,25 +390,12 @@ private void AddModToList(Mod mod) if (modInList == null) { /* This mod was not added to the install list yet */ + //Copy pkg notes to tooltip foreach (var pkg in mod.packages) { - if (pkg.status!.ToLower() == "required") + if (!string.IsNullOrEmpty(pkg.notes) && !pkg.tooltip.Contains(pkg.notes)) { - pkg.isEnabled = false; - pkg.isSelected = true; - } - else - { - if (pkg.status!.ToLower() == "recommended") - { - pkg.isSelected = true; - pkg.isEnabled = true; - } - else - { - pkg.isEnabled = true; - } - + pkg.tooltip = pkg.notes + pkg.tooltip; } } ModInstallList.Add(mod); @@ -312,26 +404,22 @@ private void AddModToList(Mod mod) { foreach(var pkgInList in modInList.packages) { - /* If the mod is already added make sure recommended and requiered packages are properly marked */ - foreach (var pkg in mod.packages) + /* If the mod is already added make sure all needed packages are properly marked */ + var pkg = mod.packages.FirstOrDefault(mp => mp.name == pkgInList.name); + if(pkg != null) { - if (pkgInList == pkg) + if (pkgInList.isEnabled && !pkg.isEnabled || pkg.status!.ToLower() == "required") { - if (pkg.status!.ToLower() == "required") - { - pkgInList.isEnabled = false; - pkgInList.isSelected = true; - } - else - { - if (pkg.status!.ToLower() == "recommended") - { - pkgInList.isSelected = true; - pkgInList.isEnabled = true; - } - - } - break; + pkgInList.isEnabled = false; + pkgInList.isSelected = true; + } + if(!pkgInList.isRequired && pkg.isRequired) + { + pkgInList.isRequired = true; + } + if (!pkgInList.tooltip.Contains(pkg.tooltip)) + { + pkgInList.tooltip += pkg.tooltip; } } } diff --git a/Knossos.NET/Views/Windows/ModInstallView.axaml b/Knossos.NET/Views/Windows/ModInstallView.axaml index a131836b..b9920053 100644 --- a/Knossos.NET/Views/Windows/ModInstallView.axaml +++ b/Knossos.NET/Views/Windows/ModInstallView.axaml @@ -12,7 +12,7 @@ xmlns:models="using:Knossos.NET.Models" Icon="/Assets/knossos-icon.ico" SizeToContent="Width" - Height="500" + Height="600" Title="{Binding Title}" CanResize="True"> @@ -54,7 +54,8 @@ - + + From 719032bdc2aafdb3c0e8ee9ab4dc17b71a46b99c Mon Sep 17 00:00:00 2001 From: Salvador Cipolla Date: Fri, 26 Jan 2024 19:12:51 -0300 Subject: [PATCH 2/4] Try tp keep package selection from installed version --- .../ViewModels/Windows/ModInstallViewModel.cs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs b/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs index a5bebb16..4b70f117 100644 --- a/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs +++ b/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs @@ -1,6 +1,7 @@ using Avalonia.Controls; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; +using Knossos.NET.Classes; using Knossos.NET.Models; using Knossos.NET.Views; using System; @@ -209,6 +210,9 @@ private async void UpdateSelectedVersion() } else { + //If this mod-version is not installed we have any other version of it installed? + var otherVersionInstalled = Knossos.GetInstalledModList(SelectedMod.id)?.MaxBy(m => new SemanticVersion(m.version)); + foreach(var pkg in SelectedMod.packages) { switch (pkg.status) @@ -226,6 +230,21 @@ private async void UpdateSelectedVersion() pkg.isSelected = false; break; } + + //Copy pkg selection from the installed version if we can + if (otherVersionInstalled != null && pkg.status != "required") + { + var pkgExist = otherVersionInstalled.packages.FirstOrDefault(p=>p.name == pkg.name); + if (pkgExist != null) + { + pkg.isSelected = true; + } + else + { + pkg.isSelected = false; + } + pkg.tooltip = "\n\nNote: Selection status was copied from installed version: " + otherVersionInstalled.version; + } } IsInstalled = false; @@ -306,11 +325,11 @@ private async Task ProcessMod(Mod mod, List allMods, List? processed = var originalPkg = mod.FindPackageWithDependency(dep.originalDependency); if(originalPkg != null) { - pkg.tooltip += "\nRequired by MOD: " + mod + "\nPKG: " + originalPkg.name; + pkg.tooltip += "\n\nRequired by MOD: " + mod + "\nPKG: " + originalPkg.name; } else { - pkg.tooltip += "\nRequired by MOD: " + mod; + pkg.tooltip += "\n\nRequired by MOD: " + mod; } } } From 0aebc882edb2e539d4c52ba89687daa10d3c00ed Mon Sep 17 00:00:00 2001 From: Salvador Cipolla Date: Sun, 28 Jan 2024 07:56:40 -0300 Subject: [PATCH 3/4] fix typos --- Knossos.NET/Models/Mod.cs | 4 ++-- Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Knossos.NET/Models/Mod.cs b/Knossos.NET/Models/Mod.cs index 8bb55e8f..7a5c3893 100644 --- a/Knossos.NET/Models/Mod.cs +++ b/Knossos.NET/Models/Mod.cs @@ -956,7 +956,7 @@ public class ModPackage [JsonIgnore] public string tooltip { get; set; } = string.Empty; /// - /// Used to indicate a pkg is needed during mod install chkbox selection + /// Used to indicate a pkg is needed during mod install checkbox selection /// Used to change checkbox foreground color during mod install/modify display /// [JsonIgnore] @@ -973,7 +973,7 @@ public class ModDependency /// /// Used to store the original dependency reference during mod.GetMissingDependencies - /// Usefull to track down the original pkg this dependency belong to with + /// Useful to track down the original pkg this dependency belongs to /// mod.FindPackageWithDependency() /// Not saved in Json /// diff --git a/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs b/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs index 4b70f117..1a302cac 100644 --- a/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs +++ b/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs @@ -210,7 +210,7 @@ private async void UpdateSelectedVersion() } else { - //If this mod-version is not installed we have any other version of it installed? + //If this mod-version is not installed, do we have any other version of it installed? var otherVersionInstalled = Knossos.GetInstalledModList(SelectedMod.id)?.MaxBy(m => new SemanticVersion(m.version)); foreach(var pkg in SelectedMod.packages) @@ -312,7 +312,7 @@ private async Task ProcessMod(Mod mod, List allMods, List? processed = { if (dep != null && dep.packages != null) { - //Auto select needed packages and inform in the tooltip and change foreground + //Auto select needed packages and inform via tooltip, updating the foreground var depPkg = dep.packages.FirstOrDefault(dp => dp == pkg.name); if (depPkg != null && pkg.status != "required") { From 79ca8dc8cfe09f790616a219e84d16fc58ba41e1 Mon Sep 17 00:00:00 2001 From: Salvador Cipolla Date: Sun, 28 Jan 2024 08:06:54 -0300 Subject: [PATCH 4/4] Make sure to inform all pkgs what may reference a pkg dependency it in the tooltips --- Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs b/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs index 1a302cac..429ab0c2 100644 --- a/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs +++ b/Knossos.NET/ViewModels/Windows/ModInstallViewModel.cs @@ -320,14 +320,17 @@ private async Task ProcessMod(Mod mod, List allMods, List? processed = pkg.isSelected = true; pkg.isRequired = true; - if (!pkg.tooltip.Contains(mod.ToString())) + var originalPkg = mod.FindPackageWithDependency(dep.originalDependency); + if(originalPkg != null) { - var originalPkg = mod.FindPackageWithDependency(dep.originalDependency); - if(originalPkg != null) + if (!pkg.tooltip.Contains(mod + "\nPKG: " + originalPkg.name)) { pkg.tooltip += "\n\nRequired by MOD: " + mod + "\nPKG: " + originalPkg.name; } - else + } + else + { + if (!pkg.tooltip.Contains(mod.ToString())) { pkg.tooltip += "\n\nRequired by MOD: " + mod; }