summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release-notes.md3
-rw-r--r--src/StardewModdingAPI/Constants.cs4
-rw-r--r--src/StardewModdingAPI/Framework/GitRelease.cs19
-rw-r--r--src/StardewModdingAPI/Framework/UpdateHelper.cs36
-rw-r--r--src/StardewModdingAPI/Program.cs21
-rw-r--r--src/StardewModdingAPI/StardewModdingAPI.csproj2
-rw-r--r--src/StardewModdingAPI/Version.cs51
7 files changed, 133 insertions, 3 deletions
diff --git a/release-notes.md b/release-notes.md
index e04c284a..8b5a87d3 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -3,8 +3,9 @@
## 1.x
* 1.0 (upcoming, [log](https://github.com/CLxS/SMAPI/compare/0.40.1.1-3...master))
* Added support for Linux and Mac.
- * Added zoom-adjusted mouse position to mouse-changed event arguments.
+ * Added background update check on launch.
* Added OS version to log.
+ * Added zoom-adjusted mouse position to mouse-changed event arguments.
* Switched to [semantic versioning](http://semver.org).
* Fixed mod versions not being displayed correctly on log.
* Fixed misspelled field in `manifest.json` schema.
diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs
index 6fe81232..b183f8b6 100644
--- a/src/StardewModdingAPI/Constants.cs
+++ b/src/StardewModdingAPI/Constants.cs
@@ -10,8 +10,12 @@ namespace StardewModdingAPI
/// </summary>
public static class Constants
{
+ /// <summary>SMAPI's current semantic version.</summary>
public static readonly Version Version = new Version(1, 0, 0, $"alpha-{DateTime.UtcNow.ToString("yyyyMMddHHmm")}");
+ /// <summary>The GitHub repository to check for updates.</summary>
+ public const string GitHubRepository = "cjsu/SMAPI";
+
/// <summary>
/// Not quite "constant", but it makes more sense for it to be here, at least for now
/// </summary>
diff --git a/src/StardewModdingAPI/Framework/GitRelease.cs b/src/StardewModdingAPI/Framework/GitRelease.cs
new file mode 100644
index 00000000..0da57efd
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/GitRelease.cs
@@ -0,0 +1,19 @@
+using Newtonsoft.Json;
+
+namespace StardewModdingAPI.Framework
+{
+ /// <summary>Metadata about a GitHub release tag.</summary>
+ internal class GitRelease
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The display name.</summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ /// <summary>The semantic version string.</summary>
+ [JsonProperty("tag_name")]
+ public string Tag { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/StardewModdingAPI/Framework/UpdateHelper.cs b/src/StardewModdingAPI/Framework/UpdateHelper.cs
new file mode 100644
index 00000000..ddd1d840
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/UpdateHelper.cs
@@ -0,0 +1,36 @@
+using System.IO;
+using System.Net;
+using System.Reflection;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace StardewModdingAPI.Framework
+{
+ /// <summary>Provides utility methods for mod updates.</summary>
+ internal class UpdateHelper
+ {
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Get the latest release from a GitHub repository.</summary>
+ /// <param name="repository">The name of the repository from which to fetch releases (like "cjsu/SMAPI").</param>
+ public static async Task<GitRelease> GetLatestVersionAsync(string repository)
+ {
+ // build request
+ // (avoid HttpClient for Mac compatibility)
+ HttpWebRequest request = WebRequest.CreateHttp($"https://api.github.com/repos/{repository}/releases/latest");
+ AssemblyName assembly = typeof(UpdateHelper).Assembly.GetName();
+ request.UserAgent = $"{assembly.Name}/{assembly.Version}";
+ request.Accept = "application/vnd.github.v3+json";
+
+ // fetch data
+ using (WebResponse response = await request.GetResponseAsync())
+ using (Stream responseStream = response.GetResponseStream())
+ using (StreamReader reader = new StreamReader(responseStream))
+ {
+ string responseText = reader.ReadToEnd();
+ return JsonConvert.DeserializeObject<GitRelease>(responseText);
+ }
+ }
+ }
+}
diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs
index b3f81217..e2576e40 100644
--- a/src/StardewModdingAPI/Program.cs
+++ b/src/StardewModdingAPI/Program.cs
@@ -10,6 +10,7 @@ using System.Windows.Forms;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI.Events;
+using StardewModdingAPI.Framework;
using StardewModdingAPI.Inheritance;
using StardewValley;
@@ -54,6 +55,7 @@ namespace StardewModdingAPI
{
Log.AsyncY($"SMAPI {Constants.Version}");
Log.AsyncY($"Stardew Valley {Game1.version} on {Environment.OSVersion}");
+ Program.CheckForUpdateAsync();
Program.ConfigureUI();
Program.CreateDirectories();
Program.StartGame();
@@ -91,6 +93,25 @@ namespace StardewModdingAPI
throw new FileNotFoundException($"Could not find executable: {GameExecutablePath}");
}
+ /// <summary>Asynchronously check for a new version of SMAPI, and print a message to the console if an update is available.</summary>
+ private static void CheckForUpdateAsync()
+ {
+ new Thread(() =>
+ {
+ try
+ {
+ GitRelease release = UpdateHelper.GetLatestVersionAsync(Constants.GitHubRepository).Result;
+ Version latestVersion = new Version(release.Tag);
+ if (latestVersion.CompareTo(Constants.Version) > 0)
+ Log.AsyncColour($"You can update SMAPI from version {Constants.Version} to {latestVersion}", ConsoleColor.Magenta);
+ }
+ catch (Exception ex)
+ {
+ Log.Debug($"Couldn't check for a new version of SMAPI. This won't affect your game, but you may not be notified of new versions if this keeps happening.\n{ex}");
+ }
+ }).Start();
+ }
+
/// <summary>
/// Load Stardev Valley and control features, and launch the game.
/// </summary>
diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj
index 43e00fbc..e79cabf4 100644
--- a/src/StardewModdingAPI/StardewModdingAPI.csproj
+++ b/src/StardewModdingAPI/StardewModdingAPI.csproj
@@ -192,6 +192,8 @@
<Compile Include="Events\PlayerEvents.cs" />
<Compile Include="Events\TimeEvents.cs" />
<Compile Include="Extensions.cs" />
+ <Compile Include="Framework\UpdateHelper.cs" />
+ <Compile Include="Framework\GitRelease.cs" />
<Compile Include="Inheritance\ChangeType.cs" />
<Compile Include="Inheritance\ItemStackChange.cs" />
<Compile Include="Log.cs" />
diff --git a/src/StardewModdingAPI/Version.cs b/src/StardewModdingAPI/Version.cs
index db5a21d4..b1903d7b 100644
--- a/src/StardewModdingAPI/Version.cs
+++ b/src/StardewModdingAPI/Version.cs
@@ -1,12 +1,21 @@
using System;
+using System.Text.RegularExpressions;
using Newtonsoft.Json;
namespace StardewModdingAPI
{
/// <summary>A semantic version with an optional release tag.</summary>
- public struct Version
+ public struct Version : IComparable<Version>
{
/*********
+ ** Properties
+ *********/
+ /// <summary>A regular expression matching a semantic version string.</summary>
+ /// <remarks>Derived from https://github.com/maxhauser/semver.</remarks>
+ private static readonly Regex Regex = new Regex(@"^(?<major>\d+)(\.(?<minor>\d+))?(\.(?<patch>\d+))?(?<build>.*)$", RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture);
+
+
+ /*********
** Accessors
*********/
/// <summary>The major version incremented for major API changes.</summary>
@@ -43,6 +52,44 @@ namespace StardewModdingAPI
this.Build = build;
}
+ /// <summary>Construct an instance.</summary>
+ /// <param name="version">The semantic version string.</param>
+ internal Version(string version)
+ {
+ var match = Version.Regex.Match(version);
+ if (!match.Success)
+ throw new FormatException($"The input '{version}' is not a semantic version.");
+
+ this.MajorVersion = int.Parse(match.Groups["major"].Value);
+ this.MinorVersion = match.Groups["minor"].Success ? int.Parse(match.Groups["minor"].Value) : 0;
+ this.PatchVersion = match.Groups["patch"].Success ? int.Parse(match.Groups["patch"].Value) : 0;
+ this.Build = (match.Groups["build"].Success ? match.Groups["build"].Value : "").Trim(' ', '-', '.');
+ }
+
+ /// <summary>Get an integer indicating whether this version precedes (less than 0), supercedes (more than 0), or is equivalent to (0) the specified version.</summary>
+ /// <param name="other">The version to compare with this instance.</param>
+ public int CompareTo(Version other)
+ {
+ // compare version numbers
+ if (this.MajorVersion != other.MajorVersion)
+ return this.MajorVersion - other.MajorVersion;
+ if (this.MinorVersion != other.MinorVersion)
+ return this.MinorVersion - other.MinorVersion;
+ if (this.PatchVersion != other.PatchVersion)
+ return this.PatchVersion - other.PatchVersion;
+
+ // stable version (without tag) supercedes prerelease (with tag)
+ bool curHasTag = !string.IsNullOrWhiteSpace(this.Build);
+ bool otherHasTag = !string.IsNullOrWhiteSpace(other.Build);
+ if (!curHasTag && otherHasTag)
+ return 1;
+ if (curHasTag && !otherHasTag)
+ return -1;
+
+ // else compare by string
+ return string.Compare(this.ToString(), other.ToString(), StringComparison.InvariantCultureIgnoreCase);
+ }
+
/// <summary>Get a string representation of the version.</summary>
public override string ToString()
{
@@ -66,7 +113,7 @@ namespace StardewModdingAPI
/// <param name="tag">The tag to normalise.</param>
private string GetNormalisedTag(string tag)
{
- tag = tag?.Trim().Trim('-');
+ tag = tag?.Trim().Trim('-', '.');
if (string.IsNullOrWhiteSpace(tag) || tag == "0")
return null;
return tag;