Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for installing .ckan files and DarkKAN mods #4006

Merged
merged 1 commit into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 70 additions & 94 deletions Cmdline/Action/Install.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,119 +31,88 @@ public Install(GameInstanceManager mgr, RepositoryDataManager repoData, IUser us
/// </returns>
public int RunCommand(CKAN.GameInstance instance, object raw_options)
{
InstallOptions options = (InstallOptions) raw_options;
var options = raw_options as InstallOptions;
if (options.modules.Count == 0 && options.ckan_files == null)
{
// What? No mods specified?
user.RaiseMessage("{0}:", Properties.Resources.Usage);
user.RaiseMessage(
" ckan install Mod [Mod2, ...] [--with-suggests] [--with-all-suggests] [--no-recommends]");
user.RaiseMessage(
" ckan install -c file_or_url.ckan [file_or_url2.ckan, ...] [--with-suggests] [--with-all-suggests] [--no-recommends]");
return Exit.BADOPT;
}

var regMgr = RegistryManager.Instance(instance, repoData);
List<CkanModule> modules = null;

if (options.ckan_files != null)
{
// Oooh! We're installing from a CKAN file.
foreach (string ckan_file in options.ckan_files)
// Install from CKAN files
try
{
Uri ckan_uri;

// Check if the argument if a wellformatted Uri.
if (!Uri.IsWellFormedUriString(ckan_file, UriKind.Absolute))
{
// Assume it is a local file, check if the file exists.
if (File.Exists(ckan_file))
{
// Get the full path of the file.
ckan_uri = new Uri(Path.GetFullPath(ckan_file));
}
else
{
// We have no further ideas as what we can do with this Uri, tell the user.
user.RaiseError(Properties.Resources.InstallNotFound, ckan_file);
return Exit.ERROR;
}
}
else
{
ckan_uri = new Uri(ckan_file);
}

string filename = string.Empty;

// If it is a local file, we already know the filename. If it is remote, create a temporary file and download the remote resource.
if (ckan_uri.IsFile)
{
filename = ckan_uri.LocalPath;
log.InfoFormat("Installing from local CKAN file \"{0}\"", filename);
}
else
{
log.InfoFormat("Installing from remote CKAN file \"{0}\"", ckan_uri);
filename = Net.Download(ckan_uri, null, user);

log.DebugFormat("Temporary file for \"{0}\" is at \"{1}\".", ckan_uri, filename);
}

// Parse the JSON file.
try
{
CkanModule m = MainClass.LoadCkanFromFile(filename);
options.modules.Add($"{m.identifier}={m.version}");
}
catch (Kraken kraken)
{
user.RaiseError("{0}",
kraken.InnerException == null
? kraken.Message
: $"{kraken.Message}: {kraken.InnerException.Message}");
}
var targets = options.ckan_files
.Select(arg => new NetAsyncDownloader.DownloadTarget(getUri(arg)))
.ToList();
log.DebugFormat("Urls: {0}", targets.SelectMany(t => t.urls));
new NetAsyncDownloader(new NullUser()).DownloadAndWait(targets);
log.DebugFormat("Files: {0}", targets.Select(t => t.filename));
modules = targets.Select(t => MainClass.LoadCkanFromFile(t.filename))
.ToList();
}
catch (FileNotFoundKraken kraken)
{
user.RaiseError(Properties.Resources.InstallNotFound,
kraken.file);
return Exit.ERROR;
}
catch (Kraken kraken)
{
user.RaiseError("{0}",
kraken.InnerException == null
? kraken.Message
: $"{kraken.Message}: {kraken.InnerException.Message}");
return Exit.ERROR;
}

// At times RunCommand() calls itself recursively - in this case we do
// not want to be doing this again, so "consume" the option
options.ckan_files = null;
}
else
{
Search.AdjustModulesCase(instance,
RegistryManager.Instance(instance, repoData).registry,
options.modules);
var identifiers = options.modules;
var registry = regMgr.registry;
var installed = registry.InstalledModules
.Select(im => im.Module)
.ToArray();
var crit = instance.VersionCriteria();
Search.AdjustModulesCase(instance, registry, identifiers);
modules = identifiers.Select(arg => CkanModule.FromIDandVersion(
registry, arg,
options.allow_incompatible
? null
: crit)
?? registry.LatestAvailable(arg, crit,
null, installed)
?? registry.InstalledModule(arg)?.Module)
.ToList();
}

if (options.modules.Count == 0)
{
// What? No files specified?
user.RaiseMessage(
$"{Properties.Resources.Usage}: ckan install [--with-suggests] [--with-all-suggests] [--no-recommends] [--headless] Mod [Mod2, ...]");
return Exit.BADOPT;
}

// Prepare options. Can these all be done in the new() somehow?
var installer = new ModuleInstaller(instance, manager.Cache, user);
var install_ops = new RelationshipResolverOptions
{
with_all_suggests = options.with_all_suggests,
with_suggests = options.with_suggests,
with_recommends = !options.no_recommends,
allow_incompatible = options.allow_incompatible
with_all_suggests = options.with_all_suggests,
with_suggests = options.with_suggests,
with_recommends = !options.no_recommends,
allow_incompatible = options.allow_incompatible,
without_toomanyprovides_kraken = user.Headless,
without_enforce_consistency = user.Headless,
};

if (user.Headless)
{
install_ops.without_toomanyprovides_kraken = true;
install_ops.without_enforce_consistency = true;
}

var regMgr = RegistryManager.Instance(instance, repoData);
List<string> modules = options.modules;

for (bool done = false; !done; )
{
// Install everything requested. :)
try
{
HashSet<string> possibleConfigOnlyDirs = null;
var installer = new ModuleInstaller(instance, manager.Cache, user);
installer.InstallList(modules.Select(arg => CkanModule.FromIDandVersion(
regMgr.registry, arg,
options.allow_incompatible
? null
: instance.VersionCriteria()))
.ToList(),
install_ops,
regMgr,
installer.InstallList(modules, install_ops, regMgr,
ref possibleConfigOnlyDirs);
user.RaiseMessage("");
done = true;
Expand Down Expand Up @@ -199,7 +168,7 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
}

// Add the module to the list.
modules.Add($"{ex.modules[result].identifier}={ex.modules[result].version}");
modules.Add(ex.modules[result]);
// DON'T return so we can loop around and try again
}
catch (FileExistsKraken ex)
Expand Down Expand Up @@ -275,6 +244,13 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
return Exit.OK;
}

private Uri getUri(string arg)
=> Uri.IsWellFormedUriString(arg, UriKind.Absolute)
? new Uri(arg)
: File.Exists(arg)
? new Uri(Path.GetFullPath(arg))
: throw new FileNotFoundKraken(arg);

private readonly GameInstanceManager manager;
private readonly RepositoryDataManager repoData;
private readonly IUser user;
Expand Down
26 changes: 22 additions & 4 deletions Core/Registry/IRegistryQuerier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,29 @@ private static bool MetadataChanged(this IRegistryQuerier querier, string identi
/// <returns>
/// String describing range of compatible game versions.
/// </returns>
public static string CompatibleGameVersions(this IRegistryQuerier querier, IGame game, string identifier)
public static string CompatibleGameVersions(this IRegistryQuerier querier,
IGame game,
string identifier)
{
List<CkanModule> releases = querier.AvailableByIdentifier(identifier).ToList();
if (releases != null && releases.Count > 0) {
CkanModule.GetMinMaxVersions(releases, out _, out _, out GameVersion minKsp, out GameVersion maxKsp);
List<CkanModule> releases = null;
try
{
releases = querier.AvailableByIdentifier(identifier)
.ToList();
}
catch
{
var instMod = querier.InstalledModule(identifier);
if (instMod != null)
{
releases = Enumerable.Repeat(instMod.Module, 1)
.ToList();
}
}
if (releases != null && releases.Count > 0)
{
CkanModule.GetMinMaxVersions(releases, out _, out _,
out GameVersion minKsp, out GameVersion maxKsp);
return GameVersionRange.VersionSpan(game, minKsp, maxKsp);
}
return "";
Expand Down
5 changes: 4 additions & 1 deletion GUI/Main/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,10 @@ private void InstallFromCkanFiles(string[] files)
Properties.Resources.AllModVersionsInstallYes,
Properties.Resources.AllModVersionsInstallNo))
{
InstallModuleDriver(registry_manager.registry, toInstall);
UpdateChangesDialog(toInstall.Select(m => new ModChange(m, GUIModChangeType.Install))
.ToList(),
null);
tabController.ShowTab("ChangesetTabPage", 1);
}
}

Expand Down
62 changes: 33 additions & 29 deletions GUI/Main/MainInstall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,40 +131,45 @@ private void InstallMods(object sender, DoWorkEventArgs e)
}

Util.Invoke(this, () => UseWaitCursor = true);
// Prompt for recommendations and suggestions, if any
if (installer.FindRecommendations(
changes.Where(ch => ch.ChangeType == GUIModChangeType.Install)
.Select(ch => ch.Mod)
.ToHashSet(),
toInstall,
registry,
out Dictionary<CkanModule, Tuple<bool, List<string>>> recommendations,
out Dictionary<CkanModule, List<string>> suggestions,
out Dictionary<CkanModule, HashSet<string>> supporters))
try
{
tabController.ShowTab("ChooseRecommendedModsTabPage", 3);
ChooseRecommendedMods.LoadRecommendations(
registry, toInstall, toUninstall,
CurrentInstance.VersionCriteria(), Manager.Cache,
recommendations, suggestions, supporters);
tabController.SetTabLock(true);
Util.Invoke(this, () => UseWaitCursor = false);
var result = ChooseRecommendedMods.Wait();
tabController.SetTabLock(false);
tabController.HideTab("ChooseRecommendedModsTabPage");
if (result == null)
{
e.Result = new InstallResult(false, changes);
throw new CancelledActionKraken();
}
else
// Prompt for recommendations and suggestions, if any
if (installer.FindRecommendations(
changes.Where(ch => ch.ChangeType == GUIModChangeType.Install)
.Select(ch => ch.Mod)
.ToHashSet(),
toInstall,
registry,
out Dictionary<CkanModule, Tuple<bool, List<string>>> recommendations,
out Dictionary<CkanModule, List<string>> suggestions,
out Dictionary<CkanModule, HashSet<string>> supporters))
{
toInstall = toInstall.Concat(result).Distinct().ToList();
tabController.ShowTab("ChooseRecommendedModsTabPage", 3);
ChooseRecommendedMods.LoadRecommendations(
registry, toInstall, toUninstall,
CurrentInstance.VersionCriteria(), Manager.Cache,
recommendations, suggestions, supporters);
tabController.SetTabLock(true);
Util.Invoke(this, () => UseWaitCursor = false);
var result = ChooseRecommendedMods.Wait();
tabController.SetTabLock(false);
tabController.HideTab("ChooseRecommendedModsTabPage");
if (result == null)
{
e.Result = new InstallResult(false, changes);
throw new CancelledActionKraken();
}
else
{
toInstall = toInstall.Concat(result).Distinct().ToList();
}
}
}
else
finally
{
// Make sure the progress tab always shows up with a normal cursor even if an exception is thrown
Util.Invoke(this, () => UseWaitCursor = false);
ShowWaitDialog();
}

// Now let's make all our changes.
Expand All @@ -173,7 +178,6 @@ private void InstallMods(object sender, DoWorkEventArgs e)
// Need to be on the GUI thread to get the translated string
tabController.RenameTab("WaitTabPage", Properties.Resources.MainInstallWaitTitle);
});
ShowWaitDialog();
tabController.SetTabLock(true);

IDownloader downloader = new NetAsyncModulesDownloader(currentUser, Manager.Cache);
Expand Down
7 changes: 5 additions & 2 deletions GUI/Main/MainWait.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,13 @@ public void HideWaitDialog()
/// <param name="description">Message displayed above the DialogProgress bar</param>
public void FailWaitDialog(string statusMsg, string logMsg, string description)
{
Util.Invoke(statusStrip1, () => {
Util.Invoke(statusStrip1, () =>
{
StatusProgress.Visible = false;
currentUser.RaiseMessage(statusMsg);
});
Util.Invoke(WaitTabPage, () => {
Util.Invoke(WaitTabPage, () =>
{
RecreateDialogs();
Wait.Finish();
});
Expand All @@ -65,6 +67,7 @@ public void FailWaitDialog(string statusMsg, string logMsg, string description)

public void Wait_OnRetry()
{
EnableMainWindow();
tabController.ShowTab("ChangesetTabPage", 1);
}

Expand Down
Loading