From 2287aeeb6580cab125d274e604115680a9b2daea Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 3 Jan 2019 18:40:55 -0500 Subject: improve save backup error-handling, keep first backup of the day --- src/SMAPI.Mods.SaveBackup/ModEntry.cs | 60 +++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 9 deletions(-) (limited to 'src/SMAPI.Mods.SaveBackup') diff --git a/src/SMAPI.Mods.SaveBackup/ModEntry.cs b/src/SMAPI.Mods.SaveBackup/ModEntry.cs index 56a86cd9..d10131b3 100644 --- a/src/SMAPI.Mods.SaveBackup/ModEntry.cs +++ b/src/SMAPI.Mods.SaveBackup/ModEntry.cs @@ -20,8 +20,11 @@ namespace StardewModdingAPI.Mods.SaveBackup /// The absolute path to the folder in which to store save backups. private readonly string BackupFolder = Path.Combine(Constants.ExecutionPath, "save-backups"); + /// A unique label for the save backup to create. + private readonly string BackupLabel = $"{DateTime.UtcNow:yyyy-MM-dd} - SMAPI {Constants.ApiVersion} with Stardew Valley {Game1.version}"; + /// The name of the save archive to create. - private readonly string FileName = $"{DateTime.UtcNow:yyyy-MM-dd} - SMAPI {Constants.ApiVersion} with Stardew Valley {Game1.version}.zip"; + private string FileName => $"{this.BackupLabel}.zip"; /********* @@ -59,8 +62,9 @@ namespace StardewModdingAPI.Mods.SaveBackup { // get target path FileInfo targetFile = new FileInfo(Path.Combine(backupFolder.FullName, this.FileName)); - if (targetFile.Exists) - targetFile.Delete(); //return; + DirectoryInfo fallbackDir = new DirectoryInfo(Path.Combine(backupFolder.FullName, this.BackupLabel)); + if (targetFile.Exists || fallbackDir.Exists) + return; // create zip // due to limitations with the bundled Mono on Mac, we can't reference System.IO.Compression. @@ -70,12 +74,23 @@ namespace StardewModdingAPI.Mods.SaveBackup case GamePlatform.Linux: case GamePlatform.Windows: { - Assembly coreAssembly = Assembly.Load("System.IO.Compression, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") ?? throw new InvalidOperationException("Can't load System.IO.Compression assembly."); - Assembly fsAssembly = Assembly.Load("System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") ?? throw new InvalidOperationException("Can't load System.IO.Compression assembly."); - Type compressionLevelType = coreAssembly.GetType("System.IO.Compression.CompressionLevel") ?? throw new InvalidOperationException("Can't load CompressionLevel type."); - Type zipFileType = fsAssembly.GetType("System.IO.Compression.ZipFile") ?? throw new InvalidOperationException("Can't load ZipFile type."); - MethodInfo createMethod = zipFileType.GetMethod("CreateFromDirectory", new[] { typeof(string), typeof(string), compressionLevelType, typeof(bool) }) ?? throw new InvalidOperationException("Can't load ZipFile.CreateFromDirectory method."); - createMethod.Invoke(null, new object[] { Constants.SavesPath, targetFile.FullName, CompressionLevel.Fastest, false }); + try + { + // create compressed backup + Assembly coreAssembly = Assembly.Load("System.IO.Compression, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") ?? throw new InvalidOperationException("Can't load System.IO.Compression assembly."); + Assembly fsAssembly = Assembly.Load("System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") ?? throw new InvalidOperationException("Can't load System.IO.Compression assembly."); + Type compressionLevelType = coreAssembly.GetType("System.IO.Compression.CompressionLevel") ?? throw new InvalidOperationException("Can't load CompressionLevel type."); + Type zipFileType = fsAssembly.GetType("System.IO.Compression.ZipFile") ?? throw new InvalidOperationException("Can't load ZipFile type."); + MethodInfo createMethod = zipFileType.GetMethod("CreateFromDirectory", new[] { typeof(string), typeof(string), compressionLevelType, typeof(bool) }) ?? throw new InvalidOperationException("Can't load ZipFile.CreateFromDirectory method."); + createMethod.Invoke(null, new object[] { Constants.SavesPath, targetFile.FullName, CompressionLevel.Fastest, false }); + } + catch (Exception ex) when (ex is TypeLoadException || ex.InnerException is TypeLoadException) + { + // create uncompressed backup if compression fails + this.Monitor.Log("Couldn't zip the save backup, creating uncompressed backup instead."); + this.Monitor.Log(ex.ToString(), LogLevel.Trace); + this.RecursiveCopy(new DirectoryInfo(Constants.SavesPath), fallbackDir, copyRoot: false); + } } break; @@ -132,5 +147,32 @@ namespace StardewModdingAPI.Mods.SaveBackup this.Monitor.Log(ex.ToString(), LogLevel.Trace); } } + + /// Recursively copy a directory or file. + /// The file or folder to copy. + /// The folder to copy into. + /// Whether to copy the root folder itself, or false to only copy its contents. + /// Derived from the SMAPI installer code. + private void RecursiveCopy(FileSystemInfo source, DirectoryInfo targetFolder, bool copyRoot = true) + { + if (!targetFolder.Exists) + targetFolder.Create(); + + switch (source) + { + case FileInfo sourceFile: + sourceFile.CopyTo(Path.Combine(targetFolder.FullName, sourceFile.Name)); + break; + + case DirectoryInfo sourceDir: + DirectoryInfo targetSubfolder = copyRoot ? new DirectoryInfo(Path.Combine(targetFolder.FullName, sourceDir.Name)) : targetFolder; + foreach (var entry in sourceDir.EnumerateFileSystemInfos()) + this.RecursiveCopy(entry, targetSubfolder); + break; + + default: + throw new NotSupportedException($"Unknown filesystem info type '{source.GetType().FullName}'."); + } + } } } -- cgit From 41f77f51c0203fa36c1e47cf67409244ed3c2ff2 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 8 Feb 2019 18:19:47 -0500 Subject: prepare for 2.10.2 release --- build/GlobalAssemblyInfo.cs | 4 ++-- docs/release-notes.md | 17 +++++++++-------- src/SMAPI.Mods.ConsoleCommands/manifest.json | 4 ++-- src/SMAPI.Mods.SaveBackup/manifest.json | 4 ++-- src/SMAPI/Constants.cs | 2 +- 5 files changed, 16 insertions(+), 15 deletions(-) (limited to 'src/SMAPI.Mods.SaveBackup') diff --git a/build/GlobalAssemblyInfo.cs b/build/GlobalAssemblyInfo.cs index da80f48b..3faed842 100644 --- a/build/GlobalAssemblyInfo.cs +++ b/build/GlobalAssemblyInfo.cs @@ -1,5 +1,5 @@ using System.Reflection; [assembly: AssemblyProduct("SMAPI")] -[assembly: AssemblyVersion("2.10.1")] -[assembly: AssemblyFileVersion("2.10.1")] +[assembly: AssemblyVersion("2.10.2")] +[assembly: AssemblyFileVersion("2.10.2")] diff --git a/docs/release-notes.md b/docs/release-notes.md index e0bf3935..4527b12d 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,13 +1,14 @@ # Release notes -## Upcoming release +## 2.10.2 +Released 08 January 2019 for Stardew Valley 1.3.32–33. + * For players: - * Added mod page link to 'missing dependency' errors for the most common dependencies. - * Improved save backups: - * When compression isn't available on Linux, SMAPI now falls back to uncompressed backups. - * SMAPI now keeps the first backup created for the day, instead of the last one. - * Restrict to Stardew Valley 1.3.33 or earlier (to avoid confusion when SDV 1.3.35 is released). + * SMAPI now keeps the first save backup created for the day, instead of the last one. + * Fixed save backup for some Linux/Mac players. (When compression isn't available, SMAPI will now create uncompressed backups instead.) + * Fixed some common dependencies not linking to the mod page in 'missing mod' errors. * Fixed 'unknown mod' deprecation warnings showing a stack trace when developers mode not enabled. * Fixed 'unknown mod' deprecation warnings when they occur in the Mod constructor. + * Fixed confusing error message when using SMAPI 2.10._x_ with Stardew Valley 1.3.35+. * Tweaked XNB mod message for clarity. * Updated compatibility list. @@ -28,13 +29,13 @@ * Deprecated `EntryDll` values whose capitalisation don't match the actual file. (This works on Windows, but causes errors for Linux/Mac players.) ## 2.10.1 -Released 30 December 2018 for Stardew Valley 1.3.32. +Released 30 December 2018 for Stardew Valley 1.3.32–33. * For players: * Fixed some mod integrations not working correctly in SMAPI 2.10. ## 2.10 -Released 29 December 2018 for Stardew Valley 1.3.32. +Released 29 December 2018 for Stardew Valley 1.3.32–33. * For players: * Added `world_clear` console command to remove spawned or placed entities. diff --git a/src/SMAPI.Mods.ConsoleCommands/manifest.json b/src/SMAPI.Mods.ConsoleCommands/manifest.json index 0341c390..b5fd0424 100644 --- a/src/SMAPI.Mods.ConsoleCommands/manifest.json +++ b/src/SMAPI.Mods.ConsoleCommands/manifest.json @@ -1,9 +1,9 @@ { "Name": "Console Commands", "Author": "SMAPI", - "Version": "2.10.1", + "Version": "2.10.2", "Description": "Adds SMAPI console commands that let you manipulate the game.", "UniqueID": "SMAPI.ConsoleCommands", "EntryDll": "ConsoleCommands.dll", - "MinimumApiVersion": "2.10.1" + "MinimumApiVersion": "2.10.2" } diff --git a/src/SMAPI.Mods.SaveBackup/manifest.json b/src/SMAPI.Mods.SaveBackup/manifest.json index b2b9ad4b..7ac537ca 100644 --- a/src/SMAPI.Mods.SaveBackup/manifest.json +++ b/src/SMAPI.Mods.SaveBackup/manifest.json @@ -1,9 +1,9 @@ { "Name": "Save Backup", "Author": "SMAPI", - "Version": "2.10.1", + "Version": "2.10.2", "Description": "Automatically backs up all your saves once per day into its folder.", "UniqueID": "SMAPI.SaveBackup", "EntryDll": "SaveBackup.dll", - "MinimumApiVersion": "2.10.1" + "MinimumApiVersion": "2.10.2" } diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index f8d1638e..51c15269 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -20,7 +20,7 @@ namespace StardewModdingAPI ** Public ****/ /// SMAPI's current semantic version. - public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("2.10.1"); + public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("2.10.2"); /// The minimum supported version of Stardew Valley. public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.3.32"); -- cgit