diff options
23 files changed, 240 insertions, 24 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md index a343cff3..df832c34 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,6 +8,7 @@ * Added friendly error when game can't start audio. * Added console warning for mods which don't have update checks configured. * Improved how mod warnings are shown in the console. + * Fixed `SEHException` errors and performance issues in some cases. * Fixed console color scheme on Mac or in PowerShell, configurable via `StardewModdingAPI.config.json`. * Fixed installer error on Linux/Mac in some cases. * Fixed installer not finding some game paths. @@ -46,6 +47,7 @@ * Fixed some common non-mod build output being included in release zip. * Fixed mods able to intercept other mods' assets via the internal asset keys. * Fixed mods able to indirectly change other mods' data through shared content caches. + * Fixed `SemanticVersion` allowing invalid versions in some cases. * **Breaking changes** (see [migration guide](https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.3)): * Dropped some deprecated APIs. * `LocationEvents` have been rewritten. @@ -58,9 +60,10 @@ * Fixed `world_setseason` not normalising the season value. * For the web UI: - * Redesigned log parser upload page to make it more intuitive for new players. - * Changed log parser filters to show `DEBUG` messages by default. + * Improved log parser design to make it more intuitive. * Improved layout on small screens. + * Added option to download from Nexus. + * Changed log parser filters to show `DEBUG` messages by default. * Fixed log parser issue when content packs have no description. * Fixed log parser mangling crossplatform paths in some cases. diff --git a/src/SMAPI.Installer/readme.txt b/src/SMAPI.Installer/readme.txt index 12053a8c..2ee5473c 100644 --- a/src/SMAPI.Installer/readme.txt +++ b/src/SMAPI.Installer/readme.txt @@ -16,7 +16,7 @@ SMAPI lets you run Stardew Valley with mods. Don't forget to download mods separ Player's guide -------------------------------- -See https://stardewvalleywiki.com/Modding:Player_Guide. +See https://stardewvalleywiki.com/Modding:Player_Guide Manual install @@ -24,7 +24,7 @@ Manual install THIS IS NOT RECOMMENDED FOR MOST PLAYERS. See instructions above instead. If you really want to install SMAPI manually, here's how. -1. Download the latest version of SMAPI: https://github.com/Pathoschild/SMAPI/releases. +1. Download the latest version of SMAPI: https://github.com/Pathoschild/SMAPI/releases 2. Unzip the .zip file somewhere (not in your game folder). 3. Copy the files from the "internal/Windows" folder (on Windows) or "internal/Mono" folder (on Linux/Mac) into your game folder. The `StardewModdingAPI.exe` file should be right next to the diff --git a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs index feab452a..35d74b60 100644 --- a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs +++ b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs @@ -22,6 +22,7 @@ namespace StardewModdingAPI.Tests.Utilities [TestCase("3000.4000.5000", ExpectedResult = "3000.4000.5000")] [TestCase("1.2-some-tag.4", ExpectedResult = "1.2-some-tag.4")] [TestCase("1.2.3-some-tag.4", ExpectedResult = "1.2.3-some-tag.4")] + [TestCase("1.2.3-SoME-tAg.4", ExpectedResult = "1.2.3-SoME-tAg.4")] [TestCase("1.2.3-some-tag.4 ", ExpectedResult = "1.2.3-some-tag.4")] public string Constructor_FromString(string input) { @@ -35,6 +36,7 @@ namespace StardewModdingAPI.Tests.Utilities [TestCase(1, 2, 3, " ", ExpectedResult = "1.2.3")] [TestCase(1, 2, 3, "0", ExpectedResult = "1.2.3-0")] [TestCase(1, 2, 3, "some-tag.4", ExpectedResult = "1.2.3-some-tag.4")] + [TestCase(1, 2, 3, "sOMe-TaG.4", ExpectedResult = "1.2.3-sOMe-TaG.4")] [TestCase(1, 2, 3, "some-tag.4 ", ExpectedResult = "1.2.3-some-tag.4")] public string Constructor_FromParts(int major, int minor, int patch, string tag) { @@ -49,6 +51,19 @@ namespace StardewModdingAPI.Tests.Utilities return version.ToString(); } + [Test(Description = "Assert that the constructor throws the expected exception for invalid versions when constructed from the individual numbers.")] + [TestCase(0, 0, 0, null)] + [TestCase(-1, 0, 0, null)] + [TestCase(0, -1, 0, null)] + [TestCase(0, 0, -1, null)] + [TestCase(1, 0, 0, "-tag")] + [TestCase(1, 0, 0, "tag spaces")] + [TestCase(1, 0, 0, "tag~")] + public void Constructor_FromParts_WithInvalidValues(int major, int minor, int patch, string tag) + { + this.AssertAndLogException<FormatException>(() => new SemanticVersion(major, minor, patch, tag)); + } + [Test(Description = "Assert that the constructor sets the expected values for all valid versions when constructed from an assembly version.")] [TestCase(1, 0, 0, ExpectedResult = "1.0")] [TestCase(1, 2, 3, ExpectedResult = "1.2.3")] @@ -79,6 +94,7 @@ namespace StardewModdingAPI.Tests.Utilities [TestCase("1.2.3.apple")] [TestCase("1..2..3")] [TestCase("1.2.3-")] + [TestCase("1.2.3--some-tag")] [TestCase("1.2.3-some-tag...")] [TestCase("1.2.3-some-tag...4")] [TestCase("apple")] diff --git a/src/SMAPI.Web/Views/Index/Index.cshtml b/src/SMAPI.Web/Views/Index/Index.cshtml index 6dc0a338..411448fa 100644 --- a/src/SMAPI.Web/Views/Index/Index.cshtml +++ b/src/SMAPI.Web/Views/Index/Index.cshtml @@ -3,9 +3,9 @@ } @model StardewModdingAPI.Web.ViewModels.IndexModel @section Head { - <link rel="stylesheet" href="~/Content/css/index.css" /> + <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="~/Content/js/index.js"></script> + <script src="~/Content/js/index.js?r=20180615"></script> } <p id="blurb"> @@ -15,16 +15,29 @@ </p> <div id="call-to-action"> - <a href="@Model.StableVersion.DownloadUrl" class="main-cta download">Download SMAPI @Model.StableVersion.Version</a><br /> + <div class="cta-dropdown"> + <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> + </div> + </div><br /> + @if (Model.BetaVersion != null) { - <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 /> + <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/> + <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 /> - <img id="pufferchick" src="favicon.ico" /> + <img id="pufferchick" src="Content/images/pufferchick.png" /> </div> -<h2>Get help</h2> +<h2 id="help">Get help</h2> <ul> <li><a href="https://stardewvalleywiki.com/Modding:SMAPI_compatibility">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> @@ -32,7 +45,7 @@ @if (Model.BetaVersion == null) { - <h2>What's new in SMAPI @Model.StableVersion.Version?</h2> + <h2 id="whatsnew">What's new in SMAPI @Model.StableVersion.Version?</h2> <div class="github-description"> @Html.Raw(Markdig.Markdown.ToHtml(Model.StableVersion.Description)) </div> @@ -40,7 +53,7 @@ } else { - <h2>What's new in...</h2> + <h2 id="whatsnew">What's new in...</h2> <h3>SMAPI @Model.StableVersion.Version?</h3> <div class="github-description"> @Html.Raw(Markdig.Markdown.ToHtml(Model.StableVersion.Description)) @@ -54,7 +67,7 @@ else <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> } -<h2>Donate to support SMAPI ♥</h2> +<h2 id="donate">Donate to support SMAPI ♥</h2> <p> SMAPI is an open-source project by Pathoschild. It will always be free, but donations are much appreciated to help pay for development, server hosting, domain fees, coffee, etc. @@ -85,7 +98,7 @@ else and a few anonymous users for their ongoing support; you're awesome! 🏅 </p> -<h2>For mod creators</h2> +<h2 id="modcreators">For mod creators</h2> <ul> <li><a href="@Model.StableVersion.DevDownloadUrl">SMAPI @Model.StableVersion.Version for developers</a> (includes <a href="https://docs.microsoft.com/en-us/visualstudio/ide/using-intellisense">intellisense</a> and full console output)</li> @if (Model.BetaVersion != null) diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index 7307817c..4d95901e 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -12,10 +12,10 @@ { <meta name="robots" content="noindex" /> } - <link rel="stylesheet" href="~/Content/css/log-parser.css?r=20180603" /> + <link rel="stylesheet" href="~/Content/css/log-parser.css?r=20180611" /> <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="~/Content/js/log-parser.js?r=20180603"></script> + <script src="~/Content/js/log-parser.js?r=20180611"></script> <script> $(function() { smapi.logParser({ diff --git a/src/SMAPI.Web/wwwroot/Content/css/index.css b/src/SMAPI.Web/wwwroot/Content/css/index.css index 6340ed87..514e1a5c 100644 --- a/src/SMAPI.Web/wwwroot/Content/css/index.css +++ b/src/SMAPI.Web/wwwroot/Content/css/index.css @@ -18,7 +18,8 @@ h1 { text-align: center; } -#call-to-action a { +#call-to-action a.main-cta, +#call-to-action a.secondary-cta { box-shadow: #caefab 0 1px 0 0 inset; background: linear-gradient(#77d42a 5%, #5cb811 100%) #77d42a; border-radius: 6px; @@ -40,6 +41,58 @@ h1 { text-shadow: #2b665e 0 1px 0; } +.cta-dropdown { + position: relative; + display: inline-block; + margin-bottom: 1em; +} + +.cta-dropdown a.download { + margin-bottom: 0 !important; +} + +.cta-dropdown .dropdown-content { + display: none; + position: absolute; + text-align: left; + box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2); + border: 1px solid #566963; + background: #5cb811; + border-top: 0; + border-radius: 0 0 6px 6px; + margin-top: -6px; + z-index: 1; +} + +.cta-dropdown .dropdown-content a:hover { + background-color: #ddd; +} + +.cta-dropdown .dropdown-content img { + width: 0.85em; + height: 0.85em; +} + +.cta-dropdown.secondary-cta-dropdown .dropdown-content a:hover { + background-color: #566963; +} + +.cta-dropdown.secondary-cta-dropdown .dropdown-content { + background-color: #768d87; + border-color: #566963; +} + +.cta-dropdown.secondary-cta-dropdown .dropdown-content a { + color: #fff; + text-shadow: #2b665e 0 1px 0; +} + +.cta-dropdown .dropdown-content a { + padding: 0.75em 1em; + text-decoration: none; + display: block; +} + /********* ** Subsections *********/ diff --git a/src/SMAPI.Web/wwwroot/Content/css/log-parser.css b/src/SMAPI.Web/wwwroot/Content/css/log-parser.css index 5c4f8aea..09bb97f5 100644 --- a/src/SMAPI.Web/wwwroot/Content/css/log-parser.css +++ b/src/SMAPI.Web/wwwroot/Content/css/log-parser.css @@ -1,6 +1,10 @@ /********* ** Main layout *********/ +#content { + max-width: 100%; +} + caption { text-align: left; padding-top: 2px; diff --git a/src/SMAPI.Web/wwwroot/Content/images/direct-download-icon.png b/src/SMAPI.Web/wwwroot/Content/images/direct-download-icon.png Binary files differnew file mode 100644 index 00000000..6c30ca36 --- /dev/null +++ b/src/SMAPI.Web/wwwroot/Content/images/direct-download-icon.png diff --git a/src/SMAPI.Web/wwwroot/Content/images/nexus-icon.png b/src/SMAPI.Web/wwwroot/Content/images/nexus-icon.png Binary files differnew file mode 100644 index 00000000..10c66712 --- /dev/null +++ b/src/SMAPI.Web/wwwroot/Content/images/nexus-icon.png diff --git a/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.png b/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.png Binary files differindex 63eb8970..f359146c 100644 --- a/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.png +++ b/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.png diff --git a/src/SMAPI.Web/wwwroot/Content/images/pufferchick.png b/src/SMAPI.Web/wwwroot/Content/images/pufferchick.png Binary files differnew file mode 100644 index 00000000..1de9cf47 --- /dev/null +++ b/src/SMAPI.Web/wwwroot/Content/images/pufferchick.png diff --git a/src/SMAPI.Web/wwwroot/Content/js/index.js b/src/SMAPI.Web/wwwroot/Content/js/index.js index 016d5fa4..d0734b02 100644 --- a/src/SMAPI.Web/wwwroot/Content/js/index.js +++ b/src/SMAPI.Web/wwwroot/Content/js/index.js @@ -1,11 +1,34 @@ $(document).ready(function () { + /* enable pufferchick */ var pufferchick = $("#pufferchick"); - $(".download").hover( + $(".cta-dropdown").hover( function () { pufferchick.attr("src", "Content/images/pufferchick-cool.png"); }, function () { - pufferchick.attr("src", "favicon.ico"); + pufferchick.attr("src", "Content/images/pufferchick.png"); } ); + + /* enable download dropdowns */ + $(".cta-dropdown a.download").each(function(i, button) { + button = $(button); + var wrapper = button.parent(".cta-dropdown"); + var button = wrapper.find(".download"); + var dropdownContent = wrapper.find(".dropdown-content"); + + $(window).on("click", function(e) { + var target = $(e.target); + + // toggle dropdown on button click + if (target.is(button) || $.contains(button.get(0), target.get(0))) { + e.preventDefault(); + dropdownContent.toggle(); + } + + // else hide dropdown + else + dropdownContent.hide(); + }); + }); }); diff --git a/src/SMAPI/Events/IWorldEvents.cs b/src/SMAPI/Events/IWorldEvents.cs index 067a79bc..d4efb53b 100644 --- a/src/SMAPI/Events/IWorldEvents.cs +++ b/src/SMAPI/Events/IWorldEvents.cs @@ -11,6 +11,9 @@ namespace StardewModdingAPI.Events /// <summary>Raised after buildings are added or removed in a location.</summary> event EventHandler<WorldBuildingListChangedEventArgs> BuildingListChanged; + /// <summary>Raised after debris are added or removed in a location.</summary> + event EventHandler<WorldDebrisListChangedEventArgs> DebrisListChanged; + /// <summary>Raised after large terrain features (like bushes) are added or removed in a location.</summary> event EventHandler<WorldLargeTerrainFeatureListChangedEventArgs> LargeTerrainFeatureListChanged; diff --git a/src/SMAPI/Events/WorldDebrisListChangedEventArgs.cs b/src/SMAPI/Events/WorldDebrisListChangedEventArgs.cs new file mode 100644 index 00000000..aad9c24d --- /dev/null +++ b/src/SMAPI/Events/WorldDebrisListChangedEventArgs.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using StardewValley; + +namespace StardewModdingAPI.Events +{ + /// <summary>Event arguments for a <see cref="IWorldEvents.DebrisListChanged"/> event.</summary> + public class WorldDebrisListChangedEventArgs : EventArgs + { + /********* + ** Accessors + *********/ + /// <summary>The location which changed.</summary> + public GameLocation Location { get; } + + /// <summary>The debris added to the location.</summary> + public IEnumerable<Debris> Added { get; } + + /// <summary>The debris removed from the location.</summary> + public IEnumerable<Debris> Removed { get; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="location">The location which changed.</param> + /// <param name="added">The debris added to the location.</param> + /// <param name="removed">The debris removed from the location.</param> + public WorldDebrisListChangedEventArgs(GameLocation location, IEnumerable<Debris> added, IEnumerable<Debris> removed) + { + this.Location = location; + this.Added = added.ToArray(); + this.Removed = removed.ToArray(); + } + } +} diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index 9f67244a..b05d82ce 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -20,6 +20,9 @@ namespace StardewModdingAPI.Framework.Events /// <summary>Raised after buildings are added or removed in a location.</summary> public readonly ManagedEvent<WorldBuildingListChangedEventArgs> World_BuildingListChanged; + /// <summary>Raised after debris are added or removed in a location.</summary> + public readonly ManagedEvent<WorldDebrisListChangedEventArgs> World_DebrisListChanged; + /// <summary>Raised after large terrain features (like bushes) are added or removed in a location.</summary> public readonly ManagedEvent<WorldLargeTerrainFeatureListChangedEventArgs> World_LargeTerrainFeatureListChanged; @@ -255,6 +258,7 @@ namespace StardewModdingAPI.Framework.Events this.Input_MouseWheelScrolled = ManageEventOf<InputMouseWheelScrolledEventArgs>(nameof(IModEvents.Input), nameof(IInputEvents.MouseWheelScrolled)); this.World_BuildingListChanged = ManageEventOf<WorldBuildingListChangedEventArgs>(nameof(IModEvents.World), nameof(IWorldEvents.LocationListChanged)); + this.World_DebrisListChanged = ManageEventOf<WorldDebrisListChangedEventArgs>(nameof(IModEvents.World), nameof(IWorldEvents.DebrisListChanged)); this.World_LargeTerrainFeatureListChanged = ManageEventOf<WorldLargeTerrainFeatureListChangedEventArgs>(nameof(IModEvents.World), nameof(IWorldEvents.LargeTerrainFeatureListChanged)); this.World_LocationListChanged = ManageEventOf<WorldLocationListChangedEventArgs>(nameof(IModEvents.World), nameof(IWorldEvents.BuildingListChanged)); this.World_NpcListChanged = ManageEventOf<WorldNpcListChangedEventArgs>(nameof(IModEvents.World), nameof(IWorldEvents.NpcListChanged)); diff --git a/src/SMAPI/Framework/Events/ModWorldEvents.cs b/src/SMAPI/Framework/Events/ModWorldEvents.cs index e1a53e0c..dc9c0f4c 100644 --- a/src/SMAPI/Framework/Events/ModWorldEvents.cs +++ b/src/SMAPI/Framework/Events/ModWorldEvents.cs @@ -23,6 +23,13 @@ namespace StardewModdingAPI.Framework.Events remove => this.EventManager.World_BuildingListChanged.Remove(value); } + /// <summary>Raised after debris are added or removed in a location.</summary> + public event EventHandler<WorldDebrisListChangedEventArgs> DebrisListChanged + { + add => this.EventManager.World_DebrisListChanged.Add(value, this.Mod); + remove => this.EventManager.World_DebrisListChanged.Remove(value); + } + /// <summary>Raised after large terrain features (like bushes) are added or removed in a location.</summary> public event EventHandler<WorldLargeTerrainFeatureListChangedEventArgs> LargeTerrainFeatureListChanged { diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index 588d30c8..984c1f57 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -536,6 +536,17 @@ namespace StardewModdingAPI.Framework this.Events.Legacy_Location_BuildingsChanged.Raise(new EventArgsLocationBuildingsChanged(location, added, removed)); } + // debris changed + if (watcher.DebrisWatcher.IsChanged) + { + GameLocation location = watcher.Location; + Debris[] added = watcher.DebrisWatcher.Added.ToArray(); + Debris[] removed = watcher.DebrisWatcher.Removed.ToArray(); + watcher.DebrisWatcher.Reset(); + + this.Events.World_DebrisListChanged.Raise(new WorldDebrisListChangedEventArgs(location, added, removed)); + } + // large terrain features changed if (watcher.LargeTerrainFeaturesWatcher.IsChanged) { diff --git a/src/SMAPI/Framework/StateTracking/LocationTracker.cs b/src/SMAPI/Framework/StateTracking/LocationTracker.cs index 1b4c0b19..708c0716 100644 --- a/src/SMAPI/Framework/StateTracking/LocationTracker.cs +++ b/src/SMAPI/Framework/StateTracking/LocationTracker.cs @@ -33,6 +33,9 @@ namespace StardewModdingAPI.Framework.StateTracking /// <summary>Tracks added or removed buildings.</summary> public ICollectionWatcher<Building> BuildingsWatcher { get; } + /// <summary>Tracks added or removed debris.</summary> + public ICollectionWatcher<Debris> DebrisWatcher { get; } + /// <summary>Tracks added or removed large terrain features.</summary> public ICollectionWatcher<LargeTerrainFeature> LargeTerrainFeaturesWatcher { get; } @@ -59,6 +62,7 @@ namespace StardewModdingAPI.Framework.StateTracking this.BuildingsWatcher = location is BuildableGameLocation buildableLocation ? WatcherFactory.ForNetCollection(buildableLocation.buildings) : (ICollectionWatcher<Building>)WatcherFactory.ForObservableCollection(new ObservableCollection<Building>()); + this.DebrisWatcher = WatcherFactory.ForNetCollection(location.debris); this.LargeTerrainFeaturesWatcher = WatcherFactory.ForNetCollection(location.largeTerrainFeatures); this.NpcsWatcher = WatcherFactory.ForNetCollection(location.characters); this.ObjectsWatcher = WatcherFactory.ForNetDictionary(location.netObjects); @@ -67,6 +71,7 @@ namespace StardewModdingAPI.Framework.StateTracking this.Watchers.AddRange(new IWatcher[] { this.BuildingsWatcher, + this.DebrisWatcher, this.LargeTerrainFeaturesWatcher, this.NpcsWatcher, this.ObjectsWatcher, diff --git a/src/SMAPI/Framework/WatcherCore.cs b/src/SMAPI/Framework/WatcherCore.cs index 64b063cf..e06423b9 100644 --- a/src/SMAPI/Framework/WatcherCore.cs +++ b/src/SMAPI/Framework/WatcherCore.cs @@ -13,7 +13,7 @@ namespace StardewModdingAPI.Framework internal class WatcherCore { /********* - ** Public methods + ** Properties *********/ /// <summary>The underlying watchers for convenience. These are accessible individually as separate properties.</summary> private readonly List<IWatcher> Watchers = new List<IWatcher>(); diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj index 67c48a57..fcd54c34 100644 --- a/src/SMAPI/StardewModdingAPI.csproj +++ b/src/SMAPI/StardewModdingAPI.csproj @@ -27,6 +27,7 @@ <ApplicationVersion>1.0.0.%2a</ApplicationVersion> <UseApplicationTrust>false</UseApplicationTrust> <BootstrapperEnabled>true</BootstrapperEnabled> + <LargeAddressAware Condition="'$(OS)' == 'Windows_NT'">true</LargeAddressAware> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> <PlatformTarget>x86</PlatformTarget> @@ -97,6 +98,7 @@ <Compile Include="Events\IInputEvents.cs" /> <Compile Include="Events\IWorldEvents.cs" /> <Compile Include="Events\MultiplayerEvents.cs" /> + <Compile Include="Events\WorldDebrisListChangedEventArgs.cs" /> <Compile Include="Events\WorldNpcListChangedEventArgs.cs" /> <Compile Include="Events\WorldLargeTerrainFeatureListChangedEventArgs.cs" /> <Compile Include="Events\WorldTerrainFeatureListChangedEventArgs.cs" /> @@ -343,4 +345,11 @@ <Import Project="..\SMAPI.Internal\SMAPI.Internal.projitems" Label="Shared" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="..\..\build\common.targets" /> + <Import Project="..\packages\LargeAddressAware.1.0.3\build\LargeAddressAware.targets" Condition="Exists('..\packages\LargeAddressAware.1.0.3\build\LargeAddressAware.targets')" /> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\packages\LargeAddressAware.1.0.3\build\LargeAddressAware.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\LargeAddressAware.1.0.3\build\LargeAddressAware.targets'))" /> + </Target> </Project>
\ No newline at end of file diff --git a/src/SMAPI/StardewModdingAPI.metadata.json b/src/SMAPI/StardewModdingAPI.metadata.json index adf5fdd1..8ac5c734 100644 --- a/src/SMAPI/StardewModdingAPI.metadata.json +++ b/src/SMAPI/StardewModdingAPI.metadata.json @@ -168,8 +168,8 @@ "Automate": { "ID": "Pathoschild.Automate", - "Default | UpdateKey": "Nexus:1063", - "~1.9.1 | Status": "AssumeBroken" // broke in SDV 1.3 + "Default | UpdateKey": "Nexus:1063", + "~1.10-beta.7 | Status": "AssumeBroken" // broke in SDV 1.3.20 }, "Automated Doors": { @@ -685,7 +685,8 @@ "Fishing Adjust": { "ID": "shuaiz.FishingAdjustMod", - "Default | UpdateKey": "Nexus:1350" + "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)' }, "Fishing Tuner Redux": { diff --git a/src/SMAPI/packages.config b/src/SMAPI/packages.config index 3e876922..3347b037 100644 --- a/src/SMAPI/packages.config +++ b/src/SMAPI/packages.config @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> + <package id="LargeAddressAware" version="1.0.3" targetFramework="net45" /> <package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" /> <package id="Newtonsoft.Json" version="11.0.2" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/src/StardewModdingAPI.Toolkit/SemanticVersion.cs b/src/StardewModdingAPI.Toolkit/SemanticVersion.cs index bd85f990..de480416 100644 --- a/src/StardewModdingAPI.Toolkit/SemanticVersion.cs +++ b/src/StardewModdingAPI.Toolkit/SemanticVersion.cs @@ -10,8 +10,11 @@ namespace StardewModdingAPI.Toolkit /********* ** Properties *********/ + /// <summary>A regex pattern matching a valid prerelease tag.</summary> + internal const string TagPattern = @"(?>[a-z0-9]+[\-\.]?)+"; + /// <summary>A regex pattern matching a version within a larger string.</summary> - internal const string UnboundedVersionPattern = @"(?>(?<major>0|[1-9]\d*))\.(?>(?<minor>0|[1-9]\d*))(?>(?:\.(?<patch>0|[1-9]\d*))?)(?:-(?<prerelease>(?>[a-z0-9]+[\-\.]?)+))?"; + internal const string UnboundedVersionPattern = @"(?>(?<major>0|[1-9]\d*))\.(?>(?<minor>0|[1-9]\d*))(?>(?:\.(?<patch>0|[1-9]\d*))?)(?:-(?<prerelease>" + SemanticVersion.TagPattern + "))?"; /// <summary>A regular expression matching a semantic version string.</summary> /// <remarks> @@ -54,6 +57,8 @@ namespace StardewModdingAPI.Toolkit this.Minor = minor; this.Patch = patch; this.Tag = this.GetNormalisedTag(tag); + + this.AssertValid(); } /// <summary>Construct an instance.</summary> @@ -67,6 +72,8 @@ namespace StardewModdingAPI.Toolkit this.Major = version.Major; this.Minor = version.Minor; this.Patch = version.Build; + + this.AssertValid(); } /// <summary>Construct an instance.</summary> @@ -87,6 +94,8 @@ namespace StardewModdingAPI.Toolkit this.Minor = match.Groups["minor"].Success ? int.Parse(match.Groups["minor"].Value) : 0; this.Patch = match.Groups["patch"].Success ? int.Parse(match.Groups["patch"].Value) : 0; this.Tag = match.Groups["prerelease"].Success ? this.GetNormalisedTag(match.Groups["prerelease"].Value) : null; + + this.AssertValid(); } /// <summary>Get an integer indicating whether this version precedes (less than 0), supercedes (more than 0), or is equivalent to (0) the specified version.</summary> @@ -235,5 +244,21 @@ namespace StardewModdingAPI.Toolkit // fallback (this should never happen) return string.Compare(this.ToString(), new SemanticVersion(otherMajor, otherMinor, otherPatch, otherTag).ToString(), StringComparison.InvariantCultureIgnoreCase); } + + /// <summary>Assert that the current version is valid.</summary> + private void AssertValid() + { + if (this.Major < 0 || this.Minor < 0 || this.Patch < 0) + throw new FormatException($"{this} isn't a valid semantic version. The major, minor, and patch numbers can't be negative."); + if (this.Major == 0 && this.Minor == 0 && this.Patch == 0) + throw new FormatException($"{this} isn't a valid semantic version. At least one of the major, minor, and patch numbers must be more than zero."); + if (this.Tag != null) + { + if (this.Tag.Trim() == "") + throw new FormatException($"{this} isn't a valid semantic version. The tag cannot be a blank string (but may be omitted)."); + if (!Regex.IsMatch(this.Tag, $"^{SemanticVersion.TagPattern}$", RegexOptions.IgnoreCase)) + throw new FormatException($"{this} isn't a valid semantic version. The tag is invalid."); + } + } } } |