Skip to content

Commit

Permalink
Fix slow response to checkbox changes
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Mar 12, 2018
1 parent 624839f commit 6a80031
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 294 deletions.
136 changes: 63 additions & 73 deletions Core/Registry/Registry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ public class Registry : IEnlistmentNotification, IRegistryQuerier
[JsonProperty] private Dictionary<string, InstalledModule> installed_modules;
[JsonProperty] private Dictionary<string, string> installed_files; // filename => module

// Index of which mods provide what, format:
// providers[provided] = { provider1, provider2, ... }
// Built by BuildProvidesIndex, makes LatestAvailableWithProvides much faster.
[JsonIgnore] private Dictionary<string, HashSet<AvailableModule>> providers
= new Dictionary<string, HashSet<AvailableModule>>();

[JsonIgnore] private string transaction_backup;

/// <summary>
Expand Down Expand Up @@ -72,8 +78,7 @@ [JsonIgnore] public IEnumerable<string> InstalledDlls
private void DeSerialisationFixes(StreamingContext context)
{
// Our context is our KSP install.
KSP ksp = (KSP) context.Context;

KSP ksp = (KSP)context.Context;

// Older registries didn't have the installed_files list, so we create one
// if absent.
Expand All @@ -90,7 +95,7 @@ private void DeSerialisationFixes(StreamingContext context)
{
log.Warn("Older registry format detected, normalising paths...");

var normalised_installed_files = new Dictionary<string,string>();
var normalised_installed_files = new Dictionary<string, string>();

foreach (KeyValuePair<string,string> tuple in installed_files)
{
Expand Down Expand Up @@ -177,6 +182,7 @@ private void DeSerialisationFixes(StreamingContext context)
}

registry_version = LATEST_REGISTRY_VERSION;
BuildProvidesIndex();
}

/// <summary>
Expand Down Expand Up @@ -210,20 +216,20 @@ public void Repair()
#region Constructors

public Registry(
Dictionary<string, InstalledModule> installed_modules,
Dictionary<string, string> installed_dlls,
Dictionary<string, AvailableModule> available_modules,
Dictionary<string, string> installed_files,
SortedDictionary<string, Repository> repositories
)
Dictionary<string, InstalledModule> installed_modules,
Dictionary<string, string> installed_dlls,
Dictionary<string, AvailableModule> available_modules,
Dictionary<string, string> installed_files,
SortedDictionary<string, Repository> repositories)
{
// Is there a better way of writing constructors than this? Srsly?
this.installed_modules = installed_modules;
this.installed_dlls = installed_dlls;
this.installed_dlls = installed_dlls;
this.available_modules = available_modules;
this.installed_files = installed_files;
this.repositories = repositories;
registry_version = LATEST_REGISTRY_VERSION;
this.installed_files = installed_files;
this.repositories = repositories;
registry_version = LATEST_REGISTRY_VERSION;
BuildProvidesIndex();
}

// If deserialsing, we don't want everything put back directly,
Expand Down Expand Up @@ -357,6 +363,7 @@ public void SetAllAvailable(IEnumerable<CkanModule> newAvail)
{
AddAvailable(module);
}
BuildProvidesIndex();
}

/// <summary>
Expand Down Expand Up @@ -390,6 +397,7 @@ public void AddAvailable(CkanModule module)

log.DebugFormat("Available: {0} version {1}", identifier, module.version);
available_modules[identifier].Add(module);
BuildProvidesIndexFor(available_modules[identifier]);
}

/// <summary>
Expand Down Expand Up @@ -425,15 +433,13 @@ public List<CkanModule> Available(KspVersionCriteria ksp_version)
// It's nice to see things in alphabetical order, so sort our keys first.
candidates.Sort();

//Cache
CkanModule[] 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)
{
CkanModule available = LatestAvailable(candidate, ksp_version);

if (available != null
&& allDependenciesCompatible(available, ksp_version, modules_for_current_version))
&& allDependenciesCompatible(available, ksp_version))
{
compatible.Add(available);
}
Expand All @@ -449,11 +455,6 @@ public List<CkanModule> Incompatible(KspVersionCriteria ksp_version)
var candidates = new List<string>(available_modules.Keys);
var incompatible = new List<CkanModule>();

