From 436c071ba4ecbe43769b438277d441057d05403e Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Thu, 15 Mar 2018 19:52:18 -0400
Subject: add support for preview GitHub releases (#457)
---
src/SMAPI.Common/Models/ModInfoModel.cs | 12 ++++--
.../Framework/Clients/GitHub/GitHubClient.cs | 50 ++++++++++++++++------
.../Framework/Clients/GitHub/GitRelease.cs | 4 ++
.../Framework/Clients/GitHub/IGitHubClient.cs | 5 ++-
.../Framework/ConfigModels/ApiClientsConfig.cs | 7 ++-
.../ModRepositories/ChucklefishRepository.cs | 2 +-
.../Framework/ModRepositories/GitHubRepository.cs | 19 ++++++--
.../Framework/ModRepositories/NexusRepository.cs | 2 +-
src/SMAPI.Web/Startup.cs | 3 +-
src/SMAPI.Web/appsettings.json | 3 +-
10 files changed, 78 insertions(+), 29 deletions(-)
(limited to 'src')
diff --git a/src/SMAPI.Common/Models/ModInfoModel.cs b/src/SMAPI.Common/Models/ModInfoModel.cs
index 48305cb8..48df235a 100644
--- a/src/SMAPI.Common/Models/ModInfoModel.cs
+++ b/src/SMAPI.Common/Models/ModInfoModel.cs
@@ -9,9 +9,12 @@ namespace StardewModdingAPI.Common.Models
/// The mod name.
public string Name { get; set; }
- /// The mod's semantic version number.
+ /// The semantic version for the mod's latest release.
public string Version { get; set; }
+ /// The semantic version for the mod's latest preview release, if available and different from .
+ public string PreviewVersion { get; set; }
+
/// The mod's web URL.
public string Url { get; set; }
@@ -28,16 +31,17 @@ namespace StardewModdingAPI.Common.Models
// needed for JSON deserialising
}
-
/// Construct an instance.
/// The mod name.
- /// The mod's semantic version number.
+ /// The semantic version for the mod's latest release.
+ /// The semantic version for the mod's latest preview release, if available and different from .
/// The mod's web URL.
/// The error message indicating why the mod is invalid (if applicable).
- public ModInfoModel(string name, string version, string url, string error = null)
+ public ModInfoModel(string name, string version, string url, string previewVersion = null, string error = null)
{
this.Name = name;
this.Version = version;
+ this.PreviewVersion = previewVersion;
this.Url = url;
this.Error = error; // mainly initialised here for the JSON deserialiser
}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
index 0b205660..4abe0737 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Pathoschild.Http.Client;
@@ -11,8 +12,11 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
/*********
** Properties
*********/
- /// The URL for a GitHub releases API query excluding the base URL, where {0} is the repository owner and name.
- private readonly string ReleaseUrlFormat;
+ /// The URL for a GitHub API query for the latest stable release, excluding the base URL, where {0} is the organisation and project name.
+ private readonly string StableReleaseUrlFormat;
+
+ /// The URL for a GitHub API query for the latest release (including prerelease), excluding the base URL, where {0} is the organisation and project name.
+ private readonly string AnyReleaseUrlFormat;
/// The underlying HTTP client.
private readonly IClient Client;
@@ -23,14 +27,16 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
*********/
/// Construct an instance.
/// The base URL for the GitHub API.
- /// The URL for a GitHub releases API query excluding the , where {0} is the repository owner and name.
+ /// The URL for a GitHub API query for the latest stable release, excluding the , where {0} is the organisation and project name.
+ /// The URL for a GitHub API query for the latest release (including prerelease), excluding the , where {0} is the organisation and project name.
/// The user agent for the API client.
/// The Accept header value expected by the GitHub API.
/// The username with which to authenticate to the GitHub API.
/// The password with which to authenticate to the GitHub API.
- public GitHubClient(string baseUrl, string releaseUrlFormat, string userAgent, string acceptHeader, string username, string password)
+ public GitHubClient(string baseUrl, string stableReleaseUrlFormat, string anyReleaseUrlFormat, string userAgent, string acceptHeader, string username, string password)
{
- this.ReleaseUrlFormat = releaseUrlFormat;
+ this.StableReleaseUrlFormat = stableReleaseUrlFormat;
+ this.AnyReleaseUrlFormat = anyReleaseUrlFormat;
this.Client = new FluentClient(baseUrl)
.SetUserAgent(userAgent)
@@ -41,18 +47,23 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
/// Get the latest release for a GitHub repository.
/// The repository key (like Pathoschild/SMAPI).
- /// Returns the latest release if found, else null.
- public async Task GetLatestReleaseAsync(string repo)
+ /// Whether to return a prerelease version if it's latest.
+ /// Returns the release if found, else null.
+ public async Task GetLatestReleaseAsync(string repo, bool includePrerelease = false)
{
- // validate key format
- if (!repo.Contains("/") || repo.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != repo.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase))
- throw new ArgumentException($"The value '{repo}' isn't a valid GitHub repository key, must be a username and project name like 'Pathoschild/SMAPI'.", nameof(repo));
-
- // fetch info
+ this.AssetKeyFormat(repo);
try
{
+ if (includePrerelease)
+ {
+ GitRelease[] results = await this.Client
+ .GetAsync(string.Format(this.AnyReleaseUrlFormat, repo))
+ .AsArray();
+ return results.FirstOrDefault();
+ }
+
return await this.Client
- .GetAsync(string.Format(this.ReleaseUrlFormat, repo))
+ .GetAsync(string.Format(this.StableReleaseUrlFormat, repo))
.As();
}
catch (ApiException ex) when (ex.Status == HttpStatusCode.NotFound)
@@ -66,5 +77,18 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
{
this.Client?.Dispose();
}
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// Assert that a repository key is formatted correctly.
+ /// The repository key (like Pathoschild/SMAPI).
+ /// The repository key is invalid.
+ private void AssetKeyFormat(string repo)
+ {
+ if (repo == null || !repo.Contains("/") || repo.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != repo.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase))
+ throw new ArgumentException($"The value '{repo}' isn't a valid GitHub repository key, must be a username and project name like 'Pathoschild/SMAPI'.", nameof(repo));
+ }
}
}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
index b944088d..827374fb 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
@@ -19,6 +19,10 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
/// The Markdown description for the release.
public string Body { get; set; }
+ /// Whether this is a prerelease version.
+ [JsonProperty("prerelease")]
+ public bool IsPrerelease { get; set; }
+
/// The attached files.
public GitAsset[] Assets { get; set; }
}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
index 6e8eadff..9519c26f 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
@@ -11,7 +11,8 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
*********/
/// Get the latest release for a GitHub repository.
/// The repository key (like Pathoschild/SMAPI).
- /// Returns the latest release if found, else null.
- Task GetLatestReleaseAsync(string repo);
+ /// Whether to return a prerelease version if it's latest.
+ /// Returns the release if found, else null.
+ Task GetLatestReleaseAsync(string repo, bool includePrerelease = false);
}
}
diff --git a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
index 61219414..de6c024a 100644
--- a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
+++ b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
@@ -29,8 +29,11 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels
/// The base URL for the GitHub API.
public string GitHubBaseUrl { get; set; }
- /// The URL for a GitHub API latest-release query excluding the , where {0} is the organisation and project name.
- public string GitHubReleaseUrlFormat { get; set; }
+ /// The URL for a GitHub API query for the latest stable release, excluding the , where {0} is the organisation and project name.
+ public string GitHubStableReleaseUrlFormat { get; set; }
+
+ /// The URL for a GitHub API query for the latest release (including prerelease), excluding the , where {0} is the organisation and project name.
+ public string GitHubAnyReleaseUrlFormat { get; set; }
/// The Accept header value expected by the GitHub API.
public string GitHubAcceptHeader { get; set; }
diff --git a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
index 266055a6..3e5a4272 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
@@ -43,7 +43,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
return new ModInfoModel("Found no mod with this ID.");
// create model
- return new ModInfoModel(mod.Name, this.NormaliseVersion(mod.Version), mod.Url);
+ return new ModInfoModel(name: mod.Name, version: this.NormaliseVersion(mod.Version), url: mod.Url);
}
catch (Exception ex)
{
diff --git a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
index 7bad6127..59eb8cd1 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
@@ -38,10 +38,21 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
// fetch info
try
{
- GitRelease release = await this.Client.GetLatestReleaseAsync(id);
- return release != null
- ? new ModInfoModel(id, this.NormaliseVersion(release.Tag), $"https://github.com/{id}/releases")
- : new ModInfoModel("Found no mod with this ID.");
+ // get latest release
+ GitRelease latest = await this.Client.GetLatestReleaseAsync(id, includePrerelease: true);
+ GitRelease preview = null;
+ if (latest == null)
+ return new ModInfoModel("Found no mod with this ID.");
+
+ // get latest stable release (if not latest)
+ if (latest.IsPrerelease)
+ {
+ preview = latest;
+ latest = await this.Client.GetLatestReleaseAsync(id, includePrerelease: false);
+ }
+
+ // return data
+ return new ModInfoModel(name: id, version: this.NormaliseVersion(latest?.Tag), previewVersion: this.NormaliseVersion(preview?.Tag), url: $"https://github.com/{id}/releases");
}
catch (Exception ex)
{
diff --git a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
index e1dc0fcc..6411ad4c 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
@@ -43,7 +43,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
return new ModInfoModel("Found no mod with this ID.");
if (mod.Error != null)
return new ModInfoModel(mod.Error);
- return new ModInfoModel(mod.Name, this.NormaliseVersion(mod.Version), mod.Url);
+ return new ModInfoModel(name: mod.Name, version: this.NormaliseVersion(mod.Version), url: mod.Url);
}
catch (Exception ex)
{
diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs
index d7d4d074..47102e5c 100644
--- a/src/SMAPI.Web/Startup.cs
+++ b/src/SMAPI.Web/Startup.cs
@@ -74,7 +74,8 @@ namespace StardewModdingAPI.Web
services.AddSingleton(new GitHubClient(
baseUrl: api.GitHubBaseUrl,
- releaseUrlFormat: api.GitHubReleaseUrlFormat,
+ stableReleaseUrlFormat: api.GitHubStableReleaseUrlFormat,
+ anyReleaseUrlFormat: api.GitHubAnyReleaseUrlFormat,
userAgent: userAgent,
acceptHeader: api.GitHubAcceptHeader,
username: api.GitHubUsername,
diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json
index 3cf72ddb..bfe827fa 100644
--- a/src/SMAPI.Web/appsettings.json
+++ b/src/SMAPI.Web/appsettings.json
@@ -24,7 +24,8 @@
"ChucklefishModPageUrlFormat": "resources/{0}",
"GitHubBaseUrl": "https://api.github.com",
- "GitHubReleaseUrlFormat": "repos/{0}/releases/latest",
+ "GitHubStableReleaseUrlFormat": "repos/{0}/releases/latest",
+ "GitHubAnyReleaseUrlFormat": "repos/{0}/releases?per_page=1",
"GitHubAcceptHeader": "application/vnd.github.v3+json",
"GitHubUsername": null, // see top note
"GitHubPassword": null, // see top note
--
cgit
From 7015e4ee8777c4a1c3934c1f3e9c3ceefcc1295d Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Thu, 15 Mar 2018 20:58:43 -0400
Subject: show prerelease SMAPI updates when updating from an older prerelease
of the same version (#457)
---
src/SMAPI/Program.cs | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
(limited to 'src')
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index 4bd40710..e4b279f7 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -541,8 +541,10 @@ namespace StardewModdingAPI
this.Monitor.Log("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.", LogLevel.Warn);
this.Monitor.Log($"Error: {response.Error}");
}
- else if (new SemanticVersion(response.Version).IsNewerThan(Constants.ApiVersion))
+ else if (this.IsValidUpdate(Constants.ApiVersion, new SemanticVersion(response.Version)))
this.Monitor.Log($"You can update SMAPI to {response.Version}: {response.Url}", LogLevel.Alert);
+ else if (response.PreviewVersion != null && this.IsValidUpdate(Constants.ApiVersion, new SemanticVersion(response.PreviewVersion)))
+ this.Monitor.Log($"You can update SMAPI to {response.PreviewVersion}: {response.Url}", LogLevel.Alert);
else
this.Monitor.Log(" SMAPI okay.", LogLevel.Trace);
}
@@ -656,6 +658,27 @@ namespace StardewModdingAPI
}).Start();
}
+ /// Get whether a given version should be offered to the user as an update.
+ /// The current semantic version.
+ /// The target semantic version.
+ private bool IsValidUpdate(ISemanticVersion currentVersion, ISemanticVersion newVersion)
+ {
+ // basic eligibility
+ bool isNewer = newVersion.IsNewerThan(currentVersion);
+ bool isPrerelease = newVersion.Build != null;
+ bool isEquallyStable = !isPrerelease || currentVersion.Build != null; // don't update stable => prerelease
+ if (!isNewer || !isEquallyStable)
+ return false;
+ if (!isPrerelease)
+ return true;
+
+ // prerelease eligible if same version (excluding prerelease tag)
+ return
+ newVersion.MajorVersion == currentVersion.MajorVersion
+ && newVersion.MinorVersion == currentVersion.MinorVersion
+ && newVersion.PatchVersion == currentVersion.PatchVersion;
+ }
+
/// Create a directory path if it doesn't exist.
/// The directory path.
private void VerifyPath(string path)
--
cgit
From 90cdbdf7b29ddeee80b213957a02e0e5c691282e Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Thu, 15 Mar 2018 21:23:36 -0400
Subject: link SMAPI update checks to smapi.io instead of GitHub (#457)
---
src/SMAPI/Constants.cs | 4 ++--
src/SMAPI/Program.cs | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
(limited to 'src')
diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs
index d91fa5fb..7a497a53 100644
--- a/src/SMAPI/Constants.cs
+++ b/src/SMAPI/Constants.cs
@@ -81,8 +81,8 @@ namespace StardewModdingAPI
/****
** Internal
****/
- /// The GitHub repository to check for updates.
- internal const string GitHubRepository = "Pathoschild/SMAPI";
+ /// The URL of the SMAPI home page.
+ internal const string HomePageUrl = "https://smapi.io";
/// The file path for the SMAPI configuration file.
internal static string ApiConfigPath => Path.Combine(Constants.ExecutionPath, $"{typeof(Program).Assembly.GetName().Name}.config.json");
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index e4b279f7..8c1ea238 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -542,9 +542,9 @@ namespace StardewModdingAPI
this.Monitor.Log($"Error: {response.Error}");
}
else if (this.IsValidUpdate(Constants.ApiVersion, new SemanticVersion(response.Version)))
- this.Monitor.Log($"You can update SMAPI to {response.Version}: {response.Url}", LogLevel.Alert);
+ this.Monitor.Log($"You can update SMAPI to {response.Version}: {Constants.HomePageUrl}", LogLevel.Alert);
else if (response.PreviewVersion != null && this.IsValidUpdate(Constants.ApiVersion, new SemanticVersion(response.PreviewVersion)))
- this.Monitor.Log($"You can update SMAPI to {response.PreviewVersion}: {response.Url}", LogLevel.Alert);
+ this.Monitor.Log($"You can update SMAPI to {response.PreviewVersion}: {Constants.HomePageUrl}", LogLevel.Alert);
else
this.Monitor.Log(" SMAPI okay.", LogLevel.Trace);
}
--
cgit
From ff6df97ae8ca12f45aaba1776472f1dc10234b91 Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Thu, 15 Mar 2018 21:26:03 -0400
Subject: fix error handling in update check API (#457)
---
src/SMAPI.Web/Controllers/ModsApiController.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
(limited to 'src')
diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs
index abae7db7..c99c87fb 100644
--- a/src/SMAPI.Web/Controllers/ModsApiController.cs
+++ b/src/SMAPI.Web/Controllers/ModsApiController.cs
@@ -115,9 +115,9 @@ namespace StardewModdingAPI.Web.Controllers
if (info.Error == null)
{
if (info.Version == null)
- info = new ModInfoModel(info.Name, info.Version, info.Url, "Mod has no version number.");
+ info = new ModInfoModel(name: info.Name, version: info.Version, url: info.Url, error: "Mod has no version number.");
if (!allowInvalidVersions && !Regex.IsMatch(info.Version, this.VersionRegex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase))
- info = new ModInfoModel(info.Name, info.Version, info.Url, $"Mod has invalid semantic version '{info.Version}'.");
+ info = new ModInfoModel(name: info.Name, version: info.Version, url: info.Url, error: $"Mod has invalid semantic version '{info.Version}'.");
}
// cache & return
--
cgit
From 594d176d39691ff46b2c99fdfaa4299a4ea43616 Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Thu, 15 Mar 2018 23:36:16 -0400
Subject: prepare home page for upcoming beta (#457)
---
src/SMAPI.Web/Controllers/IndexController.cs | 53 +++++++++++++++++++++++----
src/SMAPI.Web/ViewModels/IndexModel.cs | 28 +++++---------
src/SMAPI.Web/ViewModels/IndexVersionModel.cs | 41 +++++++++++++++++++++
src/SMAPI.Web/Views/Index/Index.cshtml | 35 ++++++++++++++----
4 files changed, 124 insertions(+), 33 deletions(-)
create mode 100644 src/SMAPI.Web/ViewModels/IndexVersionModel.cs
(limited to 'src')
diff --git a/src/SMAPI.Web/Controllers/IndexController.cs b/src/SMAPI.Web/Controllers/IndexController.cs
index 5d45118f..0464e50a 100644
--- a/src/SMAPI.Web/Controllers/IndexController.cs
+++ b/src/SMAPI.Web/Controllers/IndexController.cs
@@ -3,6 +3,7 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
+using StardewModdingAPI.Common;
using StardewModdingAPI.Web.Framework.Clients.GitHub;
using StardewModdingAPI.Web.ViewModels;
@@ -23,7 +24,10 @@ namespace StardewModdingAPI.Web.Controllers
private readonly IGitHubClient GitHub;
/// The cache time for release info.
- private readonly TimeSpan CacheTime = TimeSpan.FromMinutes(5);
+ private readonly TimeSpan CacheTime = TimeSpan.FromSeconds(1);
+
+ /// The GitHub repository name to check for update.
+ private readonly string RepositoryName = "Pathoschild/SMAPI";
/*********
@@ -42,17 +46,24 @@ namespace StardewModdingAPI.Web.Controllers
[HttpGet]
public async Task Index()
{
- // fetch latest SMAPI release
- GitRelease release = await this.Cache.GetOrCreateAsync("latest-smapi-release", async entry =>
+ // fetch SMAPI releases
+ IndexVersionModel stableVersion = await this.Cache.GetOrCreateAsync("stable-version", async entry =>
+ {
+ entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime);
+ GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: false);
+ return new IndexVersionModel(release.Name, release.Body, this.GetMainDownloadUrl(release), this.GetDevDownloadUrl(release));
+ });
+ IndexVersionModel betaVersion = await this.Cache.GetOrCreateAsync("beta-version", async entry =>
{
entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime);
- return await this.GitHub.GetLatestReleaseAsync("Pathoschild/SMAPI");
+ GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: true);
+ return release.IsPrerelease
+ ? this.GetBetaDownload(release)
+ : null;
});
- string downloadUrl = this.GetMainDownloadUrl(release);
- string devDownloadUrl = this.GetDevDownloadUrl(release);
// render view
- var model = new IndexModel(release.Name, release.Body, downloadUrl, devDownloadUrl);
+ var model = new IndexModel(stableVersion, betaVersion);
return this.View(model);
}
@@ -89,5 +100,33 @@ namespace StardewModdingAPI.Web.Controllers
// fallback just in case
return "https://github.com/pathoschild/SMAPI/releases";
}
+
+ /// Get the latest beta download for a SMAPI release.
+ /// The SMAPI release.
+ private IndexVersionModel GetBetaDownload(GitRelease release)
+ {
+ // get download with the latest version
+ SemanticVersionImpl latestVersion = null;
+ string latestUrl = null;
+ foreach (GitAsset asset in release.Assets ?? new GitAsset[0])
+ {
+ // parse version
+ Match versionMatch = Regex.Match(asset.FileName, @"SMAPI-([\d\.]+(?:-.+)?)-installer.zip");
+ if (!versionMatch.Success || !SemanticVersionImpl.TryParse(versionMatch.Groups[1].Value, out SemanticVersionImpl version))
+ continue;
+
+ // save latest version
+ if (latestVersion == null || latestVersion.CompareTo(version) < 0)
+ {
+ latestVersion = version;
+ latestUrl = asset.DownloadUrl;
+ }
+ }
+
+ // return if prerelease
+ return latestVersion?.Tag != null
+ ? new IndexVersionModel(latestVersion.ToString(), release.Body, latestUrl, null)
+ : null;
+ }
}
}
diff --git a/src/SMAPI.Web/ViewModels/IndexModel.cs b/src/SMAPI.Web/ViewModels/IndexModel.cs
index 6d3da91e..4268c878 100644
--- a/src/SMAPI.Web/ViewModels/IndexModel.cs
+++ b/src/SMAPI.Web/ViewModels/IndexModel.cs
@@ -6,17 +6,11 @@ namespace StardewModdingAPI.Web.ViewModels
/*********
** Accessors
*********/
- /// The latest SMAPI version.
- public string LatestVersion { get; set; }
+ /// The latest stable SMAPI version.
+ public IndexVersionModel StableVersion { get; set; }
- /// The Markdown description for the release.
- public string Description { get; set; }
-
- /// The main download URL.
- public string DownloadUrl { get; set; }
-
- /// The for-developers download URL.
- public string DevDownloadUrl { get; set; }
+ /// The latest prerelease SMAPI version (if newer than ).
+ public IndexVersionModel BetaVersion { get; set; }
/*********
@@ -26,16 +20,12 @@ namespace StardewModdingAPI.Web.ViewModels
public IndexModel() { }
/// Construct an instance.
- /// The latest SMAPI version.
- /// The Markdown description for the release.
- /// The main download URL.
- /// The for-developers download URL.
- internal IndexModel(string latestVersion, string description, string downloadUrl, string devDownloadUrl)
+ /// The latest stable SMAPI version.
+ /// The latest prerelease SMAPI version (if newer than ).
+ internal IndexModel(IndexVersionModel stableVersion, IndexVersionModel betaVersion)
{
- this.LatestVersion = latestVersion;
- this.Description = description;
- this.DownloadUrl = downloadUrl;
- this.DevDownloadUrl = devDownloadUrl;
+ this.StableVersion = stableVersion;
+ this.BetaVersion = betaVersion;
}
}
}
diff --git a/src/SMAPI.Web/ViewModels/IndexVersionModel.cs b/src/SMAPI.Web/ViewModels/IndexVersionModel.cs
new file mode 100644
index 00000000..4f63b979
--- /dev/null
+++ b/src/SMAPI.Web/ViewModels/IndexVersionModel.cs
@@ -0,0 +1,41 @@
+namespace StardewModdingAPI.Web.ViewModels
+{
+ /// The fields for a SMAPI version.
+ public class IndexVersionModel
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// The release version.
+ public string Version { get; set; }
+
+ /// The Markdown description for the release.
+ public string Description { get; set; }
+
+ /// The main download URL.
+ public string DownloadUrl { get; set; }
+
+ /// The for-developers download URL (not applicable for prerelease versions).
+ public string DevDownloadUrl { get; set; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// Construct an instance.
+ public IndexVersionModel() { }
+
+ /// Construct an instance.
+ /// The release number.
+ /// The Markdown description for the release.
+ /// The main download URL.
+ /// The for-developers download URL (not applicable for prerelease versions).
+ internal IndexVersionModel(string version, string description, string downloadUrl, string devDownloadUrl)
+ {
+ this.Version = version;
+ this.Description = description;
+ this.DownloadUrl = downloadUrl;
+ this.DevDownloadUrl = devDownloadUrl;
+ }
+ }
+}
diff --git a/src/SMAPI.Web/Views/Index/Index.cshtml b/src/SMAPI.Web/Views/Index/Index.cshtml
index ad58898e..4efb9f8a 100644
--- a/src/SMAPI.Web/Views/Index/Index.cshtml
+++ b/src/SMAPI.Web/Views/Index/Index.cshtml
@@ -13,7 +13,11 @@
-
Download SMAPI @Model.LatestVersion
+
Download SMAPI @Model.StableVersion.Version
+ @if (Model.BetaVersion != null)
+ {
+
Download SMAPI @Model.BetaVersion.Version
for Stardew Valley 1.3 beta
+ }
Install guide
FAQs

@@ -25,12 +29,29 @@
Get help on Discord or in the forums
-
What's new in SMAPI @Model.LatestVersion?
-
- @Html.Raw(Markdig.Markdown.ToHtml(Model.Description))
-
+@if (Model.BetaVersion == null)
+{
+
What's new in SMAPI @Model.StableVersion.Version?
+
+ @Html.Raw(Markdig.Markdown.ToHtml(Model.StableVersion.Description))
+
+
See the release notes and mod compatibility list for more info.
+}
+else
+{
+
What's new in...
+
SMAPI @Model.StableVersion.Version?
+
+ @Html.Raw(Markdig.Markdown.ToHtml(Model.StableVersion.Description))
+
+
See the release notes and mod compatibility list for more info.
-
See the release notes and mod compatibility list for more info.
+
SMAPI @Model.BetaVersion.Version?
+
+ @Html.Raw(Markdig.Markdown.ToHtml(Model.BetaVersion.Description))
+
+
See the release notes and mod compatibility list for more info.
+}
Donate to support SMAPI ♥
@@ -62,7 +83,7 @@
For mod creators
--
cgit
From ada351b163d928b5c01787e3ac3ad25ee6fe1ce4 Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Fri, 16 Mar 2018 20:28:16 -0400
Subject: reduce cache time for failed update checks to 5 minutes (#454)
---
docs/release-notes.md | 1 +
src/SMAPI.Web/Controllers/ModsApiController.cs | 12 ++++++++----
src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs | 7 +++++--
src/SMAPI.Web/appsettings.json | 3 ++-
4 files changed, 16 insertions(+), 7 deletions(-)
(limited to 'src')
diff --git a/docs/release-notes.md b/docs/release-notes.md
index 393090f2..9d654133 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -21,6 +21,7 @@
* Fixed rare crash with some combinations of manifest fields and internal mod data.
* Fixed update checks failing for Nexus Mods due to a change in their API.
* Fixed update checks failing for some older mods with non-standard versions.
+ * Fixed failed update checks being cached for an hour (now cached 5 minutes).
* Fixed error when a content pack needs a mod that couldn't be loaded.
* Fixed Linux ["magic number is wrong" errors](https://github.com/mono/mono/issues/6752) by changing default terminal order.
* Updated compatibility list and added update checks for more mods.
diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs
index c99c87fb..24517263 100644
--- a/src/SMAPI.Web/Controllers/ModsApiController.cs
+++ b/src/SMAPI.Web/Controllers/ModsApiController.cs
@@ -29,8 +29,11 @@ namespace StardewModdingAPI.Web.Controllers
/// The cache in which to store mod metadata.
private readonly IMemoryCache Cache;
- /// The number of minutes update checks should be cached before refetching them.
- private readonly int CacheMinutes;
+ /// The number of minutes successful update checks should be cached before refetching them.
+ private readonly int SuccessCacheMinutes;
+
+ /// The number of minutes failed update checks should be cached before refetching them.
+ private readonly int ErrorCacheMinutes;
/// A regex which matches SMAPI-style semantic version.
private readonly string VersionRegex;
@@ -50,7 +53,8 @@ namespace StardewModdingAPI.Web.Controllers
ModUpdateCheckConfig config = configProvider.Value;
this.Cache = cache;
- this.CacheMinutes = config.CacheMinutes;
+ this.SuccessCacheMinutes = config.SuccessCacheMinutes;
+ this.ErrorCacheMinutes = config.ErrorCacheMinutes;
this.VersionRegex = config.SemanticVersionRegex;
this.Repositories =
new IModRepository[]
@@ -121,7 +125,7 @@ namespace StardewModdingAPI.Web.Controllers
}
// cache & return
- entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.CacheMinutes);
+ entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(info.Error == null ? this.SuccessCacheMinutes : this.ErrorCacheMinutes);
return info;
});
}
diff --git a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs
index 58c3a100..fc3b7dc2 100644
--- a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs
+++ b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs
@@ -6,8 +6,11 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels
/*********
** Accessors
*********/
- /// The number of minutes update checks should be cached before refetching them.
- public int CacheMinutes { get; set; }
+ /// The number of minutes successful update checks should be cached before refetching them.
+ public int SuccessCacheMinutes { get; set; }
+
+ /// The number of minutes failed update checks should be cached before refetching them.
+ public int ErrorCacheMinutes { get; set; }
/// A regex which matches SMAPI-style semantic version.
/// Derived from SMAPI's SemanticVersion implementation.
diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json
index bfe827fa..03ca31ed 100644
--- a/src/SMAPI.Web/appsettings.json
+++ b/src/SMAPI.Web/appsettings.json
@@ -40,7 +40,8 @@
},
"ModUpdateCheck": {
- "CacheMinutes": 60,
+ "SuccessCacheMinutes": 60,
+ "ErrorCacheMinutes": 5,
"SemanticVersionRegex": "^(?>(?0|[1-9]\\d*))\\.(?>(?0|[1-9]\\d*))(?>(?:\\.(?0|[1-9]\\d*))?)(?:-(?(?>[a-z0-9]+[\\-\\.]?)+))?$",
"ChucklefishKey": "Chucklefish",
--
cgit
From ae061165442ecb93e3a5a81bb918fd4c29e85e3a Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Tue, 20 Mar 2018 00:49:14 -0400
Subject: fix minimum Stardew Valley 1.2 version mistakenly raised in 2.5.3
---
src/SMAPI/Constants.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'src')
diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs
index 7a497a53..1279f8e1 100644
--- a/src/SMAPI/Constants.cs
+++ b/src/SMAPI/Constants.cs
@@ -49,7 +49,7 @@ namespace StardewModdingAPI
#if STARDEW_VALLEY_1_3
new GameVersion("1.3.0.4");
#else
- new SemanticVersion("1.2.33");
+ new SemanticVersion("1.2.30");
#endif
/// The maximum supported version of Stardew Valley.
--
cgit
From 5be3e5af5a08f9e72a969191dca07597d9c7c1f7 Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Tue, 20 Mar 2018 19:45:45 -0400
Subject: rename class to better match usage (#459)
---
src/SMAPI/Framework/ContentCore.cs | 4 +-
src/SMAPI/Metadata/CoreAssetPropagator.cs | 221 ++++++++++++++++++++++++++++++
src/SMAPI/Metadata/CoreAssets.cs | 220 -----------------------------
src/SMAPI/StardewModdingAPI.csproj | 2 +-
4 files changed, 224 insertions(+), 223 deletions(-)
create mode 100644 src/SMAPI/Metadata/CoreAssetPropagator.cs
delete mode 100644 src/SMAPI/Metadata/CoreAssets.cs
(limited to 'src')
diff --git a/src/SMAPI/Framework/ContentCore.cs b/src/SMAPI/Framework/ContentCore.cs
index 85b8db8f..d5848d7b 100644
--- a/src/SMAPI/Framework/ContentCore.cs
+++ b/src/SMAPI/Framework/ContentCore.cs
@@ -51,7 +51,7 @@ namespace StardewModdingAPI.Framework
private readonly IDictionary LanguageCodes;
/// Provides metadata for core game assets.
- private readonly CoreAssets CoreAssets;
+ private readonly CoreAssetPropagator CoreAssets;
/// The assets currently being intercepted by instances. This is used to prevent infinite loops when a loader loads a new asset.
private readonly ContextHash AssetsBeingLoaded = new ContextHash();
@@ -103,7 +103,7 @@ namespace StardewModdingAPI.Framework
this.ModContentPrefix = this.GetAssetNameFromFilePath(Constants.ModPath);
// get asset data
- this.CoreAssets = new CoreAssets(this.NormaliseAssetName, reflection);
+ this.CoreAssets = new CoreAssetPropagator(this.NormaliseAssetName, reflection);
this.Locales = this.GetKeyLocales(reflection);
this.LanguageCodes = this.Locales.ToDictionary(p => p.Value, p => p.Key, StringComparer.InvariantCultureIgnoreCase);
}
diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs
new file mode 100644
index 00000000..d6a731cd
--- /dev/null
+++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs
@@ -0,0 +1,221 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework.Graphics;
+using StardewModdingAPI.Framework.Reflection;
+using StardewValley;
+using StardewValley.BellsAndWhistles;
+using StardewValley.Buildings;
+using StardewValley.Locations;
+using StardewValley.Menus;
+using StardewValley.Objects;
+using StardewValley.Projectiles;
+using StardewValley.TerrainFeatures;
+
+namespace StardewModdingAPI.Metadata
+{
+ /// Handles updating the game when a mod changes core assets.
+ /// This implementation only handles the core assets used by the game itself, and doesn't update any custom references to the changed textures.
+ internal class CoreAssetPropagator
+ {
+ /*********
+ ** Properties
+ *********/
+ /// Normalises an asset key to match the cache key.
+ protected readonly Func GetNormalisedPath;
+
+ /// Setters which update static or singleton texture fields indexed by normalised asset key.
+ private readonly IDictionary> SingletonSetters;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// Initialise the core asset data.
+ /// Normalises an asset key to match the cache key.
+ /// Simplifies access to private code.
+ public CoreAssetPropagator(Func getNormalisedPath, Reflector reflection)
+ {
+ this.GetNormalisedPath = getNormalisedPath;
+ this.SingletonSetters =
+ new Dictionary>
+ {
+ // from CraftingRecipe.InitShared
+ ["Data\\CraftingRecipes"] = (content, key) => CraftingRecipe.craftingRecipes = content.Load>(key),
+ ["Data\\CookingRecipes"] = (content, key) => CraftingRecipe.cookingRecipes = content.Load>(key),
+
+ // from Game1.loadContent
+ ["LooseSprites\\daybg"] = (content, key) => Game1.daybg = content.Load(key),
+ ["LooseSprites\\nightbg"] = (content, key) => Game1.nightbg = content.Load(key),
+ ["Maps\\MenuTiles"] = (content, key) => Game1.menuTexture = content.Load(key),
+ ["LooseSprites\\Lighting\\lantern"] = (content, key) => Game1.lantern = content.Load(key),
+ ["LooseSprites\\Lighting\\windowLight"] = (content, key) => Game1.windowLight = content.Load(key),
+ ["LooseSprites\\Lighting\\sconceLight"] = (content, key) => Game1.sconceLight = content.Load(key),
+ ["LooseSprites\\Lighting\\greenLight"] = (content, key) => Game1.cauldronLight = content.Load(key),
+ ["LooseSprites\\Lighting\\indoorWindowLight"] = (content, key) => Game1.indoorWindowLight = content.Load(key),
+ ["LooseSprites\\shadow"] = (content, key) => Game1.shadowTexture = content.Load(key),
+ ["LooseSprites\\Cursors"] = (content, key) => Game1.mouseCursors = content.Load(key),
+ ["LooseSprites\\ControllerMaps"] = (content, key) => Game1.controllerMaps = content.Load(key),
+ ["TileSheets\\animations"] = (content, key) => Game1.animations = content.Load(key),
+ ["Data\\Achievements"] = (content, key) => Game1.achievements = content.Load>(key),
+ ["Data\\NPCGiftTastes"] = (content, key) => Game1.NPCGiftTastes = content.Load>(key),
+ ["Fonts\\SpriteFont1"] = (content, key) => Game1.dialogueFont = content.Load(key),
+ ["Fonts\\SmallFont"] = (content, key) => Game1.smallFont = content.Load(key),
+ ["Fonts\\tinyFont"] = (content, key) => Game1.tinyFont = content.Load(key),
+ ["Fonts\\tinyFontBorder"] = (content, key) => Game1.tinyFontBorder = content.Load(key),
+ ["Maps\\springobjects"] = (content, key) => Game1.objectSpriteSheet = content.Load(key),
+ ["TileSheets\\crops"] = (content, key) => Game1.cropSpriteSheet = content.Load(key),
+ ["TileSheets\\emotes"] = (content, key) => Game1.emoteSpriteSheet = content.Load(key),
+ ["TileSheets\\debris"] = (content, key) => Game1.debrisSpriteSheet = content.Load(key),
+ ["TileSheets\\Craftables"] = (content, key) => Game1.bigCraftableSpriteSheet = content.Load(key),
+ ["TileSheets\\rain"] = (content, key) => Game1.rainTexture = content.Load(key),
+ ["TileSheets\\BuffsIcons"] = (content, key) => Game1.buffsIcons = content.Load(key),
+ ["Data\\ObjectInformation"] = (content, key) => Game1.objectInformation = content.Load>(key),
+ ["Data\\BigCraftablesInformation"] = (content, key) => Game1.bigCraftablesInformation = content.Load>(key),
+ ["Characters\\Farmer\\hairstyles"] = (content, key) => FarmerRenderer.hairStylesTexture = content.Load(key),
+ ["Characters\\Farmer\\shirts"] = (content, key) => FarmerRenderer.shirtsTexture = content.Load(key),
+ ["Characters\\Farmer\\hats"] = (content, key) => FarmerRenderer.hatsTexture = content.Load(key),
+ ["Characters\\Farmer\\accessories"] = (content, key) => FarmerRenderer.accessoriesTexture = content.Load(key),
+ ["TileSheets\\furniture"] = (content, key) => Furniture.furnitureTexture = content.Load(key),
+ ["LooseSprites\\font_bold"] = (content, key) => SpriteText.spriteTexture = content.Load(key),
+ ["LooseSprites\\font_colored"] = (content, key) => SpriteText.coloredTexture = content.Load(key),
+ ["TileSheets\\weapons"] = (content, key) => Tool.weaponsTexture = content.Load(key),
+ ["TileSheets\\Projectiles"] = (content, key) => Projectile.projectileSheet = content.Load(key),
+
+ // from Game1.ResetToolSpriteSheet
+ ["TileSheets\\tools"] = (content, key) => Game1.ResetToolSpriteSheet(),
+
+#if STARDEW_VALLEY_1_3
+ // from Bush
+ ["TileSheets\\bushes"] = (content, key) => reflection.GetField>(typeof(Bush), "texture").SetValue(new Lazy(() => content.Load(key))),
+
+ // from Farm
+ ["Buildings\\houses"] = (content, key) => reflection.GetField(typeof(Farm), nameof(Farm.houseTextures)).SetValue(content.Load(key)),
+
+ // from Farmer
+ ["Characters\\Farmer\\farmer_base"] = (content, key) =>
+ {
+ if (Game1.player != null && Game1.player.isMale)
+ Game1.player.FarmerRenderer = new FarmerRenderer(key);
+ },
+ ["Characters\\Farmer\\farmer_girl_base"] = (content, key) =>
+ {
+ if (Game1.player != null && !Game1.player.isMale)
+ Game1.player.FarmerRenderer = new FarmerRenderer(key);
+ },
+#else
+ // from Bush
+ ["TileSheets\\bushes"] = (content, key) => Bush.texture = content.Load(key),
+
+ // from Critter
+ ["TileSheets\\critters"] = (content, key) => Critter.critterTexture = content.Load(key),
+
+ // from Farm
+ ["Buildings\\houses"] = (content, key) =>
+ {
+ Farm farm = Game1.getFarm();
+ if (farm != null)
+ farm.houseTextures = content.Load(key);
+ },
+
+ // from Farmer
+ ["Characters\\Farmer\\farmer_base"] = (content, key) =>
+ {
+ if (Game1.player != null && Game1.player.isMale)
+ Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key));
+ },
+ ["Characters\\Farmer\\farmer_girl_base"] = (content, key) =>
+ {
+ if (Game1.player != null && !Game1.player.isMale)
+ Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key));
+ },
+#endif
+
+ // from Flooring
+ ["TerrainFeatures\\Flooring"] = (content, key) => Flooring.floorsTexture = content.Load(key),
+
+ // from FruitTree
+ ["TileSheets\\fruitTrees"] = (content, key) => FruitTree.texture = content.Load(key),
+
+ // from HoeDirt
+ ["TerrainFeatures\\hoeDirt"] = (content, key) => HoeDirt.lightTexture = content.Load(key),
+ ["TerrainFeatures\\hoeDirtDark"] = (content, key) => HoeDirt.darkTexture = content.Load(key),
+ ["TerrainFeatures\\hoeDirtSnow"] = (content, key) => HoeDirt.snowTexture = content.Load(key),
+
+ // from TitleMenu
+ ["Minigames\\Clouds"] = (content, key) =>
+ {
+ if (Game1.activeClickableMenu is TitleMenu)
+ reflection.GetField(Game1.activeClickableMenu, "cloudsTexture").SetValue(content.Load(key));
+ },
+ ["Minigames\\TitleButtons"] = (content, key) =>
+ {
+ if (Game1.activeClickableMenu is TitleMenu titleMenu)
+ {
+ reflection.GetField(titleMenu, "titleButtonsTexture").SetValue(content.Load(key));
+ foreach (TemporaryAnimatedSprite bird in reflection.GetField>(titleMenu, "birds").GetValue())
+#if STARDEW_VALLEY_1_3
+ bird.texture = content.Load(key);
+#else
+ bird.Texture = content.Load(key);
+#endif
+ }
+ },
+
+ // from Wallpaper
+ ["Maps\\walls_and_floors"] = (content, key) => Wallpaper.wallpaperTexture = content.Load(key)
+ }
+ .ToDictionary(p => getNormalisedPath(p.Key), p => p.Value);
+ }
+
+ /// Reload one of the game's core assets (if applicable).
+ /// The content manager through which to reload the asset.
+ /// The asset key to reload.
+ /// Returns whether an asset was reloaded.
+ public bool ReloadForKey(LocalizedContentManager content, string key)
+ {
+ // static assets
+ if (this.SingletonSetters.TryGetValue(key, out Action reload))
+ {
+ reload(content, key);
+ return true;
+ }
+
+ // building textures
+ if (key.StartsWith(this.GetNormalisedPath("Buildings\\")))
+ {
+ Building[] buildings = this.GetAllBuildings().Where(p => key == this.GetNormalisedPath($"Buildings\\{p.buildingType}")).ToArray();
+ if (buildings.Any())
+ {
+#if STARDEW_VALLEY_1_3
+ foreach (Building building in buildings)
+ building.texture = new Lazy(() => content.Load(key));
+#else
+ Texture2D texture = content.Load(key);
+ foreach (Building building in buildings)
+ building.texture = texture;
+#endif
+
+ return true;
+ }
+ return false;
+ }
+
+ return false;
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// Get all player-constructed buildings in the world.
+ private IEnumerable GetAllBuildings()
+ {
+ foreach (BuildableGameLocation location in Game1.locations.OfType())
+ {
+ foreach (Building building in location.buildings)
+ yield return building;
+ }
+ }
+ }
+}
diff --git a/src/SMAPI/Metadata/CoreAssets.cs b/src/SMAPI/Metadata/CoreAssets.cs
deleted file mode 100644
index 87629682..00000000
--- a/src/SMAPI/Metadata/CoreAssets.cs
+++ /dev/null
@@ -1,220 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.Xna.Framework.Graphics;
-using StardewModdingAPI.Framework.Reflection;
-using StardewValley;
-using StardewValley.BellsAndWhistles;
-using StardewValley.Buildings;
-using StardewValley.Locations;
-using StardewValley.Menus;
-using StardewValley.Objects;
-using StardewValley.Projectiles;
-using StardewValley.TerrainFeatures;
-
-namespace StardewModdingAPI.Metadata
-{
- /// Provides metadata about core assets in the game.
- internal class CoreAssets
- {
- /*********
- ** Properties
- *********/
- /// Normalises an asset key to match the cache key.
- protected readonly Func GetNormalisedPath;
-
- /// Setters which update static or singleton texture fields indexed by normalised asset key.
- private readonly IDictionary> SingletonSetters;
-
-
- /*********
- ** Public methods
- *********/
- /// Initialise the core asset data.
- /// Normalises an asset key to match the cache key.
- /// Simplifies access to private code.
- public CoreAssets(Func getNormalisedPath, Reflector reflection)
- {
- this.GetNormalisedPath = getNormalisedPath;
- this.SingletonSetters =
- new Dictionary>
- {
- // from CraftingRecipe.InitShared
- ["Data\\CraftingRecipes"] = (content, key) => CraftingRecipe.craftingRecipes = content.Load>(key),
- ["Data\\CookingRecipes"] = (content, key) => CraftingRecipe.cookingRecipes = content.Load>(key),
-
- // from Game1.loadContent
- ["LooseSprites\\daybg"] = (content, key) => Game1.daybg = content.Load(key),
- ["LooseSprites\\nightbg"] = (content, key) => Game1.nightbg = content.Load(key),
- ["Maps\\MenuTiles"] = (content, key) => Game1.menuTexture = content.Load(key),
- ["LooseSprites\\Lighting\\lantern"] = (content, key) => Game1.lantern = content.Load(key),
- ["LooseSprites\\Lighting\\windowLight"] = (content, key) => Game1.windowLight = content.Load(key),
- ["LooseSprites\\Lighting\\sconceLight"] = (content, key) => Game1.sconceLight = content.Load(key),
- ["LooseSprites\\Lighting\\greenLight"] = (content, key) => Game1.cauldronLight = content.Load(key),
- ["LooseSprites\\Lighting\\indoorWindowLight"] = (content, key) => Game1.indoorWindowLight = content.Load(key),
- ["LooseSprites\\shadow"] = (content, key) => Game1.shadowTexture = content.Load(key),
- ["LooseSprites\\Cursors"] = (content, key) => Game1.mouseCursors = content.Load(key),
- ["LooseSprites\\ControllerMaps"] = (content, key) => Game1.controllerMaps = content.Load(key),
- ["TileSheets\\animations"] = (content, key) => Game1.animations = content.Load(key),
- ["Data\\Achievements"] = (content, key) => Game1.achievements = content.Load>(key),
- ["Data\\NPCGiftTastes"] = (content, key) => Game1.NPCGiftTastes = content.Load>(key),
- ["Fonts\\SpriteFont1"] = (content, key) => Game1.dialogueFont = content.Load(key),
- ["Fonts\\SmallFont"] = (content, key) => Game1.smallFont = content.Load(key),
- ["Fonts\\tinyFont"] = (content, key) => Game1.tinyFont = content.Load(key),
- ["Fonts\\tinyFontBorder"] = (content, key) => Game1.tinyFontBorder = content.Load(key),
- ["Maps\\springobjects"] = (content, key) => Game1.objectSpriteSheet = content.Load(key),
- ["TileSheets\\crops"] = (content, key) => Game1.cropSpriteSheet = content.Load(key),
- ["TileSheets\\emotes"] = (content, key) => Game1.emoteSpriteSheet = content.Load(key),
- ["TileSheets\\debris"] = (content, key) => Game1.debrisSpriteSheet = content.Load(key),
- ["TileSheets\\Craftables"] = (content, key) => Game1.bigCraftableSpriteSheet = content.Load(key),
- ["TileSheets\\rain"] = (content, key) => Game1.rainTexture = content.Load(key),
- ["TileSheets\\BuffsIcons"] = (content, key) => Game1.buffsIcons = content.Load(key),
- ["Data\\ObjectInformation"] = (content, key) => Game1.objectInformation = content.Load>(key),
- ["Data\\BigCraftablesInformation"] = (content, key) => Game1.bigCraftablesInformation = content.Load>(key),
- ["Characters\\Farmer\\hairstyles"] = (content, key) => FarmerRenderer.hairStylesTexture = content.Load(key),
- ["Characters\\Farmer\\shirts"] = (content, key) => FarmerRenderer.shirtsTexture = content.Load(key),
- ["Characters\\Farmer\\hats"] = (content, key) => FarmerRenderer.hatsTexture = content.Load(key),
- ["Characters\\Farmer\\accessories"] = (content, key) => FarmerRenderer.accessoriesTexture = content.Load(key),
- ["TileSheets\\furniture"] = (content, key) => Furniture.furnitureTexture = content.Load(key),
- ["LooseSprites\\font_bold"] = (content, key) => SpriteText.spriteTexture = content.Load(key),
- ["LooseSprites\\font_colored"] = (content, key) => SpriteText.coloredTexture = content.Load(key),
- ["TileSheets\\weapons"] = (content, key) => Tool.weaponsTexture = content.Load(key),
- ["TileSheets\\Projectiles"] = (content, key) => Projectile.projectileSheet = content.Load(key),
-
- // from Game1.ResetToolSpriteSheet
- ["TileSheets\\tools"] = (content, key) => Game1.ResetToolSpriteSheet(),
-
-#if STARDEW_VALLEY_1_3
- // from Bush
- ["TileSheets\\bushes"] = (content, key) => reflection.GetField>(typeof(Bush), "texture").SetValue(new Lazy(() => content.Load(key))),
-
- // from Farm
- ["Buildings\\houses"] = (content, key) => reflection.GetField(typeof(Farm), nameof(Farm.houseTextures)).SetValue(content.Load(key)),
-
- // from Farmer
- ["Characters\\Farmer\\farmer_base"] = (content, key) =>
- {
- if (Game1.player != null && Game1.player.isMale)
- Game1.player.FarmerRenderer = new FarmerRenderer(key);
- },
- ["Characters\\Farmer\\farmer_girl_base"] = (content, key) =>
- {
- if (Game1.player != null && !Game1.player.isMale)
- Game1.player.FarmerRenderer = new FarmerRenderer(key);
- },
-#else
- // from Bush
- ["TileSheets\\bushes"] = (content, key) => Bush.texture = content.Load(key),
-
- // from Critter
- ["TileSheets\\critters"] = (content, key) => Critter.critterTexture = content.Load(key),
-
- // from Farm
- ["Buildings\\houses"] = (content, key) =>
- {
- Farm farm = Game1.getFarm();
- if (farm != null)
- farm.houseTextures = content.Load(key);
- },
-
- // from Farmer
- ["Characters\\Farmer\\farmer_base"] = (content, key) =>
- {
- if (Game1.player != null && Game1.player.isMale)
- Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key));
- },
- ["Characters\\Farmer\\farmer_girl_base"] = (content, key) =>
- {
- if (Game1.player != null && !Game1.player.isMale)
- Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key));
- },
-#endif
-
- // from Flooring
- ["TerrainFeatures\\Flooring"] = (content, key) => Flooring.floorsTexture = content.Load(key),
-
- // from FruitTree
- ["TileSheets\\fruitTrees"] = (content, key) => FruitTree.texture = content.Load(key),
-
- // from HoeDirt
- ["TerrainFeatures\\hoeDirt"] = (content, key) => HoeDirt.lightTexture = content.Load(key),
- ["TerrainFeatures\\hoeDirtDark"] = (content, key) => HoeDirt.darkTexture = content.Load(key),
- ["TerrainFeatures\\hoeDirtSnow"] = (content, key) => HoeDirt.snowTexture = content.Load(key),
-
- // from TitleMenu
- ["Minigames\\Clouds"] = (content, key) =>
- {
- if (Game1.activeClickableMenu is TitleMenu)
- reflection.GetField(Game1.activeClickableMenu, "cloudsTexture").SetValue(content.Load(key));
- },
- ["Minigames\\TitleButtons"] = (content, key) =>
- {
- if (Game1.activeClickableMenu is TitleMenu titleMenu)
- {
- reflection.GetField(titleMenu, "titleButtonsTexture").SetValue(content.Load(key));
- foreach (TemporaryAnimatedSprite bird in reflection.GetField>(titleMenu, "birds").GetValue())
-#if STARDEW_VALLEY_1_3
- bird.texture = content.Load(key);
-#else
- bird.Texture = content.Load(key);
-#endif
- }
- },
-
- // from Wallpaper
- ["Maps\\walls_and_floors"] = (content, key) => Wallpaper.wallpaperTexture = content.Load(key)
- }
- .ToDictionary(p => getNormalisedPath(p.Key), p => p.Value);
- }
-
- /// Reload one of the game's core assets (if applicable).
- /// The content manager through which to reload the asset.
- /// The asset key to reload.
- /// Returns whether an asset was reloaded.
- public bool ReloadForKey(LocalizedContentManager content, string key)
- {
- // static assets
- if (this.SingletonSetters.TryGetValue(key, out Action reload))
- {
- reload(content, key);
- return true;
- }
-
- // building textures
- if (key.StartsWith(this.GetNormalisedPath("Buildings\\")))
- {
- Building[] buildings = this.GetAllBuildings().Where(p => key == this.GetNormalisedPath($"Buildings\\{p.buildingType}")).ToArray();
- if (buildings.Any())
- {
-#if STARDEW_VALLEY_1_3
- foreach (Building building in buildings)
- building.texture = new Lazy(() => content.Load(key));
-#else
- Texture2D texture = content.Load(key);
- foreach (Building building in buildings)
- building.texture = texture;
-#endif
-
- return true;
- }
- return false;
- }
-
- return false;
- }
-
-
- /*********
- ** Private methods
- *********/
- /// Get all player-constructed buildings in the world.
- private IEnumerable GetAllBuildings()
- {
- foreach (BuildableGameLocation location in Game1.locations.OfType())
- {
- foreach (Building building in location.buildings)
- yield return building;
- }
- }
- }
-}
diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj
index bffb96e2..82a5602d 100644
--- a/src/SMAPI/StardewModdingAPI.csproj
+++ b/src/SMAPI/StardewModdingAPI.csproj
@@ -137,7 +137,7 @@
-
+
--
cgit
From de5ee6f928339198d3c3ab0a91e9343863782c59 Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Tue, 20 Mar 2018 21:22:19 -0400
Subject: rewrite core asset logic for extensibility (#459)
---
src/SMAPI/Framework/ContentCore.cs | 2 +-
src/SMAPI/Metadata/CoreAssetPropagator.cs | 379 +++++++++++++++++++-----------
2 files changed, 249 insertions(+), 132 deletions(-)
(limited to 'src')
diff --git a/src/SMAPI/Framework/ContentCore.cs b/src/SMAPI/Framework/ContentCore.cs
index d5848d7b..3c7e7b5a 100644
--- a/src/SMAPI/Framework/ContentCore.cs
+++ b/src/SMAPI/Framework/ContentCore.cs
@@ -368,7 +368,7 @@ namespace StardewModdingAPI.Framework
int reloaded = 0;
foreach (string key in removeAssetNames)
{
- if (this.CoreAssets.ReloadForKey(Game1.content, key)) // use an intercepted content manager
+ if (this.CoreAssets.Propagate(Game1.content, key)) // use an intercepted content manager
reloaded++;
}
diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs
index d6a731cd..85021727 100644
--- a/src/SMAPI/Metadata/CoreAssetPropagator.cs
+++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs
@@ -14,18 +14,17 @@ using StardewValley.TerrainFeatures;
namespace StardewModdingAPI.Metadata
{
- /// Handles updating the game when a mod changes core assets.
- /// This implementation only handles the core assets used by the game itself, and doesn't update any custom references to the changed textures.
+ /// Propagates changes to core assets to the game state.
internal class CoreAssetPropagator
{
/*********
** Properties
*********/
/// Normalises an asset key to match the cache key.
- protected readonly Func GetNormalisedPath;
+ private readonly Func GetNormalisedPath;
- /// Setters which update static or singleton texture fields indexed by normalised asset key.
- private readonly IDictionary> SingletonSetters;
+ /// Simplifies access to private game code.
+ private readonly Reflector Reflection;
/*********
@@ -37,154 +36,272 @@ namespace StardewModdingAPI.Metadata
public CoreAssetPropagator(Func getNormalisedPath, Reflector reflection)
{
this.GetNormalisedPath = getNormalisedPath;
- this.SingletonSetters =
- new Dictionary>
- {
- // from CraftingRecipe.InitShared
- ["Data\\CraftingRecipes"] = (content, key) => CraftingRecipe.craftingRecipes = content.Load>(key),
- ["Data\\CookingRecipes"] = (content, key) => CraftingRecipe.cookingRecipes = content.Load>(key),
-
- // from Game1.loadContent
- ["LooseSprites\\daybg"] = (content, key) => Game1.daybg = content.Load(key),
- ["LooseSprites\\nightbg"] = (content, key) => Game1.nightbg = content.Load(key),
- ["Maps\\MenuTiles"] = (content, key) => Game1.menuTexture = content.Load(key),
- ["LooseSprites\\Lighting\\lantern"] = (content, key) => Game1.lantern = content.Load(key),
- ["LooseSprites\\Lighting\\windowLight"] = (content, key) => Game1.windowLight = content.Load(key),
- ["LooseSprites\\Lighting\\sconceLight"] = (content, key) => Game1.sconceLight = content.Load(key),
- ["LooseSprites\\Lighting\\greenLight"] = (content, key) => Game1.cauldronLight = content.Load(key),
- ["LooseSprites\\Lighting\\indoorWindowLight"] = (content, key) => Game1.indoorWindowLight = content.Load(key),
- ["LooseSprites\\shadow"] = (content, key) => Game1.shadowTexture = content.Load(key),
- ["LooseSprites\\Cursors"] = (content, key) => Game1.mouseCursors = content.Load(key),
- ["LooseSprites\\ControllerMaps"] = (content, key) => Game1.controllerMaps = content.Load(key),
- ["TileSheets\\animations"] = (content, key) => Game1.animations = content.Load(key),
- ["Data\\Achievements"] = (content, key) => Game1.achievements = content.Load>(key),
- ["Data\\NPCGiftTastes"] = (content, key) => Game1.NPCGiftTastes = content.Load>(key),
- ["Fonts\\SpriteFont1"] = (content, key) => Game1.dialogueFont = content.Load(key),
- ["Fonts\\SmallFont"] = (content, key) => Game1.smallFont = content.Load(key),
- ["Fonts\\tinyFont"] = (content, key) => Game1.tinyFont = content.Load(key),
- ["Fonts\\tinyFontBorder"] = (content, key) => Game1.tinyFontBorder = content.Load(key),
- ["Maps\\springobjects"] = (content, key) => Game1.objectSpriteSheet = content.Load(key),
- ["TileSheets\\crops"] = (content, key) => Game1.cropSpriteSheet = content.Load(key),
- ["TileSheets\\emotes"] = (content, key) => Game1.emoteSpriteSheet = content.Load(key),
- ["TileSheets\\debris"] = (content, key) => Game1.debrisSpriteSheet = content.Load(key),
- ["TileSheets\\Craftables"] = (content, key) => Game1.bigCraftableSpriteSheet = content.Load(key),
- ["TileSheets\\rain"] = (content, key) => Game1.rainTexture = content.Load(key),
- ["TileSheets\\BuffsIcons"] = (content, key) => Game1.buffsIcons = content.Load(key),
- ["Data\\ObjectInformation"] = (content, key) => Game1.objectInformation = content.Load>(key),
- ["Data\\BigCraftablesInformation"] = (content, key) => Game1.bigCraftablesInformation = content.Load>(key),
- ["Characters\\Farmer\\hairstyles"] = (content, key) => FarmerRenderer.hairStylesTexture = content.Load(key),
- ["Characters\\Farmer\\shirts"] = (content, key) => FarmerRenderer.shirtsTexture = content.Load(key),
- ["Characters\\Farmer\\hats"] = (content, key) => FarmerRenderer.hatsTexture = content.Load(key),
- ["Characters\\Farmer\\accessories"] = (content, key) => FarmerRenderer.accessoriesTexture = content.Load(key),
- ["TileSheets\\furniture"] = (content, key) => Furniture.furnitureTexture = content.Load(key),
- ["LooseSprites\\font_bold"] = (content, key) => SpriteText.spriteTexture = content.Load(key),
- ["LooseSprites\\font_colored"] = (content, key) => SpriteText.coloredTexture = content.Load(key),
- ["TileSheets\\weapons"] = (content, key) => Tool.weaponsTexture = content.Load(key),
- ["TileSheets\\Projectiles"] = (content, key) => Projectile.projectileSheet = content.Load(key),
-
- // from Game1.ResetToolSpriteSheet
- ["TileSheets\\tools"] = (content, key) => Game1.ResetToolSpriteSheet(),
+ this.Reflection = reflection;
+ }
-#if STARDEW_VALLEY_1_3
- // from Bush
- ["TileSheets\\bushes"] = (content, key) => reflection.GetField>(typeof(Bush), "texture").SetValue(new Lazy(() => content.Load(key))),
+ /// Reload one of the game's core assets (if applicable).
+ /// The content manager through which to reload the asset.
+ /// The asset key to reload.
+ /// Returns whether an asset was reloaded.
+ public bool Propagate(LocalizedContentManager content, string key)
+ {
+ return this.PropagateImpl(content, key) != null;
+ }
- // from Farm
- ["Buildings\\houses"] = (content, key) => reflection.GetField(typeof(Farm), nameof(Farm.houseTextures)).SetValue(content.Load(key)),
- // from Farmer
- ["Characters\\Farmer\\farmer_base"] = (content, key) =>
- {
- if (Game1.player != null && Game1.player.isMale)
- Game1.player.FarmerRenderer = new FarmerRenderer(key);
- },
- ["Characters\\Farmer\\farmer_girl_base"] = (content, key) =>
+ /*********
+ ** Private methods
+ *********/
+ /// Reload one of the game's core assets (if applicable).
+ /// The content manager through which to reload the asset.
+ /// The asset key to reload.
+ /// Returns any non-null value to indicate an asset was loaded..
+ private object PropagateImpl(LocalizedContentManager content, string key)
+ {
+ Reflector reflection = this.Reflection;
+ switch (key.ToLower().Replace("/", "\\")) // normalised key so we can compare statically
+ {
+ /****
+ ** Buildings
+ ****/
+ case "buildings\\houses": // Farm
+#if STARDEW_VALLEY_1_3
+ reflection.GetField(typeof(Farm), nameof(Farm.houseTextures)).SetValue(content.Load(key));
+ return true;
+#else
{
- if (Game1.player != null && !Game1.player.isMale)
- Game1.player.FarmerRenderer = new FarmerRenderer(key);
- },
+ Farm farm = Game1.getFarm();
+ if (farm == null)
+ return null;
+ return farm.houseTextures = content.Load(key);
+ }
+#endif
+
+ /****
+ ** Content\Characters\Farmer
+ ****/
+ case "characters\\farmer\\accessories": // Game1.loadContent
+ return FarmerRenderer.accessoriesTexture = content.Load(key);
+
+ case "characters\\farmer\\farmer_base": // Farmer
+ if (Game1.player == null || !Game1.player.isMale)
+ return null;
+#if STARDEW_VALLEY_1_3
+ return Game1.player.FarmerRenderer = new FarmerRenderer(key);
#else
- // from Bush
- ["TileSheets\\bushes"] = (content, key) => Bush.texture = content.Load(key),
+ return Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key));
+#endif
- // from Critter
- ["TileSheets\\critters"] = (content, key) => Critter.critterTexture = content.Load(key),
+ case "characters\\farmer\\farmer_girl_base": // Farmer
+ if (Game1.player == null || Game1.player.isMale)
+ return null;
+#if STARDEW_VALLEY_1_3
+ return Game1.player.FarmerRenderer = new FarmerRenderer(key);
+#else
+ return Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key));
+#endif
- // from Farm
- ["Buildings\\houses"] = (content, key) =>
- {
- Farm farm = Game1.getFarm();
- if (farm != null)
- farm.houseTextures = content.Load(key);
- },
+ case "characters\\farmer\\hairstyles": // Game1.loadContent
+ return FarmerRenderer.hairStylesTexture = content.Load(key);
+
+ case "characters\\farmer\\hats": // Game1.loadContent
+ return FarmerRenderer.hatsTexture = content.Load(key);
+
+ case "characters\\farmer\\shirts": // Game1.loadContent
+ return FarmerRenderer.shirtsTexture = content.Load(key);
+
+ /****
+ ** Content\Data
+ ****/
+ case "data\\achievements": // Game1.loadContent
+ return Game1.achievements = content.Load>(key);
+
+ case "data\\bigcraftablesinformation": // Game1.loadContent
+ return Game1.bigCraftablesInformation = content.Load>(key);
+
+ case "data\\cookingrecipes": // CraftingRecipe.InitShared
+ return CraftingRecipe.cookingRecipes = content.Load>(key);
+
+ case "data\\craftingrecipes": // CraftingRecipe.InitShared
+ return CraftingRecipe.craftingRecipes = content.Load>(key);
+
+ case "data\\npcgifttastes": // Game1.loadContent
+ return Game1.NPCGiftTastes = content.Load>(key);
+
+ case "data\\objectinformation": // Game1.loadContent
+ return Game1.objectInformation = content.Load>(key);
+
+ /****
+ ** Content\Fonts
+ ****/
+ case "fonts\\spritefont1": // Game1.loadContent
+ return Game1.dialogueFont = content.Load(key);
+
+ case "fonts\\smallfont": // Game1.loadContent
+ return Game1.smallFont = content.Load(key);
+
+ case "fonts\\tinyfont": // Game1.loadContent
+ return Game1.tinyFont = content.Load(key);
+
+ case "fonts\\tinyfontborder": // Game1.loadContent
+ return Game1.tinyFontBorder = content.Load(key);
- // from Farmer
- ["Characters\\Farmer\\farmer_base"] = (content, key) =>
+ /****
+ ** Content\Lighting
+ ****/
+ case "loosesprites\\lighting\\greenlight": // Game1.loadContent
+ return Game1.cauldronLight = content.Load(key);
+
+ case "loosesprites\\lighting\\indoorwindowlight": // Game1.loadContent
+ return Game1.indoorWindowLight = content.Load(key);
+
+ case "loosesprites\\lighting\\lantern": // Game1.loadContent
+ return Game1.lantern = content.Load(key);
+
+ case "loosesprites\\lighting\\sconcelight": // Game1.loadContent
+ return Game1.sconceLight = content.Load(key);
+
+ case "loosesprites\\lighting\\windowlight": // Game1.loadContent
+ return Game1.windowLight = content.Load(key);
+
+ /****
+ ** Content\LooseSprites
+ ****/
+ case "loosesprites\\controllermaps": // Game1.loadContent
+ return Game1.controllerMaps = content.Load(key);
+
+ case "loosesprites\\cursors": // Game1.loadContent
+ return Game1.mouseCursors = content.Load(key);
+
+ case "loosesprites\\daybg": // Game1.loadContent
+ return Game1.daybg = content.Load(key);
+
+ case "loosesprites\\font_bold": // Game1.loadContent
+ return SpriteText.spriteTexture = content.Load(key);
+
+ case "loosesprites\\font_colored": // Game1.loadContent
+ return SpriteText.coloredTexture = content.Load(key);
+
+ case "loosesprites\\nightbg": // Game1.loadContent
+ return Game1.nightbg = content.Load(key);
+
+ case "loosesprites\\shadow": // Game1.loadContent
+ return Game1.shadowTexture = content.Load(key);
+
+ /****
+ ** Content\Critters
+ ****/
+ case "tilesheets\\critters": // Criter.InitShared
+ return Critter.critterTexture = content.Load(key);
+
+ case "tilesheets\\crops": // Game1.loadContent
+ return Game1.cropSpriteSheet = content.Load(key);
+
+ case "tilesheets\\debris": // Game1.loadContent
+ return Game1.debrisSpriteSheet = content.Load(key);
+
+ case "tilesheets\\emotes": // Game1.loadContent
+ return Game1.emoteSpriteSheet = content.Load(key);
+
+ case "tilesheets\\furniture": // Game1.loadContent
+ return Furniture.furnitureTexture = content.Load(key);
+
+ case "tilesheets\\projectiles": // Game1.loadContent
+ return Projectile.projectileSheet = content.Load(key);
+
+ case "tilesheets\\rain": // Game1.loadContent
+ return Game1.rainTexture = content.Load(key);
+
+ case "tilesheets\\tools": // Game1.ResetToolSpriteSheet
+ Game1.ResetToolSpriteSheet();
+ return true;
+
+ case "tilesheets\\weapons": // Game1.loadContent
+ return Tool.weaponsTexture = content.Load(key);
+
+ /****
+ ** Content\Maps
+ ****/
+ case "maps\\menutiles": // Game1.loadContent
+ return Game1.menuTexture = content.Load(key);
+
+ case "maps\\springobjects": // Game1.loadContent
+ return Game1.objectSpriteSheet = content.Load(key);
+
+ case "maps\\walls_and_floors": // Wallpaper
+ return Wallpaper.wallpaperTexture = content.Load(key);
+
+ /****
+ ** Content\Minigames
+ ****/
+ case "minigames\\clouds": // TitleMenu
+ if (Game1.activeClickableMenu is TitleMenu)
{
- if (Game1.player != null && Game1.player.isMale)
- Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key));
- },
- ["Characters\\Farmer\\farmer_girl_base"] = (content, key) =>
+ reflection.GetField(Game1.activeClickableMenu, "cloudsTexture").SetValue(content.Load(key));
+ return true;
+ }
+
+ return null;
+
+ case "minigames\\titlebuttons": // TitleMenu
+ if (Game1.activeClickableMenu is TitleMenu titleMenu)
{
- if (Game1.player != null && !Game1.player.isMale)
- Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key));
- },
+ Texture2D texture = content.Load(key);
+ reflection.GetField(titleMenu, "titleButtonsTexture").SetValue(texture);
+ foreach (TemporaryAnimatedSprite bird in reflection.GetField