Skip to content

Commit

Permalink
Merge branch 'develop' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
Pathoschild committed Feb 8, 2019
2 parents 79c6166 + 41f77f5 commit 84b9f43
Show file tree
Hide file tree
Showing 27 changed files with 463 additions and 196 deletions.
4 changes: 2 additions & 2 deletions build/GlobalAssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -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")]
14 changes: 10 additions & 4 deletions docs/mod-build-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,6 @@ If you don't want to include a file in the mod folder or release zip:
relative path in your mod folder, that file won't be included.

### Non-mod projects
**(upcoming in 2.1)**

You can use the package in non-mod projects too (e.g. unit tests or framework DLLs). You'll need to
disable deploying the mod and creating a release zip:

Expand Down Expand Up @@ -218,10 +216,18 @@ That error means the package couldn't find your game. You can specify the game p
_[Game path](#game-path)_ above.

## Release notes
### 2.1 alpha
### 2.2
* Added support for SMAPI 2.8+ (still compatible with earlier versions).
* Added default game paths for 32-bit Windows.
* Fixed valid manifests marked invalid in some cases.

### 2.1
* Added support for Stardew Valley 1.3.
* Added support for unit test projects.
* Added support for non-mod projects.
* Added C# analyzers to warn about implicit conversions of Netcode fields in Stardew Valley 1.3.
* Added option to ignore files by regex pattern.
* Added reference to new SMAPI DLL.
* Fixed some game paths not detected by NuGet package.

### 2.0.2
* Fixed compatibility issue on Linux.
Expand Down
33 changes: 31 additions & 2 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
# Release notes
## 2.10.2
Released 08 January 2019 for Stardew Valley 1.3.32–33.

* For players:
* 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.

* For the web UI:
* Added beta status filter to compatibility list.
* Fixed broken ModDrop links in the compatibility list.

* For modders:
* Asset changes are now propagated into the parsed save being loaded if applicable.
* Added locale to context trace logs.
* Fixed error loading custom map tilesheets in some cases.
* Fixed error when swapping maps mid-session for a location with interior doors.
* Fixed `Constants.SaveFolderName` and `CurrentSavePath` not available during early load stages when using `Specialised.LoadStageChanged` event.
* Fixed `LoadStage.SaveParsed` raised before the parsed save data is available.
* Fixed 'unknown mod' deprecation warnings showing the wrong stack trace.
* Fixed `e.Cursor` in input events showing wrong grab tile when player using a controller moves without moving the viewpoint.
* Fixed incorrect 'bypassed safety checks' warning for mods using the new `Specialised.LoadStageChanged` event in 2.10.
* 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.
Expand Down
4 changes: 2 additions & 2 deletions docs/technical-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ on the wiki for the first-time setup.

## Customisation
### Configuration file
You can customise the SMAPI behaviour by editing the `StardewModdingAPI.config.json` file in your
game folder.
You can customise the SMAPI behaviour by editing the `smapi-internal/StardewModdingAPI.config.json`
file in your game folder.

Basic fields:

Expand Down
4 changes: 2 additions & 2 deletions src/SMAPI.Mods.ConsoleCommands/manifest.json
Original file line number Diff line number Diff line change
@@ -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"
}
60 changes: 51 additions & 9 deletions src/SMAPI.Mods.SaveBackup/ModEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ public class ModEntry : Mod
/// <summary>The absolute path to the folder in which to store save backups.</summary>
private readonly string BackupFolder = Path.Combine(Constants.ExecutionPath, "save-backups");

/// <summary>A unique label for the save backup to create.</summary>
private readonly string BackupLabel = $"{DateTime.UtcNow:yyyy-MM-dd} - SMAPI {Constants.ApiVersion} with Stardew Valley {Game1.version}";

/// <summary>The name of the save archive to create.</summary>
private readonly string FileName = $"{DateTime.UtcNow:yyyy-MM-dd} - SMAPI {Constants.ApiVersion} with Stardew Valley {Game1.version}.zip";
private string FileName => $"{this.BackupLabel}.zip";


