Skip to content

Commit

Permalink
Merge KSP-CKAN#3351 Better recovery when registry.json is corrupted
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed May 6, 2021
2 parents dbcab22 + 2b14573 commit 72c4a56
Show file tree
Hide file tree
Showing 12 changed files with 74 additions and 26 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ All notable changes to this project will be documented in this file.
- [ConsoleUI] Update or refresh ConsoleUI mod list after repo or compat changes (#3353 by: HebaruSan; reviewed: DasSkelett)
- [Multiple] Replace repo-reinst with kraken, handle in UIs (#3344 by: HebaruSan; reviewed: DasSkelett)
- [Multiple] Make ModuleInstaller a non-singleton (#3356 by: HebaruSan; reviewed: DasSkelett)
- [Core] Better recovery when registry.json is corrupted (#3351 by: HebaruSan; reviewed: DasSkelett)

### Internal

Expand Down
4 changes: 4 additions & 0 deletions Core/GameInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public class GameInstance : IEquatable<GameInstance>
private List<GameVersion> _compatibleVersions = new List<GameVersion>();

public string Name { get; set; }
/// <summary>
/// Returns a file system safe version of the instance name that can be used within file names.
/// </summary>
public string SanitizedName => string.Join("", Name.Split(Path.GetInvalidFileNameChars()));
public GameVersion GameVersionWhenCompatibleVersionsWereStored { get; private set; }
public bool CompatibleVersionsAreFromDifferentGameVersion { get { return _compatibleVersions.Count > 0 && GameVersionWhenCompatibleVersionsWereStored != Version(); } }

Expand Down
32 changes: 24 additions & 8 deletions Core/Registry/RegistryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public class RegistryManager : IDisposable
private FileStream lockfileStream = null;
private StreamWriter lockfileWriter = null;


// The only reason we have a KSP field is so we can pass it to the registry
// when deserialising, and *it* only needs it to do registry upgrades.
// We could get rid of all of this if we declare we no longer wish to support
Expand All @@ -32,6 +31,15 @@ public class RegistryManager : IDisposable

public Registry registry;

/// <summary>
/// If loading the registry failed, the parsing error text, else null.
/// </summary>
public string previousCorruptedMessage;
/// <summary>
/// If loading the registry failed, the location to which we moved it, else null.
/// </summary>
public string previousCorruptedPath;

// We require our constructor to be private so we can
// enforce this being an instance (via Instance() above)
private RegistryManager(string path, GameInstance ksp)
Expand Down Expand Up @@ -309,6 +317,16 @@ private void LoadOrCreate()
Create();
Load();
}
catch (JsonException exc)
{
previousCorruptedMessage = exc.Message;
previousCorruptedPath = path + "_CORRUPTED_" + DateTime.Now.ToString("yyyyMMddHHmmss");
log.ErrorFormat("{0} is corrupted, archiving to {1}: {2}",
path, previousCorruptedPath, previousCorruptedMessage);
File.Move(path, previousCorruptedPath);
Create();
Load();
}
catch (Exception ex)
{
log.ErrorFormat("Uncaught exception loading registry: {0}", ex.ToString());
Expand Down Expand Up @@ -382,25 +400,23 @@ public void Save(bool enforce_consistency = true)

file_transaction.WriteAllText(path, Serialize());

string sanitizedName = string.Join("", ksp.Name.Split(Path.GetInvalidFileNameChars()));

ExportInstalled(
Path.Combine(directoryPath, $"installed-{sanitizedName}.ckan"),
Path.Combine(directoryPath, LatestInstalledExportFilename()),
false, true
);
if (!Directory.Exists(ksp.InstallHistoryDir()))
{
Directory.CreateDirectory(ksp.InstallHistoryDir());
}
ExportInstalled(
Path.Combine(
ksp.InstallHistoryDir(),
$"installed-{sanitizedName}-{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.ckan"
),
Path.Combine(ksp.InstallHistoryDir(), HistoricInstalledExportFilename()),
false, true
);
}

public string LatestInstalledExportFilename() => $"installed-{ksp.SanitizedName}.ckan";
public string HistoricInstalledExportFilename() => $"installed-{ksp.SanitizedName}-{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.ckan";

/// <summary>
/// Save a custom .ckan file that contains all the currently
/// installed mods as dependencies.
Expand Down
2 changes: 1 addition & 1 deletion GUI/Controls/ManageMods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1566,7 +1566,7 @@ public bool AllowClose()
.Select(grp => $"{grp.Key}: "
+ grp.Aggregate((a, b) => $"{a}, {b}"))
.Aggregate((a, b) => $"{a}\r\n{b}");
if (!Main.Instance.YesNoDialog(string.Format(Properties.Resources.MainQuitWIthUnappliedChanges, changeDescrip),
if (!Main.Instance.YesNoDialog(string.Format(Properties.Resources.MainQuitWithUnappliedChanges, changeDescrip),
Properties.Resources.MainQuit,
Properties.Resources.MainGoBack))
{
Expand Down
4 changes: 2 additions & 2 deletions GUI/Dialogs/CloneFakeGameDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private void comboBoxKnownInstance_SelectedIndexChanged(object sender, EventArgs
}

/// <summary>
/// Open an file dialog to search for a KSP instance, like in <code>ManageKspInstances</code>.
/// Open an file dialog to search for a game instance, like in <code>ManageGameInstancesDialog</code>.
/// </summary>
private void buttonInstancePathSelection_Click(object sender, EventArgs e)
{
Expand All @@ -72,7 +72,7 @@ private void buttonInstancePathSelection_Click(object sender, EventArgs e)
Multiselect = false
};

// Show the FileDialog and let the user search for the KSP directory.
// Show the FileDialog and let the user search for the game directory.
if (instanceDialog.ShowDialog() != DialogResult.OK || !File.Exists(instanceDialog.FileName))
return;

Expand Down
33 changes: 25 additions & 8 deletions GUI/Main/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,8 @@
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using CKAN.Versioning;
using CKAN.Extensions;
using CKAN.Properties;
using CKAN.Types;
using log4net;
using Timer = System.Windows.Forms.Timer;
using Autofac;
Expand Down Expand Up @@ -127,7 +123,18 @@ public Main(string[] cmdlineArgs, GameInstanceManager mgr, bool showConsole)
try
{
#pragma warning disable 219
var lockedReg = RegistryManager.Instance(CurrentInstance).registry;
var regMgr = RegistryManager.Instance(CurrentInstance);
var lockedReg = regMgr.registry;
// Tell the user their registry was reset if it was corrupted
if (!string.IsNullOrEmpty(regMgr.previousCorruptedMessage)
&& !string.IsNullOrEmpty(regMgr.previousCorruptedPath))
{
errorDialog.ShowErrorDialog(Properties.Resources.MainCorruptedRegistry,
regMgr.previousCorruptedPath, regMgr.previousCorruptedMessage,
Path.Combine(Path.GetDirectoryName(regMgr.previousCorruptedPath) ?? "", regMgr.LatestInstalledExportFilename()));
regMgr.previousCorruptedMessage = null;
regMgr.previousCorruptedPath = null;
}
#pragma warning restore 219
}
catch (RegistryInUseKraken kraken)
Expand Down Expand Up @@ -366,8 +373,18 @@ private void CurrentInstanceUpdated(bool allowRepoUpdate)
.ShowDialog();
}

(RegistryManager.Instance(CurrentInstance).registry as Registry)
?.BuildTagIndex(ManageMods.mainModList.ModuleTags);
var regMgr = RegistryManager.Instance(CurrentInstance);
var registry = regMgr.registry;
if (!string.IsNullOrEmpty(regMgr.previousCorruptedMessage)
&& !string.IsNullOrEmpty(regMgr.previousCorruptedPath))
{
errorDialog.ShowErrorDialog(Properties.Resources.MainCorruptedRegistry,
regMgr.previousCorruptedPath, regMgr.previousCorruptedMessage,
Path.Combine(Path.GetDirectoryName(regMgr.previousCorruptedPath) ?? "", regMgr.LatestInstalledExportFilename()));
regMgr.previousCorruptedMessage = null;
regMgr.previousCorruptedPath = null;
}
registry?.BuildTagIndex(ManageMods.mainModList.ModuleTags);

bool repoUpdateNeeded = configuration.RefreshOnStartup
|| !RegistryManager.Instance(CurrentInstance).registry.HasAnyAvailable();
Expand Down Expand Up @@ -466,7 +483,7 @@ private void installFromckanToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog open_file_dialog = new OpenFileDialog()
{
Filter = Resources.CKANFileFilter,
Filter = Properties.Resources.CKANFileFilter,
Multiselect = true,
};

Expand Down
7 changes: 5 additions & 2 deletions GUI/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion GUI/Properties/Resources.de-DE.resx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ Versuche {2} aus {3} zu verschieben und CKAN neu zu starten.</value></data>
<data name="MainQuit" xml:space="preserve"><value>Beenden</value></data>
<data name="MainGoBack" xml:space="preserve"><value>Zurück</value></data>
<data name="MainQuitWithUnmetDeps" xml:space="preserve"><value>Es gibt unerfüllte Abhängigkeiten. Trotzdem beenden?</value></data>
<data name="MainQuitWIthUnappliedChanges" xml:space="preserve">
<data name="MainQuitWithUnappliedChanges" xml:space="preserve">
<value>Du hast noch nicht übernommene Änderungen. Trotzdem beenden?

{0}</value>
Expand Down Expand Up @@ -182,6 +182,10 @@ Versuche {2} aus {3} zu verschieben und CKAN neu zu starten.</value></data>
<data name="MainPlainText" xml:space="preserve"><value>Klartext (*.txt)</value></data>
<data name="MainNotFound" xml:space="preserve"><value>Nicht gefunden.</value></data>
<data name="MainReinstallConfirm" xml:space="preserve"><value>Möchtest du {0} neu installieren?</value></data>
<data name="MainCantInstallDLC" xml:space="preserve"><value>CKAN kann die Erweiterung {0} nicht installieren!</value></data>
<data name="MainCorruptedRegistry" xml:space="preserve"><value>Beschädigte Registry wurde nach {0} archiviert: {1}

Das bedeuted, dass CKAN alle installierten Mods vergessen hat, sie befinden sich aber immer noch in GameData. Du kannst sie durch Importieren folgender Datei erneut installieren: {2}</value></data>
<data name="AllModVersionsInstallPrompt" xml:space="preserve"><value>{0} wird von Ihrer aktuellen Spielversion nicht unterstützt und funktioniert möglicherweise überhaupt nicht. Wenn Sie Probleme damit haben, sollten Sie die Betreuer NICHT um Hilfe bitten.

Möchten Sie es wirklich installieren?</value></data>
Expand Down
2 changes: 1 addition & 1 deletion GUI/Properties/Resources.fr-FR.resx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ Essayez de déplacer {2} en dehors de {3} et redémarrez CKAN.</value></data>
<data name="MainQuit" xml:space="preserve"><value>Quitter</value></data>
<data name="MainGoBack" xml:space="preserve"><value>Revenir</value></data>
<data name="MainQuitWithUnmetDeps" xml:space="preserve"><value>Il y a des dépendances non-satisfaites. Voulez-vous vraiment quitter?</value></data>
<data name="MainQuitWIthUnappliedChanges" xml:space="preserve"><value>Il y a des changements non pris en compte. Voulez-vous vraiment quitter?
<data name="MainQuitWithUnappliedChanges" xml:space="preserve"><value>Il y a des changements non pris en compte. Voulez-vous vraiment quitter?

{0}</value></data>
<data name="MainUpgradingWaitTitle" xml:space="preserve"><value>Mis à jour de CKAN</value></data>
Expand Down
2 changes: 1 addition & 1 deletion GUI/Properties/Resources.pt-BR.resx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ Tentando mover {2} de {3} e reiniciar o CKAN.</value></data>
<data name="MainQuit" xml:space="preserve"><value>Sair</value></data>
<data name="MainGoBack" xml:space="preserve"><value>Voltar</value></data>
<data name="MainQuitWithUnmetDeps" xml:space="preserve"><value>Existem dependências não atendidas. Deseja realmente sair?</value></data>
<data name="MainQuitWIthUnappliedChanges" xml:space="preserve"><value>Você tem alterações não aplicadas. Deseja realmente sair?
<data name="MainQuitWithUnappliedChanges" xml:space="preserve"><value>Você tem alterações não aplicadas. Deseja realmente sair?

{0}</value></data>
<data name="MainUpgradingWaitTitle" xml:space="preserve"><value>Atualizando o CKAN</value></data>
Expand Down
5 changes: 4 additions & 1 deletion GUI/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ Try to move {2} out of {3} and restart CKAN.</value></data>
<data name="MainQuit" xml:space="preserve"><value>Quit</value></data>
<data name="MainGoBack" xml:space="preserve"><value>Go Back</value></data>
<data name="MainQuitWithUnmetDeps" xml:space="preserve"><value>There are unmet dependencies. Really quit?</value></data>
<data name="MainQuitWIthUnappliedChanges" xml:space="preserve"><value>You have unapplied changes. Really quit?
<data name="MainQuitWithUnappliedChanges" xml:space="preserve"><value>You have unapplied changes. Really quit?

{0}</value></data>
<data name="MainUpgradingWaitTitle" xml:space="preserve"><value>Updating CKAN</value></data>
Expand Down Expand Up @@ -207,6 +207,9 @@ Try to move {2} out of {3} and restart CKAN.</value></data>
<data name="MainNotFound" xml:space="preserve"><value>Not found.</value></data>
<data name="MainReinstallConfirm" xml:space="preserve"><value>Do you want to reinstall {0}?</value></data>
<data name="MainCantInstallDLC" xml:space="preserve"><value>CKAN can't install expansion {0}!</value></data>
<data name="MainCorruptedRegistry" xml:space="preserve"><value>Corrupted registry archived to {0}: {1}

This means that CKAN forgot about all your installed mods, but they are still in GameData. You can reinstall them by importing the {2} file.</value></data>
<data name="AllModVersionsInstallPrompt" xml:space="preserve"><value>{0} is not supported on your current game version and may not work at all. If you have any problems with it, you should NOT ask its maintainers for help.

Do you really want to install it?</value></data>
Expand Down
2 changes: 1 addition & 1 deletion GUI/Properties/Resources.zh-CN.resx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@
<data name="MainQuit" xml:space="preserve"><value>退出</value></data>
<data name="MainGoBack" xml:space="preserve"><value>返回</value></data>
<data name="MainQuitWithUnmetDeps" xml:space="preserve"><value>有未满足的依赖. 真的要退出吗?</value></data>
<data name="MainQuitWIthUnappliedChanges" xml:space="preserve"><value>您有未应用的更改. 真的要退出吗?
<data name="MainQuitWithUnappliedChanges" xml:space="preserve"><value>您有未应用的更改. 真的要退出吗?

{0}</value></data>
<data name="MainUpgradingWaitTitle" xml:space="preserve"><value>正在更新CKAN</value></data>
Expand Down

0 comments on commit 72c4a56

Please sign in to comment.