diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2018-11-19 13:48:19 -0500 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2018-11-19 13:48:19 -0500 |
commit | 593723b7940ba72a786fc4c7366c56f9813d977b (patch) | |
tree | 4d23fbef5bc5a20115f10ca04ae3379df78cc8e1 /src/SMAPI.Web | |
parent | 4f28ea33bd7cc65485402c5e85259083e86b49e1 (diff) | |
parent | 3dc27a5681dcfc4ae30e95570d9966f2e14a4dd7 (diff) | |
download | SMAPI-593723b7940ba72a786fc4c7366c56f9813d977b.tar.gz SMAPI-593723b7940ba72a786fc4c7366c56f9813d977b.tar.bz2 SMAPI-593723b7940ba72a786fc4c7366c56f9813d977b.zip |
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI.Web')
32 files changed, 1030 insertions, 1469 deletions
diff --git a/src/SMAPI.Web/Controllers/IndexController.cs b/src/SMAPI.Web/Controllers/IndexController.cs index 8c4a0332..d7be664d 100644 --- a/src/SMAPI.Web/Controllers/IndexController.cs +++ b/src/SMAPI.Web/Controllers/IndexController.cs @@ -67,15 +67,22 @@ namespace StardewModdingAPI.Web.Controllers IndexVersionModel stableVersionModel = stableVersion != null ? new IndexVersionModel(stableVersion.Version.ToString(), stableVersion.Release.Body, stableVersion.Asset.DownloadUrl, stableVersionForDevs?.Asset.DownloadUrl) : new IndexVersionModel("unknown", "", "https://github.com/Pathoschild/SMAPI/releases", null); // just in case something goes wrong) - IndexVersionModel betaVersionModel = betaVersion != null && this.SiteConfig.EnableSmapiBeta + IndexVersionModel betaVersionModel = betaVersion != null && this.SiteConfig.BetaEnabled ? new IndexVersionModel(betaVersion.Version.ToString(), betaVersion.Release.Body, betaVersion.Asset.DownloadUrl, betaVersionForDevs?.Asset.DownloadUrl) : null; // render view - var model = new IndexModel(stableVersionModel, betaVersionModel); + var model = new IndexModel(stableVersionModel, betaVersionModel, this.SiteConfig.BetaBlurb); return this.View(model); } + /// <summary>Display the index page.</summary> + [HttpGet("/privacy")] + public ViewResult Privacy() + { + return this.View(); + } + /********* ** Private methods diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs index 18d55665..f0835592 100644 --- a/src/SMAPI.Web/Controllers/ModsApiController.cs +++ b/src/SMAPI.Web/Controllers/ModsApiController.cs @@ -12,6 +12,7 @@ using StardewModdingAPI.Toolkit; using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; using StardewModdingAPI.Toolkit.Framework.Clients.Wiki; using StardewModdingAPI.Toolkit.Framework.ModData; +using StardewModdingAPI.Toolkit.Framework.UpdateData; using StardewModdingAPI.Web.Framework.Clients.Chucklefish; using StardewModdingAPI.Web.Framework.Clients.GitHub; using StardewModdingAPI.Web.Framework.Clients.Nexus; @@ -29,7 +30,7 @@ namespace StardewModdingAPI.Web.Controllers ** Properties *********/ /// <summary>The mod repositories which provide mod metadata.</summary> - private readonly IDictionary<string, IModRepository> Repositories; + private readonly IDictionary<ModRepositoryKey, IModRepository> Repositories; /// <summary>The cache in which to store mod metadata.</summary> private readonly IMemoryCache Cache; @@ -46,8 +47,8 @@ namespace StardewModdingAPI.Web.Controllers /// <summary>The internal mod metadata list.</summary> private readonly ModDatabase ModDatabase; - /// <summary>The web URL for the wiki compatibility list.</summary> - private readonly string WikiCompatibilityPageUrl; + /// <summary>The web URL for the compatibility list.</summary> + private readonly string CompatibilityPageUrl; /********* @@ -64,7 +65,7 @@ namespace StardewModdingAPI.Web.Controllers { this.ModDatabase = new ModToolkit().GetModDatabase(Path.Combine(environment.WebRootPath, "StardewModdingAPI.metadata.json")); ModUpdateCheckConfig config = configProvider.Value; - this.WikiCompatibilityPageUrl = config.WikiCompatibilityPageUrl; + this.CompatibilityPageUrl = config.CompatibilityPageUrl; this.Cache = cache; this.SuccessCacheMinutes = config.SuccessCacheMinutes; @@ -73,11 +74,11 @@ namespace StardewModdingAPI.Web.Controllers this.Repositories = new IModRepository[] { - new ChucklefishRepository(config.ChucklefishKey, chucklefish), - new GitHubRepository(config.GitHubKey, github), - new NexusRepository(config.NexusKey, nexus) + new ChucklefishRepository(chucklefish), + new GitHubRepository(github), + new NexusRepository(nexus) } - .ToDictionary(p => p.VendorKey, StringComparer.CurrentCultureIgnoreCase); + .ToDictionary(p => p.VendorKey); } /// <summary>Fetch version metadata for the given mods.</summary> @@ -89,7 +90,7 @@ namespace StardewModdingAPI.Web.Controllers return new ModEntryModel[0]; // fetch wiki data - WikiCompatibilityEntry[] wikiData = await this.GetWikiDataAsync(); + WikiModEntry[] wikiData = await this.GetWikiDataAsync(); IDictionary<string, ModEntryModel> mods = new Dictionary<string, ModEntryModel>(StringComparer.CurrentCultureIgnoreCase); foreach (ModSearchEntryModel mod in model.Mods) { @@ -113,17 +114,12 @@ namespace StardewModdingAPI.Web.Controllers /// <param name="wikiData">The wiki data.</param> /// <param name="includeExtendedMetadata">Whether to include extended metadata for each mod.</param> /// <returns>Returns the mod data if found, else <c>null</c>.</returns> - private async Task<ModEntryModel> GetModData(ModSearchEntryModel search, WikiCompatibilityEntry[] wikiData, bool includeExtendedMetadata) + private async Task<ModEntryModel> GetModData(ModSearchEntryModel search, WikiModEntry[] wikiData, bool includeExtendedMetadata) { - // resolve update keys - var updateKeys = new HashSet<string>(search.UpdateKeys ?? new string[0], StringComparer.InvariantCultureIgnoreCase); + // crossreference data ModDataRecord record = this.ModDatabase.Get(search.ID); - if (record?.Fields != null) - { - string defaultUpdateKey = record.Fields.FirstOrDefault(p => p.Key == ModDataFieldKey.UpdateKey && p.IsDefault)?.Value; - if (!string.IsNullOrWhiteSpace(defaultUpdateKey)) - updateKeys.Add(defaultUpdateKey); - } + WikiModEntry wikiEntry = wikiData.FirstOrDefault(entry => entry.ID.Contains(search.ID.Trim(), StringComparer.InvariantCultureIgnoreCase)); + string[] updateKeys = this.GetUpdateKeys(search.UpdateKeys, record, wikiEntry).ToArray(); // get latest versions ModEntryModel result = new ModEntryModel { ID = search.ID }; @@ -166,9 +162,25 @@ namespace StardewModdingAPI.Web.Controllers } // get unofficial version - WikiCompatibilityEntry wikiEntry = wikiData.FirstOrDefault(entry => entry.ID.Contains(result.ID.Trim(), StringComparer.InvariantCultureIgnoreCase)); - if (wikiEntry?.UnofficialVersion != null && this.IsNewer(wikiEntry.UnofficialVersion, result.Main?.Version) && this.IsNewer(wikiEntry.UnofficialVersion, result.Optional?.Version)) - result.Unofficial = new ModEntryVersionModel(wikiEntry.UnofficialVersion, this.WikiCompatibilityPageUrl); + if (wikiEntry?.Compatibility.UnofficialVersion != null && this.IsNewer(wikiEntry.Compatibility.UnofficialVersion, result.Main?.Version) && this.IsNewer(wikiEntry.Compatibility.UnofficialVersion, result.Optional?.Version)) + result.Unofficial = new ModEntryVersionModel(wikiEntry.Compatibility.UnofficialVersion, $"{this.CompatibilityPageUrl}/#{wikiEntry.Anchor}"); + + // get unofficial version for beta + if (wikiEntry?.HasBetaInfo == true) + { + result.HasBetaInfo = true; + if (wikiEntry.BetaCompatibility.Status == WikiCompatibilityStatus.Unofficial) + { + if (wikiEntry.BetaCompatibility.UnofficialVersion != null) + { + result.UnofficialForBeta = (wikiEntry.BetaCompatibility.UnofficialVersion != null && this.IsNewer(wikiEntry.BetaCompatibility.UnofficialVersion, result.Main?.Version) && this.IsNewer(wikiEntry.BetaCompatibility.UnofficialVersion, result.Optional?.Version)) + ? new ModEntryVersionModel(wikiEntry.BetaCompatibility.UnofficialVersion, $"{this.CompatibilityPageUrl}/#{wikiEntry.Anchor}") + : null; + } + else + result.UnofficialForBeta = result.Unofficial; + } + } // fallback to preview if latest is invalid if (result.Main == null && result.Optional != null) @@ -195,28 +207,6 @@ namespace StardewModdingAPI.Web.Controllers return result; } - /// <summary>Parse a namespaced mod ID.</summary> - /// <param name="raw">The raw mod ID to parse.</param> - /// <param name="vendorKey">The parsed vendor key.</param> - /// <param name="modID">The parsed mod ID.</param> - /// <returns>Returns whether the value could be parsed.</returns> - private bool TryParseModKey(string raw, out string vendorKey, out string modID) - { - // split parts - string[] parts = raw?.Split(':'); - if (parts == null || parts.Length != 2) - { - vendorKey = null; - modID = null; - return false; - } - - // parse - vendorKey = parts[0].Trim(); - modID = parts[1].Trim(); - return true; - } - /// <summary>Get whether a <paramref name="current"/> version is newer than an <paramref name="other"/> version.</summary> /// <param name="current">The current version.</param> /// <param name="other">The other version.</param> @@ -226,21 +216,21 @@ namespace StardewModdingAPI.Web.Controllers } /// <summary>Get mod data from the wiki compatibility list.</summary> - private async Task<WikiCompatibilityEntry[]> GetWikiDataAsync() + private async Task<WikiModEntry[]> GetWikiDataAsync() { ModToolkit toolkit = new ModToolkit(); - return await this.Cache.GetOrCreateAsync($"_wiki", async entry => + return await this.Cache.GetOrCreateAsync("_wiki", async entry => { try { - WikiCompatibilityEntry[] entries = await toolkit.GetWikiCompatibilityListAsync(); + WikiModEntry[] entries = (await toolkit.GetWikiCompatibilityListAsync()).Mods; entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.SuccessCacheMinutes); return entries; } catch { entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.ErrorCacheMinutes); - return new WikiCompatibilityEntry[0]; + return new WikiModEntry[0]; } }); } @@ -250,18 +240,19 @@ namespace StardewModdingAPI.Web.Controllers private async Task<ModInfoModel> GetInfoForUpdateKeyAsync(string updateKey) { // parse update key - if (!this.TryParseModKey(updateKey, out string vendorKey, out string modID)) + UpdateKey parsed = UpdateKey.Parse(updateKey); + if (!parsed.LooksValid) return new ModInfoModel($"The update key '{updateKey}' isn't in a valid format. It should contain the site key and mod ID like 'Nexus:541'."); // get matching repository - if (!this.Repositories.TryGetValue(vendorKey, out IModRepository repository)) - return new ModInfoModel($"There's no mod site with key '{vendorKey}'. Expected one of [{string.Join(", ", this.Repositories.Keys)}]."); + if (!this.Repositories.TryGetValue(parsed.Repository, out IModRepository repository)) + return new ModInfoModel($"There's no mod site with key '{parsed.Repository}'. Expected one of [{string.Join(", ", this.Repositories.Keys)}]."); // fetch mod info - return await this.Cache.GetOrCreateAsync($"{repository.VendorKey}:{modID}".ToLower(), async entry => + return await this.Cache.GetOrCreateAsync($"{repository.VendorKey}:{parsed.ID}".ToLower(), async entry => { - ModInfoModel result = await repository.GetModInfoAsync(modID); - if (result.Error != null) + ModInfoModel result = await repository.GetModInfoAsync(parsed.ID); + if (result.Error == null) { if (result.Version == null) result.Error = $"The update key '{updateKey}' matches a mod with no version number."; @@ -273,11 +264,42 @@ namespace StardewModdingAPI.Web.Controllers }); } - /// <summary>Get the requested API version.</summary> - private ISemanticVersion GetApiVersion() + /// <summary>Get update keys based on the available mod metadata, while maintaining the precedence order.</summary> + /// <param name="specifiedKeys">The specified update keys.</param> + /// <param name="record">The mod's entry in SMAPI's internal database.</param> + /// <param name="entry">The mod's entry in the wiki list.</param> + public IEnumerable<string> GetUpdateKeys(string[] specifiedKeys, ModDataRecord record, WikiModEntry entry) { - string actualVersion = (string)this.RouteData.Values["version"]; - return new SemanticVersion(actualVersion); + IEnumerable<string> GetRaw() + { + // specified update keys + if (specifiedKeys != null) + { + foreach (string key in specifiedKeys) + yield return key?.Trim(); + } + + // default update key + string defaultKey = record?.GetDefaultUpdateKey(); + if (defaultKey != null) + yield return defaultKey; + + // wiki metadata + if (entry != null) + { + if (entry.NexusID.HasValue) + yield return $"Nexus:{entry.NexusID}"; + if (entry.ChucklefishID.HasValue) + yield return $"Chucklefish:{entry.ChucklefishID}"; + } + } + + HashSet<string> seen = new HashSet<string>(StringComparer.InvariantCulture); + foreach (string key in GetRaw()) + { + if (!string.IsNullOrWhiteSpace(key) && seen.Add(key)) + yield return key; + } } } } diff --git a/src/SMAPI.Web/Controllers/ModsController.cs b/src/SMAPI.Web/Controllers/ModsController.cs new file mode 100644 index 00000000..57aa9da9 --- /dev/null +++ b/src/SMAPI.Web/Controllers/ModsController.cs @@ -0,0 +1,74 @@ +using System; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; +using StardewModdingAPI.Toolkit; +using StardewModdingAPI.Toolkit.Framework.Clients.Wiki; +using StardewModdingAPI.Web.Framework.ConfigModels; +using StardewModdingAPI.Web.ViewModels; + +namespace StardewModdingAPI.Web.Controllers +{ + /// <summary>Provides user-friendly info about SMAPI mods.</summary> + internal class ModsController : Controller + { + /********* + ** Properties + *********/ + /// <summary>The cache in which to store mod metadata.</summary> + private readonly IMemoryCache Cache; + + /// <summary>The number of minutes successful update checks should be cached before refetching them.</summary> + private readonly int SuccessCacheMinutes; + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="cache">The cache in which to store mod metadata.</param> + /// <param name="configProvider">The config settings for mod update checks.</param> + public ModsController(IMemoryCache cache, IOptions<ModUpdateCheckConfig> configProvider) + { + ModUpdateCheckConfig config = configProvider.Value; + + this.Cache = cache; + this.SuccessCacheMinutes = config.SuccessCacheMinutes; + } + + /// <summary>Display information for all mods.</summary> + [HttpGet] + [Route("mods")] + public async Task<ViewResult> Index() + { + return this.View("Index", await this.FetchDataAsync()); + } + + + /********* + ** Private methods + *********/ + /// <summary>Asynchronously fetch mod metadata from the wiki.</summary> + public async Task<ModListModel> FetchDataAsync() + { + return await this.Cache.GetOrCreateAsync($"{nameof(ModsController)}_mod_list", async entry => + { + WikiModList data = await new ModToolkit().GetWikiCompatibilityListAsync(); + ModListModel model = new ModListModel( + stableVersion: data.StableVersion, + betaVersion: data.BetaVersion, + mods: data + .Mods + .Select(mod => new ModModel(mod)) + .OrderBy(p => Regex.Replace(p.Name.ToLower(), "[^a-z0-9]", "")) // ignore case, spaces, and special characters when sorting + ); + + entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.SuccessCacheMinutes); + return model; + }); + } + } +} diff --git a/src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs b/src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs index 4ecf2f76..f4909155 100644 --- a/src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs +++ b/src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs @@ -1,5 +1,4 @@ using Newtonsoft.Json; -using StardewModdingAPI.Toolkit; namespace StardewModdingAPI.Web.Framework.Clients.Nexus { diff --git a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs index ce4f3cb5..bde566c0 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs @@ -16,16 +16,7 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /// <remarks>Derived from SMAPI's SemanticVersion implementation.</remarks> public string SemanticVersionRegex { get; set; } - /// <summary>The repository key for the Chucklefish mod site.</summary> - public string ChucklefishKey { get; set; } - - /// <summary>The repository key for Nexus Mods.</summary> - public string GitHubKey { get; set; } - - /// <summary>The repository key for Nexus Mods.</summary> - public string NexusKey { get; set; } - /// <summary>The web URL for the wiki compatibility list.</summary> - public string WikiCompatibilityPageUrl { get; set; } + public string CompatibilityPageUrl { get; set; } } } diff --git a/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs index 3d428015..d89a4260 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs @@ -12,7 +12,13 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /// <summary>The root URL for the log parser.</summary> public string LogParserUrl { get; set; } + /// <summary>The root URL for the mod list.</summary> + public string ModListUrl { get; set; } + /// <summary>Whether to show SMAPI beta versions on the main page, if any.</summary> - public bool EnableSmapiBeta { get; set; } + public bool BetaEnabled { get; set; } + + /// <summary>A short sentence shown under the beta download button, if any.</summary> + public string BetaBlurb { get; set; } } } diff --git a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs index 013c6c47..f9b5ba76 100644 --- a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs +++ b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs @@ -70,6 +70,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing // parse log messages LogModInfo smapiMod = new LogModInfo { Name = "SMAPI", Author = "Pathoschild", Description = "" }; + LogModInfo gameMod = new LogModInfo { Name = "game", Author = "", Description = "" }; IDictionary<string, LogModInfo> mods = new Dictionary<string, LogModInfo>(); bool inModList = false; bool inContentPackList = false; @@ -78,10 +79,23 @@ namespace StardewModdingAPI.Web.Framework.LogParsing // collect stats if (message.Level == LogLevel.Error) { - if (message.Mod == "SMAPI") - smapiMod.Errors++; - else if (mods.ContainsKey(message.Mod)) - mods[message.Mod].Errors++; + switch (message.Mod) + { + case "SMAPI": + smapiMod.Errors++; + break; + + case "game": + gameMod.Errors++; + break; + + default: + { + if (mods.ContainsKey(message.Mod)) + mods[message.Mod].Errors++; + break; + } + } } // collect SMAPI metadata @@ -151,7 +165,8 @@ namespace StardewModdingAPI.Web.Framework.LogParsing } // finalise log - log.Mods = new[] { smapiMod }.Concat(mods.Values.OrderBy(p => p.Name)).ToArray(); + gameMod.Version = log.GameVersion; + log.Mods = new[] { gameMod, smapiMod }.Concat(mods.Values.OrderBy(p => p.Name)).ToArray(); return log; } catch (LogParseException ex) diff --git a/src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs index 4a4a40cd..94256005 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; -using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; +using StardewModdingAPI.Toolkit.Framework.UpdateData; namespace StardewModdingAPI.Web.Framework.ModRepositories { @@ -10,7 +10,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories ** Accessors *********/ /// <summary>The unique key for this vendor.</summary> - public string VendorKey { get; } + public ModRepositoryKey VendorKey { get; } /********* @@ -29,7 +29,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories *********/ /// <summary>Construct an instance.</summary> /// <param name="vendorKey">The unique key for this vendor.</param> - protected RepositoryBase(string vendorKey) + protected RepositoryBase(ModRepositoryKey vendorKey) { this.VendorKey = vendorKey; } diff --git a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs index e6074a60..6e2a8814 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; +using StardewModdingAPI.Toolkit.Framework.UpdateData; using StardewModdingAPI.Web.Framework.Clients.Chucklefish; namespace StardewModdingAPI.Web.Framework.ModRepositories @@ -19,10 +19,9 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories ** Public methods *********/ /// <summary>Construct an instance.</summary> - /// <param name="vendorKey">The unique key for this vendor.</param> /// <param name="client">The underlying HTTP client.</param> - public ChucklefishRepository(string vendorKey, IChucklefishClient client) - : base(vendorKey) + public ChucklefishRepository(IChucklefishClient client) + : base(ModRepositoryKey.Chucklefish) { this.Client = client; } diff --git a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs index 1d7e4fff..7ff22d0e 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; +using StardewModdingAPI.Toolkit.Framework.UpdateData; using StardewModdingAPI.Web.Framework.Clients.GitHub; namespace StardewModdingAPI.Web.Framework.ModRepositories @@ -19,10 +19,9 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories ** Public methods *********/ /// <summary>Construct an instance.</summary> - /// <param name="vendorKey">The unique key for this vendor.</param> /// <param name="client">The underlying GitHub API client.</param> - public GitHubRepository(string vendorKey, IGitHubClient client) - : base(vendorKey) + public GitHubRepository(IGitHubClient client) + : base(ModRepositoryKey.GitHub) { this.Client = client; } diff --git a/src/SMAPI.Web/Framework/ModRepositories/IModRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/IModRepository.cs index 09c59a86..68f754ae 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/IModRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/IModRepository.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using StardewModdingAPI.Toolkit.Framework.UpdateData; namespace StardewModdingAPI.Web.Framework.ModRepositories { @@ -10,7 +11,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories ** Accessors *********/ /// <summary>The unique key for this vendor.</summary> - string VendorKey { get; } + ModRepositoryKey VendorKey { get; } /********* diff --git a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs index 4afcda10..1e242c60 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; +using StardewModdingAPI.Toolkit.Framework.UpdateData; using StardewModdingAPI.Web.Framework.Clients.Nexus; namespace StardewModdingAPI.Web.Framework.ModRepositories @@ -19,10 +19,9 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories ** Public methods *********/ /// <summary>Construct an instance.</summary> - /// <param name="vendorKey">The unique key for this vendor.</param> /// <param name="client">The underlying Nexus Mods API client.</param> - public NexusRepository(string vendorKey, INexusClient client) - : base(vendorKey) + public NexusRepository(INexusClient client) + : base(ModRepositoryKey.Nexus) { this.Client = client; } diff --git a/src/SMAPI.Web/StardewModdingAPI.Web.csproj b/src/SMAPI.Web/StardewModdingAPI.Web.csproj index 6761c7ad..9d1990d9 100644 --- a/src/SMAPI.Web/StardewModdingAPI.Web.csproj +++ b/src/SMAPI.Web/StardewModdingAPI.Web.csproj @@ -10,10 +10,10 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="HtmlAgilityPack" Version="1.8.4" /> - <PackageReference Include="Markdig" Version="0.15.0" /> - <PackageReference Include="Microsoft.AspNetCore" Version="2.1.1" /> - <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.1" /> + <PackageReference Include="HtmlAgilityPack" Version="1.8.9" /> + <PackageReference Include="Markdig" Version="0.15.4" /> + <PackageReference Include="Microsoft.AspNetCore" Version="2.1.4" /> + <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.3" /> <PackageReference Include="Microsoft.AspNetCore.Rewrite" Version="2.1.1" /> <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" /> @@ -27,6 +27,12 @@ <ProjectReference Include="..\StardewModdingAPI.Toolkit\StardewModdingAPI.Toolkit.csproj" /> </ItemGroup> <ItemGroup> + <Content Update="Views\Index\Privacy.cshtml"> + <Pack>$(IncludeRazorContentInPack)</Pack> + </Content> + <Content Update="Views\Mods\Index.cshtml"> + <Pack>$(IncludeRazorContentInPack)</Pack> + </Content> <Content Update="wwwroot\StardewModdingAPI.metadata.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs index bf3ec9a1..60a16053 100644 --- a/src/SMAPI.Web/Startup.cs +++ b/src/SMAPI.Web/Startup.cs @@ -147,14 +147,14 @@ namespace StardewModdingAPI.Web redirects.Add(new ConditionalRewriteSubdomainRule( shouldRewrite: req => req.Host.Host != "localhost" - && (req.Host.Host.StartsWith("api.") || req.Host.Host.StartsWith("log.")) + && (req.Host.Host.StartsWith("api.") || req.Host.Host.StartsWith("log.") || req.Host.Host.StartsWith("mods.")) && !req.Path.StartsWithSegments("/content") && !req.Path.StartsWithSegments("/favicon.ico") )); // shortcut redirects redirects.Add(new RedirectToUrlRule(@"^/buildmsg(?:/?(.*))$", "https://github.com/Pathoschild/SMAPI/blob/develop/docs/mod-build-config.md#$1")); - redirects.Add(new RedirectToUrlRule(@"^/compat\.?$", "https://stardewvalleywiki.com/Modding:SMAPI_compatibility")); + redirects.Add(new RedirectToUrlRule(@"^/compat\.?$", "https://mods.smapi.io")); 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")); diff --git a/src/SMAPI.Web/ViewModels/IndexModel.cs b/src/SMAPI.Web/ViewModels/IndexModel.cs index 4268c878..82c4e06f 100644 --- a/src/SMAPI.Web/ViewModels/IndexModel.cs +++ b/src/SMAPI.Web/ViewModels/IndexModel.cs @@ -12,6 +12,9 @@ namespace StardewModdingAPI.Web.ViewModels /// <summary>The latest prerelease SMAPI version (if newer than <see cref="StableVersion"/>).</summary> public IndexVersionModel BetaVersion { get; set; } + /// <summary>A short sentence shown under the beta download button, if any.</summary> + public string BetaBlurb { get; set; } + /********* ** Public methods @@ -22,10 +25,12 @@ namespace StardewModdingAPI.Web.ViewModels /// <summary>Construct an instance.</summary> /// <param name="stableVersion">The latest stable SMAPI version.</param> /// <param name="betaVersion">The latest prerelease SMAPI version (if newer than <paramref name="stableVersion"/>).</param> - internal IndexModel(IndexVersionModel stableVersion, IndexVersionModel betaVersion) + /// <param name="betaBlurb">A short sentence shown under the beta download button, if any.</param> + internal IndexModel(IndexVersionModel stableVersion, IndexVersionModel betaVersion, string betaBlurb) { this.StableVersion = stableVersion; this.BetaVersion = betaVersion; + this.BetaBlurb = betaBlurb; } } } diff --git a/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs b/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs new file mode 100644 index 00000000..85bf1e46 --- /dev/null +++ b/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs @@ -0,0 +1,40 @@ +using StardewModdingAPI.Toolkit.Framework.Clients.Wiki; + +namespace StardewModdingAPI.Web.ViewModels +{ + /// <summary>Metadata about a mod's compatibility with the latest versions of SMAPI and Stardew Valley.</summary> + public class ModCompatibilityModel + { + /********* + ** Accessors + *********/ + /// <summary>The compatibility status, as a string like <c>"Broken"</c>.</summary> + public string Status { get; set; } + + /// <summary>The human-readable summary, as an HTML block.</summary> + public string Summary { get; set; } + + /// <summary>The game or SMAPI version which broke this mod (if applicable).</summary> + public string BrokeIn { get; set; } + + /// <summary>A link to the unofficial version which fixes compatibility, if any.</summary> + public ModLinkModel UnofficialVersion { get; set; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="info">The mod metadata.</param> + public ModCompatibilityModel(WikiCompatibilityInfo info) + { + this.Status = info.Status.ToString(); + this.Status = this.Status.Substring(0, 1).ToLower() + this.Status.Substring(1); + + this.Summary = info.Summary; + this.BrokeIn = info.BrokeIn; + if (info.UnofficialVersion != null) + this.UnofficialVersion = new ModLinkModel(info.UnofficialUrl, info.UnofficialVersion.ToString()); + } + } +} diff --git a/src/SMAPI.Web/ViewModels/ModLinkModel.cs b/src/SMAPI.Web/ViewModels/ModLinkModel.cs new file mode 100644 index 00000000..97dd215c --- /dev/null +++ b/src/SMAPI.Web/ViewModels/ModLinkModel.cs @@ -0,0 +1,28 @@ +namespace StardewModdingAPI.Web.ViewModels +{ + /// <summary>Metadata about a link.</summary> + public class ModLinkModel + { + /********* + ** Accessors + *********/ + /// <summary>The URL of the linked page.</summary> + public string Url { get; set; } + + /// <summary>The suggested link text.</summary> + public string Text { get; set; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="url">The URL of the linked page.</param> + /// <param name="text">The suggested link text.</param> + public ModLinkModel(string url, string text) + { + this.Url = url; + this.Text = text; + } + } +} diff --git a/src/SMAPI.Web/ViewModels/ModListModel.cs b/src/SMAPI.Web/ViewModels/ModListModel.cs new file mode 100644 index 00000000..3b87d393 --- /dev/null +++ b/src/SMAPI.Web/ViewModels/ModListModel.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; + +namespace StardewModdingAPI.Web.ViewModels +{ + /// <summary>Metadata for the mod list page.</summary> + public class ModListModel + { + /********* + ** Accessors + *********/ + /// <summary>The current stable version of the game.</summary> + public string StableVersion { get; set; } + + /// <summary>The current beta version of the game (if any).</summary> + public string BetaVersion { get; set; } + + /// <summary>The mods to display.</summary> + public ModModel[] Mods { get; set; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="stableVersion">The current stable version of the game.</param> + /// <param name="betaVersion">The current beta version of the game (if any).</param> + /// <param name="mods">The mods to display.</param> + public ModListModel(string stableVersion, string betaVersion, IEnumerable<ModModel> mods) + { + this.StableVersion = stableVersion; + this.BetaVersion = betaVersion; + this.Mods = mods.ToArray(); + } + } +} diff --git a/src/SMAPI.Web/ViewModels/ModModel.cs b/src/SMAPI.Web/ViewModels/ModModel.cs new file mode 100644 index 00000000..0e7d2076 --- /dev/null +++ b/src/SMAPI.Web/ViewModels/ModModel.cs @@ -0,0 +1,110 @@ +using System.Collections.Generic; +using System.Linq; +using StardewModdingAPI.Toolkit.Framework.Clients.Wiki; + +namespace StardewModdingAPI.Web.ViewModels +{ + /// <summary>Metadata about a mod.</summary> + public class ModModel + { + /********* + ** Accessors + *********/ + /// <summary>The mod name.</summary> + public string Name { get; set; } + + /// <summary>The mod's alternative names, if any.</summary> + public string AlternateNames { get; set; } + + /// <summary>The mod author's name.</summary> + public string Author { get; set; } + + /// <summary>The mod author's alternative names, if any.</summary> + public string AlternateAuthors { get; set; } + + /// <summary>The URL to the mod's source code, if any.</summary> + public string SourceUrl { get; set; } + + /// <summary>The compatibility status for the stable version of the game.</summary> + public ModCompatibilityModel Compatibility { get; set; } + + /// <summary>The compatibility status for the beta version of the game.</summary> + public ModCompatibilityModel BetaCompatibility { get; set; } + + /// <summary>Links to the available mod pages.</summary> + public ModLinkModel[] ModPages { get; set; } + + /// <summary>The human-readable warnings for players about this mod.</summary> + public string[] Warnings { get; set; } + + /// <summary>A unique identifier for the mod that can be used in an anchor URL.</summary> + public string Slug { get; set; } + + /// <summary>The sites where the mod can be downloaded.</summary> + public string[] ModPageSites => this.ModPages.Select(p => p.Text).ToArray(); + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="entry">The mod metadata.</param> + public ModModel(WikiModEntry entry) + { + // basic info + this.Name = entry.Name.FirstOrDefault(); + this.AlternateNames = string.Join(", ", entry.Name.Skip(1).ToArray()); + this.Author = entry.Author.FirstOrDefault(); + this.AlternateAuthors = string.Join(", ", entry.Author.Skip(1).ToArray()); + this.SourceUrl = this.GetSourceUrl(entry); + this.Compatibility = new ModCompatibilityModel(entry.Compatibility); + this.BetaCompatibility = entry.BetaCompatibility != null ? new ModCompatibilityModel(entry.BetaCompatibility) : null; + this.ModPages = this.GetModPageUrls(entry).ToArray(); + this.Warnings = entry.Warnings; + this.Slug = entry.Anchor; + } + + + /********* + ** Private methods + *********/ + /// <summary>Get the web URL for the mod's source code repository, if any.</summary> + /// <param name="entry">The mod metadata.</param> + private string GetSourceUrl(WikiModEntry entry) + { + if (!string.IsNullOrWhiteSpace(entry.GitHubRepo)) + return $"https://github.com/{entry.GitHubRepo}"; + if (!string.IsNullOrWhiteSpace(entry.CustomSourceUrl)) + return entry.CustomSourceUrl; + return null; + } + + /// <summary>Get the web URLs for the mod pages, if any.</summary> + /// <param name="entry">The mod metadata.</param> + private IEnumerable<ModLinkModel> GetModPageUrls(WikiModEntry entry) + { + bool anyFound = false; + + // normal mod pages + if (entry.NexusID.HasValue) + { + anyFound = true; + yield return new ModLinkModel($"https://www.nexusmods.com/stardewvalley/mods/{entry.NexusID}", "Nexus"); + } + if (entry.ChucklefishID.HasValue) + { + anyFound = true; + yield return new ModLinkModel($"https://community.playstarbound.com/resources/{entry.ChucklefishID}", "Chucklefish"); + } + + // fallback + if (!anyFound && !string.IsNullOrWhiteSpace(entry.CustomUrl)) + { + anyFound = true; + yield return new ModLinkModel(entry.CustomUrl, "custom"); + } + if (!anyFound && !string.IsNullOrWhiteSpace(entry.GitHubRepo)) + yield return new ModLinkModel($"https://github.com/{entry.GitHubRepo}/releases", "GitHub"); + } + } +} diff --git a/src/SMAPI.Web/Views/Index/Index.cshtml b/src/SMAPI.Web/Views/Index/Index.cshtml index 361d01de..01874f50 100644 --- a/src/SMAPI.Web/Views/Index/Index.cshtml +++ b/src/SMAPI.Web/Views/Index/Index.cshtml @@ -1,10 +1,13 @@ +@using Microsoft.Extensions.Options +@using StardewModdingAPI.Web.Framework.ConfigModels +@inject IOptions<SiteConfig> SiteConfig +@model StardewModdingAPI.Web.ViewModels.IndexModel @{ ViewData["Title"] = "SMAPI"; } -@model StardewModdingAPI.Web.ViewModels.IndexModel @section Head { <link rel="stylesheet" href="~/Content/css/index.css?r=20180615" /> - <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js" crossorigin="anonymous"></script> <script src="~/Content/js/index.js?r=20180615"></script> } @@ -16,7 +19,7 @@ <div id="call-to-action"> <div class="cta-dropdown"> - <a href="@Model.StableVersion.DownloadUrl" class="main-cta download">Download SMAPI @Model.StableVersion.Version</a><br/> + <a href="@Model.StableVersion.DownloadUrl" class="main-cta download">Download SMAPI @Model.StableVersion.Version</a><br /> <div class="dropdown-content"> <a href="https://www.nexusmods.com/stardewvalley/mods/2400"><img src="Content/images/nexus-icon.png" /> Download from Nexus</a> <a href="@Model.StableVersion.DownloadUrl"><img src="Content/images/direct-download-icon.png" /> Direct download</a> @@ -26,20 +29,29 @@ @if (Model.BetaVersion != null) { <div class="cta-dropdown secondary-cta-dropdown"> - <a href="@Model.BetaVersion.DownloadUrl" class="secondary-cta download">Download SMAPI @Model.BetaVersion.Version<br/><small>for Stardew Valley 1.3 beta</small></a><br/> + <a href="@Model.BetaVersion.DownloadUrl" class="secondary-cta download"> + Download SMAPI @Model.BetaVersion.Version + @if (!string.IsNullOrWhiteSpace(Model.BetaBlurb)) + { + <br /><small>@Model.BetaBlurb</small> + } + </a><br /> <div class="dropdown-content"> <a href="https://www.nexusmods.com/stardewvalley/mods/2400"><img src="Content/images/nexus-icon.png" /> Download from Nexus</a> <a href="@Model.BetaVersion.DownloadUrl"><img src="Content/images/direct-download-icon.png" /> Direct download</a> </div> </div><br /> } - <a href="https://stardewvalleywiki.com/Modding:Player_Guide" class="secondary-cta">Player guide</a><br /> + <div><a href="https://stardewvalleywiki.com/Modding:Player_Guide" class="secondary-cta">Player guide</a></div> + <div class="sublinks"> + <a href="https://github.com/Pathoschild/SMAPI">source code</a> | <a href="@(new UriBuilder(SiteConfig.Value.RootUrl) { Path = "privacy" }.Uri)">privacy</a> + </div> <img id="pufferchick" src="Content/images/pufferchick.png" /> </div> <h2 id="help">Get help</h2> <ul> - <li><a href="https://stardewvalleywiki.com/Modding:SMAPI_compatibility">Mod compatibility list</a></li> + <li><a href="@SiteConfig.Value.ModListUrl">Mod compatibility list</a></li> <li>Get help <a href="https://stardewvalleywiki.com/Modding:Community#Discord">on Discord</a> or <a href="https://community.playstarbound.com/threads/smapi-stardew-modding-api.108375/">in the forums</a></li> </ul> @@ -49,7 +61,7 @@ <div class="github-description"> @Html.Raw(Markdig.Markdown.ToHtml(Model.StableVersion.Description)) </div> - <p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="https://stardewvalleywiki.com/Modding:SMAPI_compatibility">mod compatibility list</a> for more info.</p> + <p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="@SiteConfig.Value.ModListUrl">mod compatibility list</a> for more info.</p> } else { @@ -58,13 +70,13 @@ else <div class="github-description"> @Html.Raw(Markdig.Markdown.ToHtml(Model.StableVersion.Description)) </div> - <p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="https://stardewvalleywiki.com/Modding:SMAPI_compatibility">mod compatibility list</a> for more info.</p> + <p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="@SiteConfig.Value.ModListUrl">mod compatibility list</a> for more info.</p> <h3>SMAPI @Model.BetaVersion.Version?</h3> <div class="github-description"> @Html.Raw(Markdig.Markdown.ToHtml(Model.BetaVersion.Description)) </div> - <p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="https://stardewvalleywiki.com/Modding:SMAPI_compatibility">mod compatibility list</a> for more info.</p> + <p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="@SiteConfig.Value.ModListUrl">mod compatibility list</a> for more info.</p> } <h2 id="donate">Donate to support SMAPI ♥</h2> @@ -87,13 +99,13 @@ else <p> Special thanks to + AbroadKew, acerbicon, <a href="https://www.nexusmods.com/stardewvalley/users/31393530">ChefRude</a>, cheesysteak, hawkfalcon, jwdred, - KNakamura, - Kono Tyran, + <a href="https://www.nexusmods.com/users/12252523">Karmylla</a>, Pucklynn, Robby LaFarge, and a few anonymous users for their ongoing support; you're awesome! 🏅 diff --git a/src/SMAPI.Web/Views/Index/Privacy.cshtml b/src/SMAPI.Web/Views/Index/Privacy.cshtml new file mode 100644 index 00000000..ca99eef6 --- /dev/null +++ b/src/SMAPI.Web/Views/Index/Privacy.cshtml @@ -0,0 +1,43 @@ +@using Microsoft.Extensions.Options +@using StardewModdingAPI.Web.Framework.ConfigModels +@inject IOptions<SiteConfig> SiteConfig +@{ + ViewData["Title"] = "SMAPI privacy notes"; +} +@section Head { + <link rel="stylesheet" href="~/Content/css/privacy.css" /> +} + +← <a href="@SiteConfig.Value.RootUrl">back to SMAPI page</a> + +<p>SMAPI is an <a href="https://github.com/Pathoschild/SMAPI">open-source</a> and non-profit project. Your privacy is important, so this page explains what information SMAPI uses and transmits. <strong>This page is informational only, it's not a legal document.</strong></p> + +<h2>Principles</h2> +<ol> + <li>SMAPI collects the minimum information needed to enable its features (see below).</li> + <li>SMAPI does not collect telemetry, analytics, etc.</li> + <li>SMAPI will never sell your information.</li> +</ol> + +<h2>Data collected and transmitted</h2> +<h3 id="web-logging">Web logging</h3> +<p>This website and SMAPI's web API are hosted by Amazon Web Services. Their servers may automatically collect diagnostics like your IP address, but this information is not visible to SMAPI's web application or developers. For more information, see the <a href="https://aws.amazon.com/privacy/">Amazon Privacy Notice</a>.</p> + +<h3>Update checks</h3> +<p>SMAPI notifies you when there's a new version of SMAPI or your mods available. To do so, it sends your SMAPI and mod versions to its web API. No personal information is stored by the web application, but see <em><a href="#web-logging">web logging</a></em>.</p> + +<p>You can disable update checks, and no information will be transmitted to the web API. To do so:</p> +<ol> + <li><a href="https://stardewvalleywiki.com/Modding:Game_folder">find your game folder</a>;</li> + <li>open the <code>smapi-internal/StardewModdingAPI.config.json</code> file in a text editor;</li> + <li>change <code>"CheckForUpdates": true</code> to <code>"CheckForUpdates": false</code>.</li> +</ol> + +<h3>Log parser</h3> +<p>The <a href="https://log.smapi.io/">log parser page</a> lets you store a log file for analysis and sharing. The log data is stored indefinitely in an obfuscated form as unlisted pastes in <a href="https://pastebin.com/">Pastebin</a>. No personal information is stored by the log parser beyond what you choose to upload, but see <em><a href="#web-logging">web logging</a></em> and the <a href="https://pastebin.com/doc_privacy_statement">Pastebin Privacy Statement</a>.</p> + +<h3>Multiplayer sync</h3> +<p>As part of its multiplayer API, SMAPI transmits basic context to players you connect to (mainly your OS, SMAPI version, game version, and installed mods). This is used to enable multiplayer features like inter-mod messages, compatibility checks, etc. Although this information is normally hidden from players, it may be visible due to mods or configuration changes.</p> + +<h3>Custom mods</h3> +<p><strong>Mods may collect and transmit any information. Mods (except those provided as part of the SMAPI download) are not covered by this page. Install third-party mods at your own risk.</strong></p> diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index e735e8f3..58830d64 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -18,8 +18,8 @@ <meta name="robots" content="noindex" /> } <link rel="stylesheet" href="~/Content/css/log-parser.css?r=20180627" /> - <script src="https://cdn.jsdelivr.net/npm/vue"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js" crossorigin="anonymous"></script> <script src="~/Content/js/log-parser.js?r=20180627"></script> <script> $(function() { @@ -84,7 +84,7 @@ else if (Model.ParsedLog?.IsValid == true) On Mac: <ol> <li>Open the Finder app.</li> - <li>Click <em>Go</em> at the top, then <em>Enter Location</em>.</li> + <li>Click <em>Go</em> at the top, then <em>Go to Folder</em>.</li> <li>Enter this exact text: <pre>~/.config/StardewValley/ErrorLogs</pre></li> <li>The log file is <code>SMAPI-crash.txt</code> if it exists, otherwise <code>SMAPI-latest.txt</code>.</li> </ol> @@ -145,14 +145,14 @@ else if (Model.ParsedLog?.IsValid == true) @if (!Model.ShowRaw) { <span class="notice txt"><i>click any mod to filter</i></span> - <span class="notice btn txt" v-on:click="showAllMods" v-show="stats.modsHidden > 0">show all</span> - <span class="notice btn txt" v-on:click="hideAllMods" v-show="stats.modsShown > 0 && stats.modsHidden > 0">hide all</span> + <span class="notice btn txt" v-on:click="showAllMods" v-bind:class="{ invisible: !anyModsHidden }">show all</span> + <span class="notice btn txt" v-on:click="hideAllMods" v-bind:class="{ invisible: !anyModsShown || !anyModsHidden }">hide all</span> } </caption> @foreach (var mod in Model.ParsedLog.Mods.Where(p => p.ContentPackFor == null)) { <tr v-on:click="toggleMod('@Model.GetSlug(mod.Name)')" class="mod-entry" v-bind:class="{ hidden: !showMods['@Model.GetSlug(mod.Name)'] }"> - <td><input type="checkbox" v-bind:checked="showMods['@Model.GetSlug(mod.Name)']" v-show="anyModsHidden" /></td> + <td><input type="checkbox" v-bind:checked="showMods['@Model.GetSlug(mod.Name)']" v-bind:class="{ invisible: !anyModsHidden }" /></td> <td v-pre> <strong>@mod.Name</strong> @mod.Version @if (contentPacks != null && contentPacks.TryGetValue(mod.Name, out LogModInfo[] contentPackList)) @@ -165,7 +165,18 @@ else if (Model.ParsedLog?.IsValid == true) </div> } </td> - <td v-pre>@mod.Author</td> + <td v-pre> + @mod.Author + @if (contentPacks != null && contentPacks.TryGetValue(mod.Name, out contentPackList)) + { + <div class="content-packs"> + @foreach (var contentPack in contentPackList) + { + <text>+ @contentPack.Author</text><br /> + } + </div> + } + </td> @if (mod.Errors == 0) { <td v-pre class="color-green">no errors</td> diff --git a/src/SMAPI.Web/Views/Mods/Index.cshtml b/src/SMAPI.Web/Views/Mods/Index.cshtml new file mode 100644 index 00000000..372d6706 --- /dev/null +++ b/src/SMAPI.Web/Views/Mods/Index.cshtml @@ -0,0 +1,94 @@ +@using Newtonsoft.Json +@model StardewModdingAPI.Web.ViewModels.ModListModel +@{ + ViewData["Title"] = "SMAPI mod compatibility"; +} +@section Head { + <link rel="stylesheet" href="~/Content/css/mods.css?r=20181109" /> + <script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/npm/tablesorter@2.31.0/dist/js/jquery.tablesorter.combined.min.js" crossorigin="anonymous"></script> + <script src="~/Content/js/mods.js?r=20181109"></script> + <script> + $(function() { + var data = @Json.Serialize(Model.Mods, new JsonSerializerSettings { Formatting = Formatting.None }); + smapi.modList(data); + }); + </script> +} + +<div id="intro"> + <p>This page lists all known SMAPI mods, whether they're compatible with the latest versions of Stardew Valley and SMAPI, and how to fix broken mods if possible. The list is updated every few days. (You can help <a href="https://stardewvalleywiki.com/Modding:SMAPI_compatibility">edit this list</a>!)</p> + + <p>If a mod doesn't work after following the instructions below, check <a href="https://stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting">the troubleshooting guide</a> or <a href="https://stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting#Ask_for_help">ask for help</a>.</p> + + @if (Model.BetaVersion != null) + { + <p id="beta-blurb"><strong>Note:</strong> "SDV beta only" means Stardew Valley @Model.BetaVersion-beta; if you didn't opt in to the beta, you have the stable version and can ignore that line. If a mod doesn't have a "SDV beta only" line, the compatibility applies to both versions of the game.</p> + } +</div> + +<div id="app"> + <div id="options"> + <div> + <label for="search-box">Search: </label> + <input type="text" id="search-box" v-model="search" v-on:input="applyFilters" /> + </div> + <div id="filter-area"> + <input type="checkbox" id="show-advanced" v-model="showAdvanced" /> + <label for="show-advanced">show detailed 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> + </div> + </div> + </div> + </div> + <div id="mod-count" v-show="showAdvanced">{{visibleCount}} mods shown.</div> + <table class="wikitable" id="mod-list"> + <thead> + <tr> + <th>mod name</th> + <th>links</th> + <th>author</th> + <th>compatibility</th> + <th v-show="showAdvanced">broke in</th> + <th v-show="showAdvanced">code</th> + <th> </th> + </tr> + </thead> + <tbody> + <tr v-for="mod in mods" :key="mod.Name" v-bind:id="mod.Slug" :key="mod.Slug" v-bind:data-status="mod.BetaCompatibility != null ? mod.BetaCompatibility.Status : mod.Compatibility.Status" v-show="mod.Visible"> + <td> + {{mod.Name}} + <small class="mod-alt-names" v-if="mod.AlternateNames">(aka {{mod.AlternateNames}})</small> + </td> + <td class="mod-page-links"> + <span v-for="(link, i) in mod.ModPages"> + <a v-bind:href="link.Url">{{link.Text}}</a>{{i < mod.ModPages.length - 1 ? ', ' : ''}} + </span> + </td> + <td> + {{mod.Author}} + <small class="mod-alt-authors" v-if="mod.AlternateAuthors">(aka {{mod.AlternateAuthors}})</small> + </td> + <td> + <div v-html="mod.Compatibility.Summary"></div> + <div v-if="mod.BetaCompatibility"> + <strong v-if="mod.BetaCompatibility">SDV beta only:</strong> + <span v-html="mod.BetaCompatibility.Summary"></span> + </div> + <div v-for="(warning, i) in mod.Warnings">⚠ {{warning}}</div> + </td> + <td class="mod-broke-in" v-html="mod.BetaCompatibility ? mod.BetaCompatibility.BrokeIn : mod.Compatibility.BrokeIn" v-show="showAdvanced"></td> + <td v-show="showAdvanced"> + <span v-if="mod.SourceUrl"><a v-bind:href="mod.SourceUrl">source</a></span> + <span v-else class="mod-closed-source">no source</span> + </td> + <td> + <small><a v-bind:href="'#' + mod.Slug">#</a></small> + </td> + </tr> + </tbody> + </table> +</div> diff --git a/src/SMAPI.Web/Views/Shared/_Layout.cshtml b/src/SMAPI.Web/Views/Shared/_Layout.cshtml index 29da9100..4c602b29 100644 --- a/src/SMAPI.Web/Views/Shared/_Layout.cshtml +++ b/src/SMAPI.Web/Views/Shared/_Layout.cshtml @@ -16,6 +16,7 @@ <h4>SMAPI</h4> <ul> <li><a href="@SiteConfig.Value.RootUrl">About SMAPI</a></li> + <li><a href="@SiteConfig.Value.ModListUrl">Mod compatibility</a></li> <li><a href="@SiteConfig.Value.LogParserUrl">Log parser</a></li> <li><a href="https://stardewvalleywiki.com/Modding:Index">Docs</a></li> </ul> diff --git a/src/SMAPI.Web/appsettings.Development.json b/src/SMAPI.Web/appsettings.Development.json index 67bb7748..db90a3de 100644 --- a/src/SMAPI.Web/appsettings.Development.json +++ b/src/SMAPI.Web/appsettings.Development.json @@ -19,8 +19,10 @@ "Site": { "RootUrl": "http://localhost:59482/", + "ModListUrl": "http://localhost:59482/mods/", "LogParserUrl": "http://localhost:59482/log/", - "EnableSmapiBeta": false + "BetaEnabled": false, + "BetaBlurb": null }, "ApiClients": { diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index 9e3270ae..aba8c448 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -16,8 +16,10 @@ "Site": { "RootUrl": null, // see top note + "ModListUrl": null, // see top note "LogParserUrl": null, // see top note - "EnableSmapiBeta": null // see top note + "BetaEnabled": null, // see top note + "BetaBlurb": null // see top note }, "ApiClients": { @@ -46,11 +48,6 @@ "SuccessCacheMinutes": 60, "ErrorCacheMinutes": 5, "SemanticVersionRegex": "^(?>(?<major>0|[1-9]\\d*))\\.(?>(?<minor>0|[1-9]\\d*))(?>(?:\\.(?<patch>0|[1-9]\\d*))?)(?:-(?<prerelease>(?>[a-z0-9]+[\\-\\.]?)+))?$", - - "ChucklefishKey": "Chucklefish", - "GitHubKey": "GitHub", - "NexusKey": "Nexus", - - "WikiCompatibilityPageUrl": "https://smapi.io/compat" + "CompatibilityPageUrl": "https://mods.smapi.io" } } diff --git a/src/SMAPI.Web/wwwroot/Content/css/index.css b/src/SMAPI.Web/wwwroot/Content/css/index.css index 514e1a5c..979af4af 100644 --- a/src/SMAPI.Web/wwwroot/Content/css/index.css +++ b/src/SMAPI.Web/wwwroot/Content/css/index.css @@ -93,6 +93,11 @@ h1 { display: block; } +.sublinks { + font-size: 0.9em; + margin-bottom: 1em; +} + /********* ** Subsections *********/ diff --git a/src/SMAPI.Web/wwwroot/Content/css/log-parser.css b/src/SMAPI.Web/wwwroot/Content/css/log-parser.css index 1fcd1bff..2f3dd0a1 100644 --- a/src/SMAPI.Web/wwwroot/Content/css/log-parser.css +++ b/src/SMAPI.Web/wwwroot/Content/css/log-parser.css @@ -63,6 +63,10 @@ table#metadata, table#mods { box-shadow: 1px 1px 1px 1px #dddddd; } +.invisible { + visibility: hidden; +} + #mods { min-width: 400px; } diff --git a/src/SMAPI.Web/wwwroot/Content/css/mods.css b/src/SMAPI.Web/wwwroot/Content/css/mods.css new file mode 100644 index 00000000..730bfc2e --- /dev/null +++ b/src/SMAPI.Web/wwwroot/Content/css/mods.css @@ -0,0 +1,135 @@ +/********* +** Intro +*********/ +#content { + max-width: calc(100% - 2em); /* allow for wider table if room available */ +} + +#intro { + width: 50em; +} + +#beta-blurb { + margin-bottom: 2em; + padding: 1em; + border: 3px solid darkgreen; +} + +table.wikitable { + background-color:#f8f9fa; + color:#222; + border:1px solid #a2a9b1; + border-collapse:collapse +} + +table.wikitable > tr > th, +table.wikitable > tr > td, +table.wikitable > * > tr > th, +table.wikitable > * > tr > td { + border:1px solid #a2a9b1; + padding:0.2em 0.4em +} + +table.wikitable > tr > th, +table.wikitable > * > tr > th { + background-color:#eaecf0; +} + +table.wikitable > caption { + font-weight:bold +} + +#options { + margin-bottom: 1em; +} + +#options #filter-area { + opacity: 0.7; +} + +#options #filters { + margin-left: 2em; + padding-left: 0.5em; + border-left: 2px solid gray; +} + +#options #filters span { + padding: 2px; + margin: 2px; + display: inline-block; + border-radius: 3px; + color: #000; + border-color: #880000; + background-color: #fcc; + font-size: 0.9em; +} + +#options #filters span.active { + background: #cfc; +} + +#mod-count { + font-size: 0.8em; + opacity: 0.5; +} + +#mod-list { + font-size: 0.9em; +} + +#mod-list th.header { + background-repeat: no-repeat; + background-position: center right; + cursor: pointer; + background-image: url(data:image/gif;base64,R0lGODlhFQAJAIABAAAAAAAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjAxODAxMTc0MDcyMDY4MTE4OEM2REYyN0ExMDhBNDJFIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjdCNTAyODcwMEY4NjExRTBBMzkyQzAyM0E1RDk3RDc3IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjdCNTAyODZGMEY4NjExRTBBMzkyQzAyM0E1RDk3RDc3IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzUgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDE4MDExNzQwNzIwNjgxMTg4QzZERjI3QTEwOEE0MkUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDE4MDExNzQwNzIwNjgxMTg4QzZERjI3QTEwOEE0MkUiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQBAAABACwAAAAAFQAJAAACF4yPgMsJ2mJ4VDKKrd4GVz5lYPeMiVUAADs=); + padding-right: 1.5em; +} + +#mod-list th.headerSortUp { + background-image: url(data:image/gif;base64,R0lGODlhFQAEAIABAAAAAAAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjAzODAxMTc0MDcyMDY4MTE4OEM2REYyN0ExMDhBNDJFIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjdCNTAyODc0MEY4NjExRTBBMzkyQzAyM0E1RDk3RDc3IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjdCNTAyODczMEY4NjExRTBBMzkyQzAyM0E1RDk3RDc3IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzUgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDM4MDExNzQwNzIwNjgxMTg4QzZERjI3QTEwOEE0MkUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDM4MDExNzQwNzIwNjgxMTg4QzZERjI3QTEwOEE0MkUiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQBAAABACwAAAAAFQAEAAACDYwfoAvoz9qbZ9FrJC0AOw==); +} + +#mod-list th.headerSortDown { + background-image: url(data:image/gif;base64,R0lGODlhFQAEAIABAAAAAAAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjAyODAxMTc0MDcyMDY4MTE4OEM2REYyN0ExMDhBNDJFIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjhFNzNGQjI3MEY4NjExRTBBMzkyQzAyM0E1RDk3RDc3IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjhFNzNGQjI2MEY4NjExRTBBMzkyQzAyM0E1RDk3RDc3IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzUgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDI4MDExNzQwNzIwNjgxMTg4QzZERjI3QTEwOEE0MkUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDI4MDExNzQwNzIwNjgxMTg4QzZERjI3QTEwOEE0MkUiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQBAAABACwAAAAAFQAEAAACDYyPAcmtsJyDVDKKWQEAOw==); +} + +#mod-list .mod-page-links, +#mod-list .mod-broke-in { + font-size: 0.9em; +} + +#mod-list .mod-alt-authors, +#mod-list .mod-alt-names { + font-size: 0.8em; +} + +#mod-list .mod-alt-authors, +#mod-list .mod-alt-names { + display: block; +} + +#mod-list tr[data-status="ok"], +#mod-list tr[data-status="optional"] { + background: #BFB; +} + +#mod-list tr[data-status="workaround"], +#mod-list tr[data-status="unofficial"] { + background: #FFFEC6; +} + +#mod-list tr[data-status="broken"] { + background: #FBB; +} + +#mod-list tr[data-status="obsolete"], +#mod-list tr[data-status="abandoned"] { + background: #BBB; + opacity: 0.7; +} + +#mod-list .mod-closed-source { + color: red; + font-size: 0.8em; + opacity: 0.5; +} diff --git a/src/SMAPI.Web/wwwroot/Content/css/privacy.css b/src/SMAPI.Web/wwwroot/Content/css/privacy.css new file mode 100644 index 00000000..94bc68a9 --- /dev/null +++ b/src/SMAPI.Web/wwwroot/Content/css/privacy.css @@ -0,0 +1,3 @@ +h3 { + border: 0; +} diff --git a/src/SMAPI.Web/wwwroot/Content/js/mods.js b/src/SMAPI.Web/wwwroot/Content/js/mods.js new file mode 100644 index 00000000..2cff551f --- /dev/null +++ b/src/SMAPI.Web/wwwroot/Content/js/mods.js @@ -0,0 +1,205 @@ +/* globals $ */ + +var smapi = smapi || {}; +var app; +smapi.modList = function (mods) { + // init data + var data = { + mods: mods, + visibleCount: mods.length, + showAdvanced: false, + filters: { + source: { + open: { + label: "open", + id: "show-open-source", + value: true + }, + closed: { + label: "closed", + id: "show-closed-source", + value: true + } + }, + status: { + ok: { + label: "ok", + id: "show-status-ok", + value: true + }, + optional: { + label: "optional", + id: "show-status-optional", + value: true + }, + unofficial: { + label: "unofficial", + id: "show-status-unofficial", + value: true + }, + workaround: { + label: "workaround", + id: "show-status-workaround", + value: true + }, + broken: { + label: "broken", + id: "show-status-broken", + value: true + }, + abandoned: { + label: "abandoned", + id: "show-status-abandoned", + value: true + }, + obsolete: { + label: "obsolete", + id: "show-status-obsolete", + value: true + } + }, + download: { + chucklefish: { + label: "Chucklefish", + id: "show-chucklefish", + value: true + }, + nexus: { + label: "Nexus", + id: "show-nexus", + value: true + }, + custom: { + label: "custom", + id: "show-custom", + value: true + } + } + }, + search: "" + }; + for (var i = 0; i < data.mods.length; i++) { + var mod = mods[i]; + + // set initial visibility + mod.Visible = true; + + // concatenate searchable text + mod.SearchableText = [mod.Name, mod.AlternateNames, mod.Author, mod.AlternateAuthors, mod.Compatibility.Summary, mod.BrokeIn]; + if (mod.Compatibility.UnofficialVersion) + mod.SearchableText.push(mod.Compatibility.UnofficialVersion); + if (mod.BetaCompatibility) { + mod.SearchableText.push(mod.BetaCompatibility.Summary); + if (mod.BetaCompatibility.UnofficialVersion) + mod.SearchableText.push(mod.BetaCompatibility.UnofficialVersion); + } + for (var p = 0; p < mod.ModPages; p++) + mod.SearchableField.push(mod.ModPages[p].Text); + mod.SearchableText = mod.SearchableText.join(" ").toLowerCase(); + } + + // init app + app = new Vue({ + el: "#app", + data: data, + mounted: function() { + // enable table sorting + $("#mod-list").tablesorter({ + cssHeader: "header", + cssAsc: "headerSortUp", + cssDesc: "headerSortDown" + }); + + // put focus in textbox for quick search + if (!location.hash) + $("#search-box").focus(); + + // jump to anchor (since table is added after page load) + if (location.hash) { + var row = $(location.hash).get(0); + if (row) + row.scrollIntoView(); + } + }, + methods: { + /** + * Update the visibility of all mods based on the current search text and filters. + */ + applyFilters: function () { + // get search terms + var words = data.search.toLowerCase().split(" "); + + // apply criteria + data.visibleCount = data.mods.length; + for (var i = 0; i < data.mods.length; i++) { + var mod = data.mods[i]; + mod.Visible = true; + + // check filters + if (!this.matchesFilters(mod)) { + mod.Visible = false; + data.visibleCount--; + continue; + } + + // check search terms (all search words should match) + if (words.length) { + for (var w = 0; w < words.length; w++) { + if (mod.SearchableText.indexOf(words[w]) === -1) { + mod.Visible = false; + data.visibleCount--; + break; + } + } + } + } + }, + + + /** + * Get whether a mod matches the current filters. + * @param {object} mod The mod to check. + * @returns {bool} Whether the mod matches the filters. + */ + matchesFilters: function(mod) { + var filters = data.filters; + + // check source + if (!filters.source.open.value && mod.SourceUrl) + return false; + if (!filters.source.closed.value && !mod.SourceUrl) + return false; + + // check status + var status = (mod.BetaCompatibility || mod.Compatibility).Status; + if (filters.status[status] && !filters.status[status].value) + return false; + + // check download sites + var ignoreSites = []; + + if (!filters.download.chucklefish.value) + ignoreSites.push("Chucklefish"); + if (!filters.download.nexus.value) + ignoreSites.push("Nexus"); + if (!filters.download.custom.value) + ignoreSites.push("custom"); + + if (ignoreSites.length) { + var anyLeft = false; + for (var i = 0; i < mod.ModPageSites.length; i++) { + if (ignoreSites.indexOf(mod.ModPageSites[i]) === -1) { + anyLeft = true; + break; + } + } + + if (!anyLeft) + return false; + } + + return true; + } + } + }); +}; diff --git a/src/SMAPI.Web/wwwroot/StardewModdingAPI.metadata.json b/src/SMAPI.Web/wwwroot/StardewModdingAPI.metadata.json index c95abe75..b16cb99f 100644 --- a/src/SMAPI.Web/wwwroot/StardewModdingAPI.metadata.json +++ b/src/SMAPI.Web/wwwroot/StardewModdingAPI.metadata.json @@ -68,51 +68,10 @@ /********* ** Mods *********/ - "AccessChestAnywhere": { - "ID": "AccessChestAnywhere", - "MapLocalVersions": { "1.1-1078": "1.1" }, - "Default | UpdateKey": "Nexus:257", - "~1.1 | Status": "AssumeBroken" - }, - "Adjust Artisan Prices": { "ID": "ThatNorthernMonkey.AdjustArtisanPrices", "FormerIDs": "1e36d4ca-c7ef-4dfb-9927-d27a6c3c8bdc", // changed in 0.0.2-pathoschild-update - "MapRemoteVersions": { "0.01": "0.0.1" }, - "Default | UpdateKey": "Chucklefish:3532" - }, - - "Adjust Monster": { - "ID": "mmanlapat.AdjustMonster", - "Default | UpdateKey": "Nexus:1161" - }, - - "Advanced Location Loader": { - "ID": "Entoarox.AdvancedLocationLoader", - "~1.3.7 | UpdateKey": "Chucklefish:3619" // only enable update checks up to 1.3.7 by request (has its own update-check feature) - }, - - "Adventure Shop Inventory": { - "ID": "HammurabiAdventureShopInventory", - "Default | UpdateKey": "Chucklefish:4608" - }, - - "AgingMod": { - "ID": "skn.AgingMod", - "Default | UpdateKey": "Nexus:1129", - "~1.0 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "All Crops All Seasons": { - "ID": "cantorsdust.AllCropsAllSeasons", - "FormerIDs": "29ee8246-d67b-4242-a340-35a9ae0d5dd7 | community.AllCropsAllSeasons", // changed in 1.3 and 1.5 - "Default | UpdateKey": "Nexus:170" - }, - - "All Professions": { - "ID": "cantorsdust.AllProfessions", - "FormerIDs": "8c37b1a7-4bfb-4916-9d8a-9533e6363ea3 | community.AllProfessions", // changed in 1.2 and 1.3.1 - "Default | UpdateKey": "Nexus:174" + "MapRemoteVersions": { "0.01": "0.0.1" } }, "Almighty Farming Tool": { @@ -120,14 +79,7 @@ "MapRemoteVersions": { "1.21": "1.2.1", "1.22-unofficial.3.mizzion": "1.2.2-unofficial.3.mizzion" - }, - "Default | UpdateKey": "Nexus:439" - }, - - "Animal Husbandry": { - "ID": "DIGUS.ANIMALHUSBANDRYMOD", - "FormerIDs": "DIGUS.BUTCHER", // changed in 2.0.1 - "Default | UpdateKey": "Nexus:1538" + } }, "Animal Mood Fix": { @@ -136,163 +88,46 @@ "~ | StatusReasonPhrase": "the animal mood bugs were fixed in Stardew Valley 1.2." }, - "Animal Sitter": { - "ID": "jwdred.AnimalSitter", - "Default | UpdateKey": "Nexus:581", - "~1.0.8 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - "Arcade Pong": { "ID": "Platonymous.ArcadePong", "~1.0.2 | Status": "AssumeBroken" // broke in SMAPI 2.6-beta.16 due to reflection into SMAPI internals }, - "A Tapper's Dream": { - "ID": "ddde5195-8f85-4061-90cc-0d4fd5459358", - "Default | UpdateKey": "Nexus:260" - }, - - "Auto Animal Doors": { - "ID": "AaronTaggart.AutoAnimalDoors", - "Default | UpdateKey": "Nexus:1019" - }, - - "Auto-Eat": { - "ID": "Permamiss.AutoEat", - "FormerIDs": "BALANCEMOD_AutoEat", // changed in 1.1.1 - "Default | UpdateKey": "Nexus:643" - }, - - "AutoFish": { - "ID": "WhiteMind.AF", - "Default | UpdateKey": "Nexus:1895" - }, - - "AutoGate": { - "ID": "AutoGate", - "Default | UpdateKey": "Nexus:820" - }, - - "Automate": { - "ID": "Pathoschild.Automate", - "Default | UpdateKey": "Nexus:1063", - "~1.10-beta.7 | Status": "AssumeBroken" // broke in SDV 1.3.20 - }, - "Automated Doors": { "ID": "azah.automated-doors", "FormerIDs": "1abcfa07-2cf4-4dc3-a6e9-6068b642112b", // changed in 1.4.1 "Default | UpdateKey": "GitHub:azah/AutomatedDoors" // added in 1.4.2 }, - "AutoSpeed": { - "ID": "Omegasis.AutoSpeed", - "Default | UpdateKey": "Nexus:443" // added in 1.4.1 - }, - "Basic Sprinklers Improved": { "ID": "lrsk_sdvm_bsi.0117171308", - "MapRemoteVersions": { "1.0.2": "1.0.1-release" }, // manifest not updated - "Default | UpdateKey": "Nexus:833" - }, - - "Better Hay": { - "ID": "cat.betterhay", - "Default | UpdateKey": "Nexus:1430" - }, - - "Better Quality More Seasons": { - "ID": "SB_BQMS", - "Default | UpdateKey": "Nexus:935" - }, - - "Better Quarry": { - "ID": "BetterQuarry", - "Default | UpdateKey": "Nexus:771" - }, - - "Better Ranching": { - "ID": "BetterRanching", - "Default | UpdateKey": "Nexus:859" + "MapRemoteVersions": { "1.0.2": "1.0.1-release" } // manifest not updated }, "Better Shipping Box": { "ID": "Kithio:BetterShippingBox", - "MapLocalVersions": { "1.0.1": "1.0.2" }, - "Default | UpdateKey": "Chucklefish:4302" - }, - - "Better Sprinklers": { - "ID": "Speeder.BetterSprinklers", - "FormerIDs": "SPDSprinklersMod", // changed in 2.3 - "Default | UpdateKey": "Nexus:41" - }, - - "Billboard Anywhere": { - "ID": "Omegasis.BillboardAnywhere", - "Default | UpdateKey": "Nexus:492" // added in 1.4.1 + "MapLocalVersions": { "1.0.1": "1.0.2" } }, "Birthday Mail": { "ID": "KathrynHazuka.BirthdayMail", "FormerIDs": "005e02dc-d900-425c-9c68-1ff55c5a295d", // changed in 1.2.3-pathoschild-update - "Default | UpdateKey": "Nexus:276", "MapRemoteVersions": { "1.3.1": "1.3" } // manifest not updated }, - "Breed Like Rabbits": { - "ID": "dycedarger.breedlikerabbits", - "Default | UpdateKey": "Nexus:948" - }, - - "Build Endurance": { - "ID": "Omegasis.BuildEndurance", - "Default | UpdateKey": "Nexus:445" // added in 1.4.1 - }, - - "Build Health": { - "ID": "Omegasis.BuildHealth", - "Default | UpdateKey": "Nexus:446" // added in 1.4.1 - }, - - "Buy Cooking Recipes": { - "ID": "Denifia.BuyRecipes", - "Default | UpdateKey": "Nexus:1126" // added in 1.0.1 (2017-10-04) - }, - - "Buy Back Collectables": { - "ID": "Omegasis.BuyBackCollectables", - "FormerIDs": "BuyBackCollectables", // changed in 1.4 - "Default | UpdateKey": "Nexus:507" // added in 1.4.1 - }, - - "Carry Chest": { - "ID": "spacechase0.CarryChest", - "Default | UpdateKey": "Nexus:1333" + "BJS Night Sounds": { + "ID": "BunnyJumps.BJSNightSounds", + "~1.0.0 | Status": "AssumeBroken" // runtime errors with Harmony 1.2.0.1 in SMAPI 2.8+ }, "Casks Anywhere": { "ID": "CasksAnywhere", - "MapLocalVersions": { "1.1-alpha": "1.1" }, - "Default | UpdateKey": "Nexus:878" - }, - - "Categorize Chests": { - "ID": "CategorizeChests", - "Default | UpdateKey": "Nexus:1300", - "~1.4.3-unofficial.2.mizzion | Status": "AssumeBroken" // broke in SMAPI 2.6-beta.18 (in-game errors) + "MapLocalVersions": { "1.1-alpha": "1.1" } }, "Chefs Closet": { "ID": "Duder.ChefsCloset", - "MapLocalVersions": { "1.3-1": "1.3" }, - "Default | UpdateKey": "Nexus:1030" - }, - - "Chest Label System": { - "ID": "Speeder.ChestLabel", - "FormerIDs": "SPDChestLabel", // changed in 1.5.1-pathoschild-update - "Default | UpdateKey": "Nexus:242" + "MapLocalVersions": { "1.3-1": "1.3" } }, "Chest Pooling": { @@ -300,401 +135,81 @@ "Default | UpdateKey": "GitHub:mralbobo/stardew-chest-pooling" }, - "Chests Anywhere": { - "ID": "Pathoschild.ChestsAnywhere", - "FormerIDs": "ChestsAnywhere", // changed in 1.9 - "Default | UpdateKey": "Nexus:518", - "~1.12.4 | Status": "AssumeBroken" // broke in SDV 1.3 - }, - - "CJB Automation": { - "ID": "CJBAutomation", - "Default | UpdateKey": "Nexus:211", - "~1.4 | Status": "AssumeBroken", // broke in SDV 1.2 - "~1.4 | AlternativeUrl": "http://www.nexusmods.com/stardewvalley/mods/1063" - }, - - "CJB Cheats Menu": { - "ID": "CJBok.CheatsMenu", - "FormerIDs": "CJBCheatsMenu", // changed in 1.14 - "Default | UpdateKey": "Nexus:4", - "~1.18-beta | Status": "AssumeBroken" // broke in SDV 1.3, first beta causes significant friendship bugs - }, - - "CJB Item Spawner": { - "ID": "CJBok.ItemSpawner", - "FormerIDs": "CJBItemSpawner", // changed in 1.7 - "Default | UpdateKey": "Nexus:93", - "~1.10 | Status": "AssumeBroken" // broke in SDV 1.3 - }, - - "CJB Show Item Sell Price": { - "ID": "CJBok.ShowItemSellPrice", - "FormerIDs": "CJBShowItemSellPrice", // changed in 1.7 - "Default | UpdateKey": "Nexus:5", - "~1.8 | Status": "AssumeBroken" // broke in SDV 1.3 - }, - - "Clean Farm": { - "ID": "tstaples.CleanFarm", - "Default | UpdateKey": "Nexus:794" - }, - - "Climates of Ferngill": { - "ID": "KoihimeNakamura.ClimatesOfFerngill", - "Default | UpdateKey": "Nexus:604" - }, - - "Coal Regen": { - "ID": "Blucifer.CoalRegen", - "Default | UpdateKey": "Nexus:1664" - }, - "Cobalt": { "ID": "spacechase0.Cobalt", "MapRemoteVersions": { "1.1.3": "1.1.2" } // not updated in manifest }, - "Cold Weather Haley": { - "ID": "LordXamon.ColdWeatherHaleyPRO", - "Default | UpdateKey": "Nexus:1169", - "~1.0 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - "Colored Chests": { "ID": "4befde5c-731c-4853-8e4b-c5cdf946805f", "~ | Status": "Obsolete", "~ | StatusReasonPhrase": "colored chests were added in Stardew Valley 1.1." }, - "Combat with Farm Implements": { - "ID": "SPDFarmingImplementsInCombat", - "Default | UpdateKey": "Nexus:313" - }, - - "Community Bundle Item Tooltip": { - "ID": "musbah.bundleTooltip", - "Default | UpdateKey": "Nexus:1329" - }, - - "Concentration on Farming": { - "ID": "punyo.ConcentrationOnFarming", - "Default | UpdateKey": "Nexus:1445" - }, - "Configurable Machines": { "ID": "21da6619-dc03-4660-9794-8e5b498f5b97", - "MapLocalVersions": { "1.2-beta": "1.2" }, - "Default | UpdateKey": "Nexus:280" - }, - - "Configurable Shipping Dates": { - "ID": "ConfigurableShippingDates", - "Default | UpdateKey": "Nexus:675", - "~1.1.1 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Content Patcher": { - "ID": "Pathoschild.ContentPatcher", - "Default | UpdateKey": "Nexus:1915", - "~1.4-beta.5 | Status": "AssumeBroken" // broke in SMAPI 2.6-beta.18 (in-game errors) - }, - - "Cooking Skill": { - "ID": "spacechase0.CookingSkill", - "FormerIDs": "CookingSkill", // changed in 1.0.4–6 - "Default | UpdateKey": "Nexus:522" - }, - - "CrabNet": { - "ID": "jwdred.CrabNet", - "Default | UpdateKey": "Nexus:584" + "MapLocalVersions": { "1.2-beta": "1.2" } }, "Crafting Counter": { "ID": "lolpcgaming.CraftingCounter", - "Default | UpdateKey": "Nexus:1585", "MapRemoteVersions": { "1.1": "1.0" } // not updated in manifest }, - "Current Location": { - "ID": "CurrentLocation102120161203", - "Default | UpdateKey": "Nexus:638" - }, - - "Custom Asset Modifier": { - "ID": "Omegasis.CustomAssetModifier", - "Default | UpdateKey": "1836" - }, - - "Custom Critters": { - "ID": "spacechase0.CustomCritters", - "Default | UpdateKey": "Nexus:1255" - }, - - "Custom Crops": { - "ID": "spacechase0.CustomCrops", - "Default | UpdateKey": "Nexus:1592" - }, - - "Custom Element Handler": { - "ID": "Platonymous.CustomElementHandler", - "Default | UpdateKey": "Nexus:1068" // added in 1.3.1 - }, - - "Custom Farming Redux": { - "ID": "Platonymous.CustomFarming", - "Default | UpdateKey": "Nexus:991" // added in 0.6.1 - }, - "Custom Farming Automate Bridge": { "ID": "Platonymous.CFAutomate", "~1.0.1 | Status": "AssumeBroken", // no longer compatible with Automate "~1.0.1 | AlternativeUrl": "https://www.nexusmods.com/stardewvalley/mods/991" }, - "Custom Farm Types": { - "ID": "spacechase0.CustomFarmTypes", - "Default | UpdateKey": "Nexus:1140" - }, - - "Custom Furniture": { - "ID": "Platonymous.CustomFurniture", - "Default | UpdateKey": "Nexus:1254" // added in 0.4.1 - }, - - "Customize Exterior": { - "ID": "spacechase0.CustomizeExterior", - "FormerIDs": "CustomizeExterior", // changed in 1.0.3 - "Default | UpdateKey": "Nexus:1099", - "~1.0.2 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - "Customizable Cart Redux": { "ID": "KoihimeNakamura.CCR", - "MapLocalVersions": { "1.1-20170917": "1.1" }, - "Default | UpdateKey": "Nexus:1402" - }, - - "Customizable Traveling Cart Days": { - "ID": "TravelingCartYyeahdude", - "Default | UpdateKey": "Nexus:567" + "MapLocalVersions": { "1.1-20170917": "1.1" } }, "Custom Linens": { "ID": "Mevima.CustomLinens", - "MapRemoteVersions": { "1.1": "1.0" }, // manifest not updated - "Default | UpdateKey": "Nexus:1027" - }, - - "Custom NPC": { - "ID": "Platonymous.CustomNPC", - "Default | UpdateKey": "Nexus:1607" - }, - - "Custom Shops Redux": { - "ID": "Omegasis.CustomShopReduxGui", - "Default | UpdateKey": "Nexus:1378" // added in 1.4.1 - }, - - "Custom TV": { - "ID": "Platonymous.CustomTV", - "Default | UpdateKey": "Nexus:1139" // added in 1.0.6 + "MapRemoteVersions": { "1.1": "1.0" } // manifest not updated }, - "Daily Luck Message": { - "ID": "Schematix.DailyLuckMessage", - "Default | UpdateKey": "Nexus:1327" - }, - - "Daily News": { - "ID": "bashNinja.DailyNews", - "Default | UpdateKey": "Nexus:1141", - "~1.2 | Status": "AssumeBroken" // broke in Stardew Valley 1.3 (or depends on CustomTV which broke) - }, - - "Daily Quest Anywhere": { - "ID": "Omegasis.DailyQuestAnywhere", - "FormerIDs": "DailyQuest", // changed in 1.4 - "Default | UpdateKey": "Nexus:513" // added in 1.4.1 - }, - - "Data Maps": { - "ID": "Pathoschild.DataMaps", - "Default | UpdateKey": "Nexus:1691", - "~1.4 | Status": "AssumeBroken" // replaced by Data Layers - }, - - "Debug Mode": { - "ID": "Pathoschild.DebugMode", - "FormerIDs": "Pathoschild.Stardew.DebugMode", // changed in 1.4 - "Default | UpdateKey": "Nexus:679", - "~1.8 | Status": "AssumeBroken" // broke in SDV 1.3 - }, - - "Did You Water Your Crops?": { - "ID": "Nishtra.DidYouWaterYourCrops", - "Default | UpdateKey": "Nexus:1583" - }, - - "Dynamic Checklist": { - "ID": "gunnargolf.DynamicChecklist", - "Default | UpdateKey": "Nexus:1145" // added in 1.0.1-pathoschild-update + "Custom Shirts": { + "ID": "Platonymous.CustomShirts", + "Default | UpdateKey": "Nexus:2416" // keep for dependencies }, "Dynamic Horses": { "ID": "Bpendragon-DynamicHorses", - "MapRemoteVersions": { "1.2": "1.1-release" }, // manifest not updated - "Default | UpdateKey": "Nexus:874" + "MapRemoteVersions": { "1.2": "1.1-release" } // manifest not updated }, "Dynamic Machines": { "ID": "DynamicMachines", - "MapLocalVersions": { "1.1": "1.1.1" }, - "Default | UpdateKey": "Nexus:374", - "~1.1.1 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Dynamic NPC Sprites": { - "ID": "BashNinja.DynamicNPCSprites", - "Default | UpdateKey": "Nexus:1183" - }, - - "Easier Farming": { - "ID": "cautiouswafffle.EasierFarming", - "Default | UpdateKey": "Nexus:1426" - }, - - "Empty Hands": { - "ID": "QuicksilverFox.EmptyHands", - "Default | UpdateKey": "Nexus:1176" // added in 1.0.1-pathoschild-update + "MapLocalVersions": { "1.1": "1.1.1" } }, "Enemy Health Bars": { "ID": "Speeder.HealthBars", - "FormerIDs": "SPDHealthBar", // changed in 1.7.1-pathoschild-update - "Default | UpdateKey": "Nexus:193" - }, - - "Entoarox Framework": { - "ID": "Entoarox.EntoaroxFramework", - "FormerIDs": "eacdb74b-4080-4452-b16b-93773cda5cf9", // changed in ??? - "~2.0.6 | UpdateKey": "Chucklefish:4228", // only enable update checks up to 2.0.6 by request (has its own update-check feature) - "~2.0.6 | Status": "AssumeBroken" // broke in SMAPI 2.5 (error reflecting into SMAPI internals) - }, - - "Expanded Fridge": { - "ID": "Uwazouri.ExpandedFridge", - "Default | UpdateKey": "Nexus:1191" - }, - - "Experience Bars": { - "ID": "spacechase0.ExperienceBars", - "FormerIDs": "ExperienceBars", // changed in 1.0.2 - "Default | UpdateKey": "Nexus:509" - }, - - "Extended Bus System": { - "ID": "ExtendedBusSystem", - "Default | UpdateKey": "Chucklefish:4373" - }, - - "Extended Fridge": { - "ID": "Crystalmir.ExtendedFridge", - "FormerIDs": "Mystra007ExtendedFridge", // changed in 1.0.1 - "Default | UpdateKey": "Nexus:485" - }, - - "Extended Greenhouse": { - "ID": "ExtendedGreenhouse", - "Default | UpdateKey": "Chucklefish:4303", - "~1.0.2 | Status": "AssumeBroken" // broke in SDV 1.2 - }, - - "Extended Minecart": { - "ID": "Entoarox.ExtendedMinecart", - "~1.7.1 | UpdateKey": "Chucklefish:4359" // only enable update checks up to 1.7.1 by request (has its own update-check feature) - }, - - "Extended Reach": { - "ID": "spacechase0.ExtendedReach", - "Default | UpdateKey": "Nexus:1493" + "FormerIDs": "SPDHealthBar" // changed in 1.7.1-pathoschild-update }, "Fall 28 Snow Day": { "ID": "Omegasis.Fall28SnowDay", - "Default | UpdateKey": "Nexus:486", // added in 1.4.1 "~1.4.1 | Status": "AssumeBroken" // broke in SMAPI 2.0, and update for SMAPI 2.0 doesn't do anything }, - "Farm Automation Unofficial: Item Collector": { - "ID": "Maddy99.FarmAutomation.ItemCollector", - "~0.5 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Farm Expansion": { - "ID": "Advize.FarmExpansion", - "FormerIDs": "3888bdfd-73f6-4776-8bb7-8ad45aea1915 | AdvizeFarmExpansionMod-2-0 | AdvizeFarmExpansionMod-2-0-5", // changed in 2.0, 2.0.5, and 3.0 - "Default | UpdateKey": "Nexus:130" - }, - - "Fast Animations": { - "ID": "Pathoschild.FastAnimations", - "Default | UpdateKey": "Nexus:1089", - "~1.5 | Status": "AssumeBroken" // broke in SDV 1.3 - }, - - "Faster Grass": { - "ID": "IceGladiador.FasterGrass", - "Default | UpdateKey": "Nexus:1772" - }, - - "Faster Paths": { - "ID": "Entoarox.FasterPaths", - "FormerIDs": "615f85f8-5c89-44ee-aecc-c328f172e413", // changed in 1.3 - "~1.3.3 | UpdateKey": "Chucklefish:3641" // only enable update checks up to 1.3.3 by request (has its own update-check feature) - }, - "Fishing Adjust": { "ID": "shuaiz.FishingAdjustMod", - "Default | UpdateKey": "Nexus:1350", - "~2.0.1 | Status": "AssumeBroken" // Method not found: 'Void Harmony.HarmonyInstance.Patch(System.Reflection.MethodBase, Harmony.HarmonyMethod, Harmony.HarmonyMethod, Harmony.HarmonyMethod)' + "~2.0.1 | Status": "AssumeBroken" // Method not found: 'Void Harmony.HarmonyInstance.Patch(System.Reflection.MethodBase, Harmony.HarmonyMethod, Harmony.HarmonyMethod, Harmony.HarmonyMethod)' }, - "Fishing Tuner Redux": { - "ID": "HammurabiFishingTunerRedux", - "Default | UpdateKey": "Chucklefish:4578" - }, - - "Fixed Secret Woods Debris": { - "ID": "f4iTh.WoodsDebrisFix", - "Default | UpdateKey": "Nexus:1941" + "Fishing Automaton": { + "ID": "Drynwynn.FishingAutomaton", + "~1.1 | Status": "AssumeBroken" // runtime errors with Harmony 1.2.0.1 in SMAPI 2.8+ }, "Fix Scythe Exp": { "ID": "bcmpinc.FixScytheExp", - "~0.2 | Status": "AssumeBroken" // Exception from HarmonyInstance "bcmpinc.FixScytheExp" [...] Bad label content in ILGenerator. - }, - - "Flower Color Picker": { - "ID": "spacechase0.FlowerColorPicker", - "Default | UpdateKey": "Nexus:1229" - }, - - "Forage at the Farm": { - "ID": "Nishtra.ForageAtTheFarm", - "FormerIDs": "ForageAtTheFarm", // changed in <=1.6 - "Default | UpdateKey": "Nexus:673" - }, - - "Furniture Anywhere": { - "ID": "Entoarox.FurnitureAnywhere", - "~1.1.5 | UpdateKey": "Chucklefish:4324" // only enable update checks up to 1.1.5 by request (has its own update-check feature) - }, - - "Game Reminder": { - "ID": "mmanlapat.GameReminder", - "Default | UpdateKey": "Nexus:1153" + "~0.3 | Status": "AssumeBroken" // broke in 1.3: Exception from HarmonyInstance "bcmpinc.FixScytheExp" [...] Bad label content in ILGenerator. }, "Gate Opener": { @@ -702,231 +217,24 @@ "Default | UpdateKey": "GitHub:mralbobo/stardew-gate-opener" }, - "GenericShopExtender": { - "ID": "GenericShopExtender", - "Default | UpdateKey": "Nexus:814" // added in 0.1.3 - }, - - "Geode Info Menu": { - "ID": "cat.geodeinfomenu", - "Default | UpdateKey": "Nexus:1448" - }, - - "Get Dressed": { - "ID": "Advize.GetDressed", - "Default | UpdateKey": "Nexus:331" - }, - - "Giant Crop Ring": { - "ID": "cat.giantcropring", - "Default | UpdateKey": "Nexus:1182" - }, - - "Gift Taste Helper": { - "ID": "tstaples.GiftTasteHelper", - "FormerIDs": "8008db57-fa67-4730-978e-34b37ef191d6", // changed in 2.5 - "Default | UpdateKey": "Nexus:229" - }, - - "Grandfather's Gift": { - "ID": "ShadowDragon.GrandfathersGift", - "Default | UpdateKey": "Nexus:985" - }, - - "Happy Animals": { - "ID": "HappyAnimals", - "~1.0.3 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Happy Birthday (Omegasis)": { - "ID": "Omegasis.HappyBirthday", - "Default | UpdateKey": "Nexus:520" // added in 1.4.1 - }, - - "Hardcore Mines": { - "ID": "kibbe.hardcore_mines", - "Default | UpdateKey": "Nexus:1674" - }, - - "Harp of Yoba Redux": { - "ID": "Platonymous.HarpOfYobaRedux", - "Default | UpdateKey": "Nexus:914" // added in 2.0.3 - }, - - "Harvest Moon Witch Princess": { - "ID": "Sasara.WitchPrincess", - "Default | UpdateKey": "Nexus:1157" - }, - - "Harvest With Scythe": { - "ID": "965169fd-e1ed-47d0-9f12-b104535fb4bc", - "Default | UpdateKey": "Nexus:236" - }, - - "Horse Whistle (icepuente)": { - "ID": "icepuente.HorseWhistle", - "Default | UpdateKey": "Nexus:1131", - "~1.1.2-unofficial.1-pathoschild | Status": "AssumeBroken" // causes significant lag, fixed in unofficial.2 - }, - - "Hunger (Yyeadude)": { - "ID": "HungerYyeadude", - "Default | UpdateKey": "Nexus:613" - }, - - "Hunger for Food (Tigerle)": { - "ID": "HungerForFoodByTigerle", - "Default | UpdateKey": "Nexus:810", - "~0.1.2 | Status": "AssumeBroken" // broke in SMAPI 2.0 + "Grass Growth": { + "ID": "bcmpinc.GrassGrowth", + "~0.3 | Status": "AssumeBroken" // broke in 1.3.29 (runtime errors: System.IndexOutOfRangeException: Could not find instruction sequence) }, "Hunger Mod (skn)": { "ID": "skn.HungerMod", - "MapRemoteVersions": { "1.2.1": "1.0" }, // manifest not updated - "Default | UpdateKey": "Nexus:1127" + "MapRemoteVersions": { "1.2.1": "1.0" } // manifest not updated }, "Idle Pause": { "ID": "Veleek.IdlePause", - "MapRemoteVersions": { "1.2": "1.1" }, // manifest not updated - "Default | UpdateKey": "Nexus:1092" - }, - - "Improved Quality of Life": { - "ID": "Demiacle.ImprovedQualityOfLife", - "Default | UpdateKey": "Nexus:1025", - "~1.1 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Instant Geode": { - "ID": "InstantGeode", - "~1.12 | Status": "AssumeBroken" // broke in SDV 1.2 - }, - - "Instant Grow Trees": { - "ID": "cantorsdust.InstantGrowTrees", - "FormerIDs": "dc50c58b-c7d8-4e60-86cc-e27b5d95ee59 | community.InstantGrowTrees", // changed in 1.2 and 1.3.1 - "Default | UpdateKey": "Nexus:173" - }, - - "Interaction Helper": { - "ID": "HammurabiInteractionHelper", - "Default | UpdateKey": "Chucklefish:4640" // added in 1.0.4-pathoschild-update + "MapRemoteVersions": { "1.2": "1.1" } // manifest not updated }, "Item Auto Stacker": { "ID": "cat.autostacker", - "MapRemoteVersions": { "1.0.1": "1.0" }, // manifest not updated - "Default | UpdateKey": "Nexus:1184" - }, - - "Json Assets": { - "ID": "spacechase0.JsonAssets", - "Default | UpdateKey": "Nexus:1720" - }, - - "Junimo Farm": { - "ID": "Platonymous.JunimoFarm", - "MapRemoteVersions": { "1.1.2": "1.1.1" }, // manifest not updated - "Default | UpdateKey": "Nexus:984" // added in 1.1.3 - }, - - "Less Strict Over-Exertion (AntiExhaustion)": { - "ID": "BALANCEMOD_AntiExhaustion", - "MapLocalVersions": { "0.0": "1.1" }, - "Default | UpdateKey": "Nexus:637", - "~1.1 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Level Extender": { - "ID": "DevinLematty.LevelExtender", - "FormerIDs": "Devin Lematty.Level Extender", // changed in 1.3 - "Default | UpdateKey": "Nexus:1471" - }, - - "Level Up Notifications": { - "ID": "Level Up Notifications", - "MapRemoteVersions": { "0.0.1a": "0.0.1" }, - "Default | UpdateKey": "Nexus:855" - }, - - "Location and Music Logging": { - "ID": "Brandy Lover.LMlog", - "Default | UpdateKey": "Nexus:1366" - }, - - "Longevity": { - "ID": "RTGOAT.Longevity", - "MapRemoteVersions": { "1.6.8h": "1.6.8" }, - "Default | UpdateKey": "Nexus:649" - }, - - "Lookup Anything": { - "ID": "Pathoschild.LookupAnything", - "FormerIDs": "LookupAnything", // changed in 1.10.1 - "Default | UpdateKey": "Nexus:541", - "~1.18.1 | Status": "AssumeBroken" // broke in SDV 1.3 - }, - - "Love Bubbles": { - "ID": "LoveBubbles", - "Default | UpdateKey": "Nexus:1318" - }, - - "Loved Labels": { - "ID": "Advize.LovedLabels", - "Default | UpdateKey": "Nexus:279" - }, - - "Luck Skill": { - "ID": "spacechase0.LuckSkill", - "FormerIDs": "LuckSkill", // changed in 0.1.4 - "Default | UpdateKey": "Nexus:521" - }, - - "Magic": { - "ID": "spacechase0.Magic", - "MapRemoteVersions": { "0.1.2": "0.1.1" } // not updated in manifest - }, - - "Mail Framework": { - "ID": "DIGUS.MailFrameworkMod", - "Default | UpdateKey": "Nexus:1536" - }, - - "MailOrderPigs": { - "ID": "jwdred.MailOrderPigs", - "Default | UpdateKey": "Nexus:632" - }, - - "Makeshift Multiplayer": { - "ID": "spacechase0.StardewValleyMP", - "FormerIDs": "StardewValleyMP", // changed in 0.3 - "Default | UpdateKey": "Nexus:501" - }, - - "Map Image Exporter": { - "ID": "spacechase0.MapImageExporter", - "FormerIDs": "MapImageExporter", // changed in 1.0.2 - "Default | UpdateKey": "Nexus:1073" - }, - - "Message Box [API]? (ChatMod)": { - "ID": "Kithio:ChatMod", - "Default | UpdateKey": "Chucklefish:4296", - "~1.0 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Mining at the Farm": { - "ID": "Nishtra.MiningAtTheFarm", - "FormerIDs": "MiningAtTheFarm", // changed in <=1.7 - "Default | UpdateKey": "Nexus:674" - }, - - "Mining With Explosives": { - "ID": "Nishtra.MiningWithExplosives", - "FormerIDs": "MiningWithExplosives", // changed in 1.1 - "Default | UpdateKey": "Nexus:770" + "MapRemoteVersions": { "1.0.1": "1.0" } // manifest not updated }, "Modder Serialization Utility": { @@ -935,81 +243,39 @@ "~ | StatusReasonPhrase": "it's no longer maintained or used." }, - "Monster Level Tip": { - "ID": "WhiteMind.MonsterLT", - "Default | UpdateKey": "Nexus:1896" - }, - - "More Animals": { - "ID": "Entoarox.MoreAnimals", - "FormerIDs": "821ce8f6-e629-41ad-9fde-03b54f68b0b6MOREPETS | Entoarox.MorePets", // changed in 1.3 and 2.0 - "~2.0.2 | UpdateKey": "Chucklefish:4288" // only enable update checks up to 2.0.2 by request (has its own update-check feature) - }, - - "More Artifact Spots": { - "ID": "451", - "Default | UpdateKey": "Nexus:451" - }, - - "More Map Layers": { - "ID": "Platonymous.MoreMapLayers", - "Default | UpdateKey": "Nexus:1134" // added in 1.1.1 - }, - "More Rain": { "ID": "Omegasis.MoreRain", - "Default | UpdateKey": "Nexus:441", // added in 1.5.1 "~1.4 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, - "More Weapons": { - "ID": "Joco80.MoreWeapons", - "Default | UpdateKey": "Nexus:1168" + "More Silo Storage": { + "ID": "OrneryWalrus.MoreSiloStorage", + "~1.0.1 | Status": "AssumeBroken" // broke in SDV 1.3 }, "Move Faster": { "ID": "shuaiz.MoveFasterMod", - "Default | UpdateKey": "Nexus:1351", - "1.0.1 | Status": "AssumeBroken" // doesn't do anything as of SDV 1.2.33 (bad Harmony patch?) - }, - - "Multiple Sprites and Portraits On Rotation (File Loading)": { - "ID": "FileLoading", - "MapLocalVersions": { "1.1": "1.12" }, - "Default | UpdateKey": "Nexus:1094", - "~1.12 | Status": "AssumeBroken" // broke in SMAPI 2.0 + "~1.0.1 | Status": "AssumeBroken" // doesn't do anything as of SDV 1.2.33 (bad Harmony patch?) }, - "Museum Rearranger": { - "ID": "Omegasis.MuseumRearranger", - "Default | UpdateKey": "Nexus:428" // added in 1.4.1 + "MTN": { + "ID": "SgtPickles.MTN", + "~1.2.5 | Status": "AssumeBroken" // replaces Game1.multiplayer, which breaks SMAPI's multiplayer API. }, - "Mushroom Level Tip": { - "ID": "WhiteMind.MLT", - "Default | UpdateKey": "Nexus:1894" - }, - - "New Machines": { - "ID": "F70D4FAB-0AB2-4B78-9F1B-AF2CA2236A59", - "Default | UpdateKey": "Chucklefish:3683", - "~4.2.1343 | Status": "AssumeBroken" // broke in SMAPI 2.0 + "Multiple Sprites and Portraits On Rotation (File Loading)": { + "ID": "FileLoading", + "MapLocalVersions": { "1.1": "1.12" } }, "Night Owl": { "ID": "Omegasis.NightOwl", - "MapLocalVersions": { "2.1": "1.3" }, // 1.3 had wrong version in manifest - "Default | UpdateKey": "Nexus:433" // added in 1.4.1 + "MapLocalVersions": { "2.1": "1.3" } // 1.3 had wrong version in manifest }, - "No Crows": { - "ID": "cat.nocrows", - "Default | UpdateKey": "Nexus:1682" - }, - - "No Kids Ever": { - "ID": "Hangy.NoKidsEver", - "Default | UpdateKey": "Nexus:1464" + "No Added Flying Mine Monsters": { + "ID": "Drynwynn.NoAddedFlyingMineMonsters", + "~1.1 | Status": "AssumeBroken" // runtime errors with Harmony 1.2.0.1 in SMAPI 2.8+ }, "No Debug Mode": { @@ -1018,90 +284,12 @@ "~ | StatusReasonPhrase": "debug mode was removed in SMAPI 1.0." }, - "No Fence Decay": { - "ID": "cat.nofencedecay", - "Default | UpdateKey": "Nexus:1180" - }, - - "No More Pets": { - "ID": "Omegasis.NoMorePets", - "FormerIDs": "NoMorePets", // changed in 1.4 - "Default | UpdateKey": "Nexus:506" // added in 1.4.1 - }, - - "No Rumble Horse": { - "ID": "Xangria.NoRumbleHorse", - "Default | UpdateKey": "Nexus:1779" - }, - - "No Soil Decay": { - "ID": "289dee03-5f38-4d8e-8ffc-e440198e8610", - "Default | UpdateKey": "Nexus:237", - "~0.5 | Status": "AssumeBroken" // broke in SDV 1.2 and uses Assembly.GetExecutingAssembly().Location - }, - - "No Soil Decay Redux": { - "ID": "Platonymous.NoSoilDecayRedux", - "Default | UpdateKey": "Nexus:1084" // added in 1.1.9 - }, - - "NPC Map Locations": { - "ID": "Bouhm.NPCMapLocations", - "FormerIDs": "NPCMapLocationsMod", // changed in 2.0 - "Default | UpdateKey": "Nexus:239" - }, - - "Object Time Left": { - "ID": "spacechase0.ObjectTimeLeft", - "Default | UpdateKey": "Nexus:1315" - }, - "OmniFarm": { "ID": "PhthaloBlue.OmniFarm", "FormerIDs": "BlueMod_OmniFarm", // changed in 2.0.2-pathoschild-update "Default | UpdateKey": "GitHub:lambui/StardewValleyMod_OmniFarm" }, - "One Click Shed": { - "ID": "BitwiseJonMods.OneClickShedReloader", - "Default | UpdateKey": "Nexus:2052" - }, - - "Out of Season Bonuses (Seasonal Items)": { - "ID": "midoriarmstrong.seasonalitems", - "Default | UpdateKey": "Nexus:1452" - }, - - "Part of the Community": { - "ID": "SB_PotC", - "Default | UpdateKey": "Nexus:923" - }, - - "PelicanFiber": { - "ID": "jwdred.PelicanFiber", - "Default | UpdateKey": "Nexus:631" - }, - - "PelicanTTS": { - "ID": "Platonymous.PelicanTTS", - "Default | UpdateKey": "Nexus:1079" // added in 1.6.1 - }, - - "Persia the Mermaid - Standalone Custom NPC": { - "ID": "63b9f419-7449-42db-ab2e-440b4d05c073", - "Default | UpdateKey": "Nexus:1419" - }, - - "Persistent Game Options": { - "ID": "Xangria.PersistentGameOptions", - "Default | UpdateKey": "Nexus:1778" - }, - - "Plant on Grass": { - "ID": "Demiacle.PlantOnGrass", - "Default | UpdateKey": "Nexus:1026" - }, - "PyTK - Platonymous Toolkit": { "ID": "Platonymous.Toolkit", "Default | UpdateKey": "Nexus:1726" @@ -1109,383 +297,82 @@ "Point-and-Plant": { "ID": "jwdred.PointAndPlant", - "Default | UpdateKey": "Nexus:572", "MapRemoteVersions": { "1.0.3": "1.0.2" } // manifest not updated }, - "Pony Weight Loss Program": { - "ID": "BadNetCode.PonyWeightLossProgram", - "Default | UpdateKey": "Nexus:1232" - }, - - "Portraiture": { - "ID": "Platonymous.Portraiture", - "Default | UpdateKey": "Nexus:999" // added in 1.3.1 - }, - "Prairie King Made Easy": { "ID": "Mucchan.PrairieKingMadeEasy", - "Default | UpdateKey": "Chucklefish:3594", "~1.0 | Status": "AssumeBroken" // broke in SDV 1.2 }, - "Purchasable Recipes": { - "ID": "Paracosm.PurchasableRecipes", - "Default | UpdateKey": "Nexus:1722" - }, - - "Quest Delay": { - "ID": "BadNetCode.QuestDelay", - "Default | UpdateKey": "Nexus:1239" - }, - - "Recatch Legendary Fish": { - "ID": "cantorsdust.RecatchLegendaryFish", - "FormerIDs": "b3af8c31-48f0-43cf-8343-3eb08bcfa1f9 | community.RecatchLegendaryFish", // changed in 1.3 and 1.5.1 - "Default | UpdateKey": "Nexus:172" - }, - - "Regeneration": { - "ID": "HammurabiRegeneration", - "Default | UpdateKey": "Chucklefish:4584" - }, - - "Relationship Bar UI": { - "ID": "RelationshipBar", - "Default | UpdateKey": "Nexus:1009" - }, - - "RelationshipsEnhanced": { - "ID": "relationshipsenhanced", - "Default | UpdateKey": "Chucklefish:4435", - "~1.0 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - "Relationship Status": { "ID": "relationshipstatus", - "MapRemoteVersions": { "1.0.5": "1.0.4" }, // not updated in manifest - "Default | UpdateKey": "Nexus:751", - "~1.0.5 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Rented Tools": { - "ID": "JarvieK.RentedTools", - "Default | UpdateKey": "Nexus:1307" - }, - - "Replanter": { - "ID": "jwdred.Replanter", - "Default | UpdateKey": "Nexus:589" + "MapRemoteVersions": { "1.0.5": "1.0.4" } // not updated in manifest }, "ReRegeneration": { "ID": "lrsk_sdvm_rerg.0925160827", - "MapLocalVersions": { "1.1.2-release": "1.1.2" }, - "Default | UpdateKey": "Chucklefish:4465" - }, - - "Reseed": { - "ID": "Roc.Reseed", - "Default | UpdateKey": "Nexus:887" - }, - - "Reusable Wallpapers and Floors (Wallpaper Retain)": { - "ID": "dae1b553-2e39-43e7-8400-c7c5c836134b", - "Default | UpdateKey": "Nexus:356", - "~1.5 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Ring of Fire": { - "ID": "Platonymous.RingOfFire", - "Default | UpdateKey": "Nexus:1166" // added in 1.0.1 - }, - - "Rope Bridge": { - "ID": "RopeBridge", - "Default | UpdateKey": "Nexus:824" - }, - - "Rotate Toolbar": { - "ID": "Pathoschild.RotateToolbar", - "Default | UpdateKey": "Nexus:1100", - "~1.2.1 | Status": "AssumeBroken" // broke in SDV 1.3 - }, - - "Rush Orders": { - "ID": "spacechase0.RushOrders", - "FormerIDs": "RushOrders", // changed in 1.1 - "Default | UpdateKey": "Nexus:605" - }, - - "Save Anywhere": { - "ID": "Omegasis.SaveAnywhere", - "Default | UpdateKey": "Nexus:444", // added in 2.6.1 - "MapRemoteVersions": { "2.6.2": "2.6.1" } // not updated in manifest + "MapLocalVersions": { "1.1.2-release": "1.1.2" } }, "Save Backup": { "ID": "Omegasis.SaveBackup", - "Default | UpdateKey": "Nexus:435", // added in 1.3.1 "~1.2 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, - "Scroll to Blank": { - "ID": "caraxian.scroll.to.blank", - "Default | UpdateKey": "Chucklefish:4405" - }, - - "Scythe Harvesting": { - "ID": "mmanlapat.ScytheHarvesting", - "FormerIDs": "ScytheHarvesting", // changed in 1.6 - "Default | UpdateKey": "Nexus:1106" - }, - - "SDV Twitch": { - "ID": "MTD.SDVTwitch", - "Default | UpdateKey": "Nexus:1760" - }, - - "Seasonal Immersion": { - "ID": "Entoarox.SeasonalImmersion", - "FormerIDs": "EntoaroxSeasonalHouse | EntoaroxSeasonalBuildings | EntoaroxSeasonalImmersion", // changed in 1.1, 1.6 or earlier, and 1.7 - "~1.11 | UpdateKey": "Chucklefish:4262" // only enable update checks up to 1.11 by request (has its own update-check feature) - }, - - "Seed Bag": { - "ID": "Platonymous.SeedBag", - "Default | UpdateKey": "Nexus:1133" // added in 1.1.2 - }, - - "Seed Catalogue": { - "ID": "spacechase0.SeedCatalogue", - "Default | UpdateKey": "Nexus:1640" - }, - - "Self Service": { - "ID": "JarvieK.SelfService", - "MapRemoteVersions": { "0.2.1": "0.2" }, // manifest not updated - "Default | UpdateKey": "Nexus:1304" - }, - - "Send Items": { - "ID": "Denifia.SendItems", - "Default | UpdateKey": "Nexus:1087" // added in 1.0.3 (2017-10-04) - }, - - "Shed Notifications (BuildingsNotifications)": { - "ID": "TheCroak.BuildingsNotifications", - "Default | UpdateKey": "Nexus:620", - "~0.4.1 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Shenandoah Project": { - "ID": "Nishtra.ShenandoahProject", - "FormerIDs": "Shenandoah Project", // changed in 1.2 - "MapRemoteVersions": { "1.1.1": "1.1" }, // not updated in manifest - "Default | UpdateKey": "Nexus:756" - }, - - "Ship Anywhere": { - "ID": "spacechase0.ShipAnywhere", - "Default | UpdateKey": "Nexus:1379" - }, - - "Shipment Tracker": { - "ID": "7e474181-e1a0-40f9-9c11-d08a3dcefaf3", - "Default | UpdateKey": "Nexus:321" + "Server Bookmarker": { + "ID": "Ilyaki.ServerBookmarker", + "~1.0.0 | Status": "AssumeBroken" // broke in Stardew Valley 1.3.29 (runtime errors) }, "Shop Expander": { "ID": "Entoarox.ShopExpander", - "FormerIDs": "EntoaroxShopExpander", // changed in 1.5 and 1.5.2; disambiguate from Faster Paths - "MapRemoteVersions": { "1.6.0b": "1.6.0" }, - "~1.6 | UpdateKey": "Chucklefish:4381" // only enable update checks up to 1.6 by request (has its own update-check feature) + "FormerIDs": "EntoaroxShopExpander", // changed in 1.5.2 + "MapRemoteVersions": { "1.6.0b": "1.6.0" } }, "Showcase Mod": { "ID": "Igorious.Showcase", - "MapLocalVersions": { "0.9-500": "0.9" }, - "Default | UpdateKey": "Chucklefish:4487", - "~0.9 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Shroom Spotter": { - "ID": "TehPers.ShroomSpotter", - "Default | UpdateKey": "Nexus:908" - }, - - "Simple Crop Label": { - "ID": "SimpleCropLabel", - "Default | UpdateKey": "Nexus:314" - }, - - "Simple Sound Manager": { - "ID": "Omegasis.SimpleSoundManager", - "Default | UpdateKey": "Nexus:1410" // added in 1.0.1 - }, - - "Simple Sprinklers": { - "ID": "tZed.SimpleSprinkler", - "Default | UpdateKey": "Nexus:76" + "MapLocalVersions": { "0.9-500": "0.9" } }, "Siv's Marriage Mod": { "ID": "6266959802", // official version "FormerIDs": "Siv.MarriageMod | medoli900.Siv's Marriage Mod", // 1.2.3-unofficial versions - "MapLocalVersions": { "0.0": "1.4" }, - "Default | UpdateKey": "Nexus:366" - }, - - "Skill Prestige": { - "ID": "alphablackwolf.skillPrestige", - "FormerIDs": "6b843e60-c8fc-4a25-a67b-4a38ac8dcf9b", // changed circa 1.2.3 - "Default | UpdateKey": "Nexus:569" + "MapLocalVersions": { "0.0": "1.4" } }, "Skill Prestige: Cooking Adapter": { "ID": "Alphablackwolf.CookingSkillPrestigeAdapter", "FormerIDs": "20d6b8a3-b6e7-460b-a6e4-07c2b0cb6c63", // changed circa 1.1 - "MapRemoteVersions": { "1.2.3": "1.1" }, // manifest not updated - "Default | UpdateKey": "Nexus:569" - }, - - "Skip Intro": { - "ID": "Pathoschild.SkipIntro", - "FormerIDs": "SkipIntro", // changed in 1.4 - "Default | UpdateKey": "Nexus:533", - "~1.7.2 | Status": "AssumeBroken" // broke in SDV 1.3 - }, - - "Skull Cavern Elevator": { - "ID": "SkullCavernElevator", - "Default | UpdateKey": "Nexus:963" + "MapRemoteVersions": { "1.2.3": "1.1" } // manifest not updated }, "Skull Cave Saver": { "ID": "cantorsdust.SkullCaveSaver", "FormerIDs": "8ac06349-26f7-4394-806c-95d48fd35774 | community.SkullCaveSaver", // changed in 1.1 and 1.2.2 - "Default | UpdateKey": "Nexus:175", "1.3-beta | Status": "AssumeBroken" // doesn't work in multiplayer, no longer maintained }, - "Sleepy Eye": { - "ID": "spacechase0.SleepyEye", - "Default | UpdateKey": "Nexus:1152" - }, - - "Slower Fence Decay": { - "ID": "Speeder.SlowerFenceDecay", - "FormerIDs": "SPDSlowFenceDecay", // changed in 0.5.2-pathoschild-update - "Default | UpdateKey": "Nexus:252", - "~0.5.1 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Smart Mod": { - "ID": "KuroBear.SmartMod", - "~2.2 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - "Solar Eclipse Event": { "ID": "KoihimeNakamura.SolarEclipseEvent", - "Default | UpdateKey": "Nexus:897", "MapLocalVersions": { "1.3.1-20180131": "1.3.1" } }, - "SpaceCore": { - "ID": "spacechase0.SpaceCore", - "Default | UpdateKey": "Nexus:1348" - }, - - "Speedster": { - "ID": "Platonymous.Speedster", - "Default | UpdateKey": "Nexus:1102" // added in 1.3.1 - }, - "Split Screen": { "ID": "Ilyaki.SplitScreen", "~3.0.1 | Status": "AssumeBroken" // broke in SMAPI 2.6-beta.16 due to reflection into SMAPI internals }, - "Sprinkler Range": { - "ID": "cat.sprinklerrange", - "Default | UpdateKey": "Nexus:1179" - }, - - "Sprinkles": { - "ID": "Platonymous.Sprinkles", - "Default | UpdateKey": "Chucklefish:4592" - }, - - "Sprint and Dash": { - "ID": "SPDSprintAndDash", - "Default | UpdateKey": "Nexus:235", - "~1.0 | Status": "AssumeBroken" // broke in SDV 1.2 - }, - - "Sprint and Dash Redux": { - "ID": "littleraskol.SprintAndDashRedux", - "FormerIDs": "lrsk_sdvm_sndr.0921161059", // changed in 1.3 - "Default | UpdateKey": "Chucklefish:4201" - }, - - "StackSplitX": { - "ID": "tstaples.StackSplitX", - "Default | UpdateKey": "Nexus:798" - }, - - "Stardew Config Menu": { - "ID": "Juice805.StardewConfigMenu", - "Default | UpdateKey": "Nexus:1312" - }, - - "Stardew Content Compatibility Layer (SCCL)": { - "ID": "SCCL", - "Default | UpdateKey": "Nexus:889", - "~0.1 | Status": "AssumeBroken" // broke in SDV 1.2 - }, - - "Stardew Editor Game Integration": { - "ID": "spacechase0.StardewEditor.GameIntegration", - "Default | UpdateKey": "Nexus:1298" - }, - "Stardew Notification": { "ID": "stardewnotification", "Default | UpdateKey": "GitHub:monopandora/StardewNotification" }, - "Stardew Symphony": { - "ID": "Omegasis.StardewSymphony", - "Default | UpdateKey": "Nexus:425" // added in 1.4.1 - }, - - "StarDustCore": { - "ID": "StarDustCore", - "~ | Status": "Obsolete", - "~ | StatusReasonPhrase": "it was only used by earlier versions of Save Anywhere, and is no longer used or maintained." - }, - - "Starting Money": { - "ID": "mmanlapat.StartingMoney", - "FormerIDs": "StartingMoney", // changed in 1.1 - "Default | UpdateKey": "Nexus:1138" - }, - - "StashItemsToChest": { - "ID": "BlueMod_StashItemsToChest", - "Default | UpdateKey": "GitHub:lambui/StardewValleyMod_StashItemsToChest", - "~1.0.1 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - "Stephan's Lots of Crops": { "ID": "stephansstardewcrops", "MapRemoteVersions": { "1.41": "1.1" }, // manifest not updated - "Default | UpdateKey": "Chucklefish:4314" - }, - - "Stumps to Hardwood Stumps": { - "ID": "StumpsToHardwoodStumps", - "Default | UpdateKey": "Nexus:691" + "~1.1 | Status": "AssumeBroken" // broke in SDV 1.3 (overwrites vanilla items) }, "Summit Reborn": { @@ -1494,65 +381,9 @@ "~1.0.2 | Status": "AssumeBroken" // broke in SDV 1.3 (runtime errors) }, - "Super Greenhouse Warp Modifier": { - "ID": "SuperGreenhouse", - "Default | UpdateKey": "Chucklefish:4334", - "~1.0 | Status": "AssumeBroken" // broke in SMAPI 2.0 - }, - - "Swim Almost Anywhere / Swim Suit": { - "ID": "Platonymous.SwimSuit", - "Default | UpdateKey": "Nexus:1215" // added in 0.5.1 - }, - - "Tapper Ready": { - "ID": "skunkkk.TapperReady", - "Default | UpdateKey": "Nexus:1219" - }, - - "Teh's Fishing Overhaul": { - "ID": "TehPers.FishingOverhaul", - "Default | UpdateKey": "Nexus:866" - }, - - "Teleporter": { - "ID": "Teleporter", - "Default | UpdateKey": "Chucklefish:4374", - "~1.0.2 | Status": "AssumeBroken" // broke in SDV 1.2 - }, - - "The Long Night": { - "ID": "Pathoschild.TheLongNight", - "Default | UpdateKey": "Nexus:1369", - "~1.1.1 | Status": "AssumeBroken" // broke in SDV 1.3 - }, - - "Three-heart Dance Partner": { - "ID": "ThreeHeartDancePartner", - "Default | UpdateKey": "Nexus:500" - }, - - "TimeFreeze": { - "ID": "Omegasis.TimeFreeze", - "FormerIDs": "4108e859-333c-4fec-a1a7-d2e18c1019fe", // changed in 1.2 - "Default | UpdateKey": "Nexus:973" // added in 1.2.1 - }, - "Time Reminder": { "ID": "KoihimeNakamura.TimeReminder", - "MapLocalVersions": { "1.0-20170314": "1.0.2" }, - "Default | UpdateKey": "Nexus:1000" - }, - - "TimeSpeed": { - "ID": "cantorsdust.TimeSpeed", - "FormerIDs": "community.TimeSpeed", // changed in 2.3.3 - "Default | UpdateKey": "Nexus:169" - }, - - "To Do List": { - "ID": "eleanor.todolist", - "Default | UpdateKey": "Nexus:1630" + "MapLocalVersions": { "1.0-20170314": "1.0.2" } }, "Tool Charging": { @@ -1560,134 +391,15 @@ "Default | UpdateKey": "GitHub:mralbobo/stardew-tool-charging" }, - "TractorMod": { - "ID": "Pathoschild.TractorMod", - "FormerIDs": "BlueMod_TractorMod | PhthaloBlue.TractorMod | community.TractorMod", // changed in 3.2, 4.0 beta, and 4.0 - "Default | UpdateKey": "Nexus:1401", - "~4.5-beta | Status": "AssumeBroken" // broke in SDV 1.3 - }, - - "TrainerMod": { - "ID": "SMAPI.TrainerMod", - "~ | Status": "Obsolete", - "~ | StatusReasonPhrase": "replaced by ConsoleCommands, which is added by the SMAPI installer." - }, - - "Tree Transplant": { - "ID": "TreeTransplant", - "Default | UpdateKey": "Nexus:1342" - }, - - "UI Info Suite": { - "ID": "Cdaragorn.UiInfoSuite", - "Default | UpdateKey": "Nexus:1150" - }, - - "UiModSuite": { - "ID": "Demiacle.UiModSuite", - "MapLocalVersions": { "0.5": "1.0" }, // not updated in manifest - "Default | UpdateKey": "Nexus:1023", - "~1.0 | Status": "AssumeBroken" // broke in SDV 1.2 - }, - "Variable Grass": { "ID": "dantheman999.VariableGrass", "Default | UpdateKey": "GitHub:dantheman999301/StardewMods" }, - "Vertical Toolbar": { - "ID": "SB_VerticalToolMenu", - "Default | UpdateKey": "Nexus:943" - }, - - "WarpAnimals": { - "ID": "Symen.WarpAnimals", - "Default | UpdateKey": "Nexus:1400" - }, - - "What Farm Cave / WhatAMush": { - "ID": "WhatAMush", - "Default | UpdateKey": "Nexus:1097" - }, - - "WHats Up": { - "ID": "wHatsUp", - "Default | UpdateKey": "Nexus:1082" - }, - - "Winter Grass": { - "ID": "cat.wintergrass", - "Default | UpdateKey": "Nexus:1601" - }, - - "Xnb Loader": { - "ID": "Entoarox.XnbLoader", - "~1.1.10 | UpdateKey": "Chucklefish:4506" // only enable update checks up to 1.1.10 by request (has its own update-check feature) - }, - - "zDailyIncrease": { - "ID": "zdailyincrease", - "MapRemoteVersions": { "1.3.5": "1.3.4" }, // not updated in manifest - "Default | UpdateKey": "Chucklefish:4247" - }, - "Zoom Out Extreme": { "ID": "RockinMods.ZoomMod", "FormerIDs": "ZoomMod", // changed circa 1.2.1 - "Default | UpdateKey": "Nexus:1326", - "~0.1 | Status": "AssumeBroken" // broke in SDV 1.2 - }, - - "Zoryn's Better RNG": { - "ID": "Zoryn.BetterRNG", - "FormerIDs": "76b6d1e1-f7ba-4d72-8c32-5a1e6d2716f6", // changed in 1.6 - "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods", - "~1.6 | Status": "AssumeBroken" // broke in SDV 1.2 - }, - - "Zoryn's Calendar Anywhere": { - "ID": "Zoryn.CalendarAnywhere", - "FormerIDs": "a41c01cd-0437-43eb-944f-78cb5a53002a", // changed in 1.6 - "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods", - "~1.6 | Status": "AssumeBroken" // broke in SDV 1.2 - }, - - "Zoryn's Durable Fences": { - "ID": "Zoryn.DurableFences", - "FormerIDs": "56d3439c-7b9b-497e-9496-0c4890e8a00e", // changed in 1.6 - "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods" - }, - - "Zoryn's Health Bars": { - "ID": "Zoryn.HealthBars", - "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods", - "~1.6 | Status": "AssumeBroken" // broke in SDV 1.2 - }, - - "Zoryn's Fishing Mod": { - "ID": "Zoryn.FishingMod", - "FormerIDs": "fa277b1f-265e-47c3-a84f-cd320cc74949", // changed in 1.6 - "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods" - }, - - "Zoryn's Junimo Deposit Anywhere": { - "ID": "Zoryn.JunimoDepositAnywhere", - "FormerIDs": "f93a4fe8-cade-4146-9335-b5f82fbbf7bc", // changed in 1.6 - "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods", - "~1.7 | Status": "AssumeBroken" // broke in SDV 1.2 - }, - - "Zoryn's Movement Mod": { - "ID": "Zoryn.MovementModifier", - "FormerIDs": "8a632929-8335-484f-87dd-c29d2ba3215d", // changed in 1.6 - "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods" - }, - - "Zoryn's Regen Mod": { - "ID": "Zoryn.RegenMod", - "FormerIDs": "dfac4383-1b6b-4f33-ae4e-37fc23e5252e", // changed in 1.6 - "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods", - "~1.6 | Status": "AssumeBroken" // broke in SDV 1.2 + "~0.1 | Status": "AssumeBroken" // broke in SDV 1.2 } } } |