/*********
Expand Down Expand Up @@ -59,8 +62,9 @@ private void CreateBackup(DirectoryInfo backupFolder)
{
// 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.
Expand All @@ -70,12 +74,23 @@ private void CreateBackup(DirectoryInfo backupFolder)
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;

Expand Down Expand Up @@ -132,5 +147,32 @@ private void PruneBackups(DirectoryInfo backupFolder, int backupsToKeep)
this.Monitor.Log(ex.ToString(), LogLevel.Trace);
}
}

/// <summary>Recursively copy a directory or file.</summary>
/// <param name="source">The file or folder to copy.</param>
/// <param name="targetFolder">The folder to copy into.</param>
/// <param name="copyRoot">Whether to copy the root folder itself, or <c>false</c> to only copy its contents.</param>
/// <remarks>Derived from the SMAPI installer code.</remarks>
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}'.");
}
}
}
}
4 changes: 2 additions & 2 deletions src/SMAPI.Mods.SaveBackup/manifest.json
Original file line number Diff line number Diff line change
@@ -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"
}
3 changes: 2 additions & 1 deletion src/SMAPI.Web/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,13 @@ private RewriteOptions GetRedirectRules()
));

// shortcut redirects
redirects.Add(new RedirectToUrlRule(@"^/3\.0\.?$", "https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_3.0"));
redirects.Add(new RedirectToUrlRule(@"^/buildmsg(?:/?(.*))$", "https://github.com/Pathoschild/SMAPI/blob/develop/docs/mod-build-config.md#$1"));
redirects.Add(new RedirectToUrlRule(@"^/compat\.?$", "https://mods.smapi.io"));
redirects.Add(new RedirectToUrlRule(@"^/3\.0\.?$", "https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_3.0"));
redirects.Add(new RedirectToUrlRule(@"^/docs\.?$", "https://stardewvalleywiki.com/Modding:Index"));
redirects.Add(new RedirectToUrlRule(@"^/install\.?$", "https://stardewvalleywiki.com/Modding:Player_Guide/Getting_Started#Install_SMAPI"));
redirects.Add(new RedirectToUrlRule(@"^/troubleshoot(.*)$", "https://stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting$1"));
redirects.Add(new RedirectToUrlRule(@"^/xnb\.?$", "https://stardewvalleywiki.com/Modding:Using_XNB_mods"));

// redirect legacy canimod.com URLs
var wikiRedirects = new Dictionary<string, string[]>
Expand Down
2 changes: 1 addition & 1 deletion src/SMAPI.Web/ViewModels/ModModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ private IEnumerable<ModLinkModel> GetModPageUrls(WikiModEntry entry)
if (entry.ModDropID.HasValue)
{
anyFound = true;
yield return new ModLinkModel($"https://www.moddrop.com/sdv/mod/467243/{entry.ModDropID}", "ModDrop");
yield return new ModLinkModel($"https://www.moddrop.com/sdv/mod/{entry.ModDropID}", "ModDrop");
}

// fallback
Expand Down
19 changes: 9 additions & 10 deletions src/SMAPI.Web/Views/Mods/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
ViewData["Title"] = "SMAPI mod compatibility";
}
@section Head {
<link rel="stylesheet" href="~/Content/css/mods.css?r=20181122" />
<link rel="stylesheet" href="~/Content/css/mods.css?r=20190125" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/jquery.tablesorter.combined.min.js" crossorigin="anonymous"></script>
<script src="~/Content/js/mods.js?r=20181122"></script>
<script src="~/Content/js/mods.js?r=20190125"></script>
<script>
$(function() {
var data = @Json.Serialize(Model.Mods, new JsonSerializerSettings { Formatting = Formatting.None });
smapi.modList(data);
var enableBeta = @Json.Serialize(Model.BetaVersion != null);
smapi.modList(data, enableBeta);
});
</script>
}
Expand All @@ -39,7 +40,7 @@
<label for="show-advanced">show advanced info and options</label>
<div id="filters" v-show="showAdvanced">
<div v-for="(filterGroup, key) in filters">
{{key}}: <span v-for="filter in filterGroup" v-bind:class="{ active: filter.value }"><input type="checkbox" v-bind:id="filter.id" v-model="filter.value" v-on:change="applyFilters" /> <label v-bind:for="filter.id">{{filter.label}}</label></span>
{{filterGroup.label}}: <span v-for="filter in filterGroup.value" v-bind:class="{ active: filter.value }"><input type="checkbox" v-bind:id="filter.id" v-model="filter.value" v-on:change="applyFilters" /> <label v-bind:for="filter.id">{{filter.label}}</label></span>
</div>
</div>
</div>
Expand Down Expand Up @@ -93,12 +94,10 @@
<span v-else class="mod-closed-source">no source</span>
</td>
<td v-show="showAdvanced">
<template v-if="mod.LatestCompatibility.Status == 'ok' || mod.LatestCompatibility.Status == 'unofficial' || mod.Smapi3Status == 'ok' || mod.Smapi3Status == 'soon'">
<small v-if="mod.Smapi3Status == 'ok'">✓</small>
<small v-else-if="mod.Smapi3Status == 'broken'">✖</small>
<small v-else-if="mod.Smapi3Status == 'soon' && mod.Smapi3Url"><a v-bind:href="mod.Smapi3Url">↻ soon</a></small>
<small v-else>↻ {{mod.Smapi3Status}}</small>
</template>
<small v-if="mod.LatestCompatibility.Status == 'ok' || mod.LatestCompatibility.Status == 'unofficial' || mod.Smapi3Status == 'ok' || mod.Smapi3Status == 'soon' || mod.Smapi3Url">
<a v-if="mod.Smapi3Url" v-bind:href="mod.Smapi3Url">{{mod.Smapi3DisplayText}}</a>
<template v-else>{{mod.Smapi3DisplayText}}</template>
</small>
</td>
<td>
<small><a v-bind:href="'#' + mod.Slug">#</a></small>
Expand Down
Loading

0 comments on commit 84b9f43

Please sign in to comment.