CkanModule[] modules_for_current_version = available_modules.Values
.Select(pair => pair.Latest(ksp_version))
.Where(mod => mod != null)
.ToArray();

// It's nice to see things in alphabetical order, so sort our keys first.
candidates.Sort();

Expand All @@ -464,7 +465,7 @@ public List<CkanModule> Incompatible(KspVersionCriteria ksp_version)

// If a mod is available, it might still have incompatible dependencies.
if (available == null
|| !allDependenciesCompatible(available, ksp_version, modules_for_current_version))
|| !allDependenciesCompatible(available, ksp_version))
{
incompatible.Add(LatestAvailable(candidate, null));
}
Expand All @@ -473,7 +474,7 @@ public List<CkanModule> Incompatible(KspVersionCriteria ksp_version)
return incompatible;
}

private bool allDependenciesCompatible(CkanModule mod, KspVersionCriteria ksp_version, CkanModule[] modules_for_current_version)
private bool allDependenciesCompatible(CkanModule mod, KspVersionCriteria ksp_version)
{
// we need to check that we can get everything we depend on
if (mod.depends != null)
Expand All @@ -482,7 +483,7 @@ private bool allDependenciesCompatible(CkanModule mod, KspVersionCriteria ksp_ve
{
try
{
if (!LatestAvailableWithProvides(dependency.name, ksp_version, modules_for_current_version).Any())
if (!LatestAvailableWithProvides(dependency.name, ksp_version).Any())
{
return false;
}
Expand Down Expand Up @@ -518,7 +519,7 @@ public CkanModule LatestAvailable(

try
{
return available_modules[module].Latest(ksp_version,relationship_descriptor);
return available_modules[module].Latest(ksp_version, relationship_descriptor);
}
catch (KeyNotFoundException)
{
Expand Down Expand Up @@ -604,66 +605,53 @@ public static void GetMinMaxVersions(IEnumerable<CkanModule> modVersions,
}

/// <summary>
/// <see cref = "IRegistryQuerier.LatestAvailableWithProvides" />
/// Generate the providers index so we can find providing modules quicker
/// </summary>
public List<CkanModule> LatestAvailableWithProvides(string module, KspVersionCriteria ksp_version, RelationshipDescriptor relationship_descriptor = null)
private void BuildProvidesIndex()
{
// Calculates a cache of modules which
// are compatible with the current version of KSP, and then
// calls the private version below for heavy lifting.
return LatestAvailableWithProvides(module, ksp_version,
available_modules.Values.Select(pair => pair.Latest(ksp_version)).Where(mod => mod != null).ToArray(),
relationship_descriptor);
providers.Clear();
foreach (AvailableModule am in available_modules.Values)
{
BuildProvidesIndexFor(am);
}
}

/// <summary>
/// Returns the latest version of a module that can be installed for
/// the given KSP version. This is a *private* method that assumes
/// the `available_for_current_version` list has been correctly
/// calculated. Not for direct public consumption. ;)
/// </summary>
private List<CkanModule> LatestAvailableWithProvides(string module, KspVersionCriteria ksp_version,
IEnumerable<CkanModule> available_for_current_version, RelationshipDescriptor relationship_descriptor=null)
private void BuildProvidesIndexFor(AvailableModule am)
{
log.DebugFormat("Finding latest available with provides for {0}", module);

// TODO: Check user's stability tolerance (stable, unstable, testing, etc)

var modules = new List<CkanModule>();

try
foreach (CkanModule m in am.AllAvailable())
{
// If we can find the module requested for our KSP, use that.
CkanModule mod = LatestAvailable(module, ksp_version, relationship_descriptor);
if (mod != null)
foreach (string provided in m.ProvidesList)
{
modules.Add(mod);
HashSet<AvailableModule> provs = null;
if (providers.TryGetValue(provided, out provs))
provs.Add(am);
else
providers.Add(provided, new HashSet<AvailableModule>() { am });
}
}
catch (ModuleNotFoundKraken)
}

/// <summary>
/// <see cref="IRegistryQuerier.LatestAvailableWithProvides" />
/// </summary>
public List<CkanModule> LatestAvailableWithProvides(
string module,
KspVersionCriteria ksp_version,
RelationshipDescriptor relationship_descriptor = null)
{
HashSet<AvailableModule> provs;
if (providers.TryGetValue(module, out provs))
{
// It's cool if we can't find it, though.
// For each AvailableModule, we want the latest one matching our constraints
return provs.Select(am => am.Latest(ksp_version, relationship_descriptor))
.Where(m => m?.ProvidesList?.Contains(module) ?? false)
.ToList();
}

// Walk through all our available modules, and see if anything
// provides what we need.

// Get our candidate module. We can assume this is non-null, as
// if it *is* null then available_for_current_version is corrupted,
// and something is terribly wrong.
foreach (CkanModule candidate in available_for_current_version)
else
{
// Find everything this module provides (for our version of KSP)
List<string> provides = candidate.provides;

// If the module has provides, and any of them are what we're looking
// for, the add it to our list.
if (provides != null && provides.Any(provided => provided == module))
{
modules.Add(candidate);
}
// Nothing provides this, return empty list
return new List<CkanModule>();
}
return modules;
}

/// <summary>
Expand Down Expand Up @@ -948,7 +936,9 @@ public Version InstalledVersion(string modIdentifier, bool with_provides=true)
public CkanModule GetInstalledVersion(string mod_identifer)
{
InstalledModule installedModule;
return installed_modules.TryGetValue(mod_identifer, out installedModule) ? installedModule.Module : null;
return installed_modules.TryGetValue(mod_identifer, out installedModule)
? installedModule.Module
: null;
}

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions GUI/CKAN-GUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
<SubType>Form</SubType>
</Compile>
<Compile Include="GUIMod.cs" />
<Compile Include="GUIUser.cs" />
<Compile Include="HintTextBox.cs">
<SubType>Component</SubType>
</Compile>
Expand Down
11 changes: 6 additions & 5 deletions GUI/GUIMod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,18 +242,19 @@ public void SetInstallChecked(DataGridViewRow row, bool? set_value_to = null)
if (install_cell != null)
{
bool changeTo = set_value_to ?? (bool)install_cell.Value;
//Need to do this check here to prevent an infinite loop
//which is at least happening on Linux
//TODO: Eliminate the cause
if (changeTo != IsInstallChecked)
if (IsInstallChecked != changeTo)
{
IsInstallChecked = changeTo;
}
// Setting this property causes ModList_CellValueChanged to be called,
// which calls SetInstallChecked again. Treat it conservatively.
if ((bool)install_cell.Value != IsInstallChecked)
{
install_cell.Value = IsInstallChecked;
}
}
}


private bool Equals(GUIMod other)
{
return Equals(Name, other.Name);
Expand Down
39 changes: 39 additions & 0 deletions GUI/GUIUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;

namespace CKAN
{

public class GUIUser : NullUser
{
public delegate bool DisplayYesNo(string message);

public Action<string, object[]> displayMessage;
public Action<string, object[]> displayError;
public DisplayYesNo displayYesNo;

protected override bool DisplayYesNoDialog(string message)
{
if (displayYesNo == null)
return true;

return displayYesNo(message);
}

protected override void DisplayMessage(string message, params object[] args)
{
displayMessage(message, args);
}

protected override void DisplayError(string message, params object[] args)
{
displayError(message, args);
}

protected override void ReportProgress(string format, int percent)
{
Main.Instance.SetDescription($"{format} - {percent}%");
Main.Instance.SetProgress(percent);
}
}

}
Loading

0 comments on commit 6a80031

Please sign in to comment.