summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2018-12-29 20:09:33 -0500
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2018-12-29 20:09:33 -0500
commitf046091fe637963fd6a8cc8c1324daf81b64899f (patch)
treeadeffec4a5d31503548ef5dead7d67b3bff9e694 /src
parent82beefd8531467de318c1881afd15a258d489f37 (diff)
parentca18a2867b457fd6bfda71d9828884032ecadfb8 (diff)
downloadSMAPI-f046091fe637963fd6a8cc8c1324daf81b64899f.tar.gz
SMAPI-f046091fe637963fd6a8cc8c1324daf81b64899f.tar.bz2
SMAPI-f046091fe637963fd6a8cc8c1324daf81b64899f.zip
Merge branch 'develop' into stable
Diffstat (limited to 'src')
-rw-r--r--src/SMAPI.Installer/InteractiveInstaller.cs9
-rw-r--r--src/SMAPI.Installer/Program.cs2
-rw-r--r--src/SMAPI.Installer/StardewModdingAPI.Installer.csproj1
-rw-r--r--src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs2
-rw-r--r--src/SMAPI.Internal/EnvironmentUtility.cs2
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs2
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs2
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/SMAPI.ModBuildConfig.Analyzer.Tests.csproj1
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs2
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs2
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/StardewModdingAPI.ModBuildConfig.Analyzer.csproj1
-rw-r--r--src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs2
-rw-r--r--src/SMAPI.ModBuildConfig/StardewModdingAPI.ModBuildConfig.csproj1
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs246
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/ModEntry.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/manifest.json4
-rw-r--r--src/SMAPI.Mods.SaveBackup/ModEntry.cs2
-rw-r--r--src/SMAPI.Mods.SaveBackup/StardewModdingAPI.Mods.SaveBackup.csproj1
-rw-r--r--src/SMAPI.Mods.SaveBackup/manifest.json4
-rw-r--r--src/SMAPI.Tests/Sample.cs6
-rw-r--r--src/SMAPI.Tests/StardewModdingAPI.Tests.csproj1
-rw-r--r--src/SMAPI.Tests/Utilities/SDateTests.cs2
-rw-r--r--src/SMAPI.Web/Controllers/IndexController.cs2
-rw-r--r--src/SMAPI.Web/Controllers/LogParserController.cs2
-rw-r--r--src/SMAPI.Web/Controllers/ModsApiController.cs2
-rw-r--r--src/SMAPI.Web/Controllers/ModsController.cs12
-rw-r--r--src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs2
-rw-r--r--src/SMAPI.Web/Framework/BeanstalkEnvPropsConfigProvider.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/Nexus/NexusWebScrapeClient.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs2
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/ModCompatibilityListConfig.cs12
-rw-r--r--src/SMAPI.Web/Framework/LogParsing/LogParser.cs2
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs2
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs2
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/ModDropRepository.cs2
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs2
-rw-r--r--src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs2
-rw-r--r--src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs2
-rw-r--r--src/SMAPI.Web/StardewModdingAPI.Web.csproj1
-rw-r--r--src/SMAPI.Web/Startup.cs1
-rw-r--r--src/SMAPI.Web/ViewModels/LogParserModel.cs2
-rw-r--r--src/SMAPI.Web/appsettings.json4
-rw-r--r--src/SMAPI.sln.DotSettings1
-rw-r--r--src/SMAPI/Constants.cs4
-rw-r--r--src/SMAPI/Enums/LoadStage.cs36
-rw-r--r--src/SMAPI/Events/BuildingListChangedEventArgs.cs3
-rw-r--r--src/SMAPI/Events/ButtonPressedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/ButtonReleasedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/ContentEvents.cs2
-rw-r--r--src/SMAPI/Events/ControlEvents.cs2
-rw-r--r--src/SMAPI/Events/DebrisListChangedEventArgs.cs3
-rw-r--r--src/SMAPI/Events/EventArgsInput.cs2
-rw-r--r--src/SMAPI/Events/GameEvents.cs2
-rw-r--r--src/SMAPI/Events/GraphicsEvents.cs2
-rw-r--r--src/SMAPI/Events/IGameLoopEvents.cs8
-rw-r--r--src/SMAPI/Events/ISpecialisedEvents.cs3
-rw-r--r--src/SMAPI/Events/InputEvents.cs2
-rw-r--r--src/SMAPI/Events/LargeTerrainFeatureListChangedEventArgs.cs3
-rw-r--r--src/SMAPI/Events/LoadStageChangedEventArgs.cs31
-rw-r--r--src/SMAPI/Events/LocationEvents.cs2
-rw-r--r--src/SMAPI/Events/MenuEvents.cs2
-rw-r--r--src/SMAPI/Events/MineEvents.cs2
-rw-r--r--src/SMAPI/Events/ModMessageReceivedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/MultiplayerEvents.cs2
-rw-r--r--src/SMAPI/Events/NpcListChangedEventArgs.cs3
-rw-r--r--src/SMAPI/Events/ObjectListChangedEventArgs.cs3
-rw-r--r--src/SMAPI/Events/OneSecondUpdateTickedEventArgs.cs26
-rw-r--r--src/SMAPI/Events/OneSecondUpdateTickingEventArgs.cs26
-rw-r--r--src/SMAPI/Events/PlayerEvents.cs2
-rw-r--r--src/SMAPI/Events/SaveEvents.cs2
-rw-r--r--src/SMAPI/Events/SpecialisedEvents.cs2
-rw-r--r--src/SMAPI/Events/TerrainFeatureListChangedEventArgs.cs3
-rw-r--r--src/SMAPI/Events/TimeEvents.cs2
-rw-r--r--src/SMAPI/Events/UnvalidatedUpdateTickedEventArgs.cs13
-rw-r--r--src/SMAPI/Events/UnvalidatedUpdateTickingEventArgs.cs13
-rw-r--r--src/SMAPI/Events/UpdateTickedEventArgs.cs13
-rw-r--r--src/SMAPI/Events/UpdateTickingEventArgs.cs13
-rw-r--r--src/SMAPI/Framework/CommandManager.cs2
-rw-r--r--src/SMAPI/Framework/Content/AssetData.cs2
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForDictionary.cs6
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForImage.cs2
-rw-r--r--src/SMAPI/Framework/Content/AssetInfo.cs2
-rw-r--r--src/SMAPI/Framework/Content/ContentCache.cs2
-rw-r--r--src/SMAPI/Framework/ContentCoordinator.cs2
-rw-r--r--src/SMAPI/Framework/ContentManagers/BaseContentManager.cs2
-rw-r--r--src/SMAPI/Framework/ContentManagers/GameContentManager.cs2
-rw-r--r--src/SMAPI/Framework/ContentManagers/ModContentManager.cs2
-rw-r--r--src/SMAPI/Framework/ContentPack.cs2
-rw-r--r--src/SMAPI/Framework/DeprecationManager.cs10
-rw-r--r--src/SMAPI/Framework/Events/EventManager.cs14
-rw-r--r--src/SMAPI/Framework/Events/ManagedEvent.cs6
-rw-r--r--src/SMAPI/Framework/Events/ManagedEventBase.cs2
-rw-r--r--src/SMAPI/Framework/Events/ModEventsBase.cs2
-rw-r--r--src/SMAPI/Framework/Events/ModGameLoopEvents.cs16
-rw-r--r--src/SMAPI/Framework/Events/ModSpecialisedEvents.cs7
-rw-r--r--src/SMAPI/Framework/Input/GamePadStateBuilder.cs2
-rw-r--r--src/SMAPI/Framework/Logging/ConsoleInterceptionManager.cs2
-rw-r--r--src/SMAPI/Framework/Logging/LogFileManager.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/CommandHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/ContentHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/DataHelper.cs10
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/TranslationHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyLoader.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/ModResolver.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/RewriteHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/FieldToPropertyRewriter.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/StaticFieldToConstantRewriter.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs2
-rw-r--r--src/SMAPI/Framework/ModRegistry.cs2
-rw-r--r--src/SMAPI/Framework/Monitor.cs2
-rw-r--r--src/SMAPI/Framework/Networking/MultiplayerPeer.cs2
-rw-r--r--src/SMAPI/Framework/Networking/SGalaxyNetClient.cs2
-rw-r--r--src/SMAPI/Framework/Networking/SGalaxyNetServer.cs2
-rw-r--r--src/SMAPI/Framework/Networking/SLidgrenClient.cs2
-rw-r--r--src/SMAPI/Framework/Networking/SLidgrenServer.cs2
-rw-r--r--src/SMAPI/Framework/Patching/GamePatcher.cs2
-rw-r--r--src/SMAPI/Framework/Reflection/CacheEntry.cs6
-rw-r--r--src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs2
-rw-r--r--src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs2
-rw-r--r--src/SMAPI/Framework/Reflection/ReflectedField.cs2
-rw-r--r--src/SMAPI/Framework/Reflection/ReflectedMethod.cs2
-rw-r--r--src/SMAPI/Framework/Reflection/ReflectedProperty.cs2
-rw-r--r--src/SMAPI/Framework/Reflection/Reflector.cs2
-rw-r--r--src/SMAPI/Framework/SCore.cs17
-rw-r--r--src/SMAPI/Framework/SGame.cs379
-rw-r--r--src/SMAPI/Framework/SModHooks.cs2
-rw-r--r--src/SMAPI/Framework/SMultiplayer.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/BaseDisposableWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/LocationTracker.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/PlayerTracker.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs2
-rw-r--r--src/SMAPI/Framework/WatcherCore.cs2
-rw-r--r--src/SMAPI/Metadata/CoreAssetPropagator.cs2
-rw-r--r--src/SMAPI/Metadata/InstructionMetadata.cs2
-rw-r--r--src/SMAPI/Patches/LoadForNewGamePatch.cs109
-rw-r--r--src/SMAPI/Program.cs2
-rw-r--r--src/SMAPI/SemanticVersion.cs4
-rw-r--r--src/SMAPI/StardewModdingAPI.csproj6
-rw-r--r--src/SMAPI/Translation.cs2
-rw-r--r--src/SMAPI/Utilities/SDate.cs2
-rw-r--r--src/StardewModdingAPI.Toolkit.CoreInterfaces/StardewModdingAPI.Toolkit.CoreInterfaces.csproj1
-rw-r--r--src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs2
-rw-r--r--src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs2
-rw-r--r--src/StardewModdingAPI.Toolkit/Framework/ModData/ModDatabase.cs2
-rw-r--r--src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs2
-rw-r--r--src/StardewModdingAPI.Toolkit/ModToolkit.cs2
-rw-r--r--src/StardewModdingAPI.Toolkit/SemanticVersion.cs2
-rw-r--r--src/StardewModdingAPI.Toolkit/StardewModdingAPI.Toolkit.csproj1
-rw-r--r--src/StardewModdingAPI.Toolkit/Utilities/PathUtilities.cs2
183 files changed, 988 insertions, 382 deletions
diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs
index d5866c74..7148b1d9 100644
--- a/src/SMAPI.Installer/InteractiveInstaller.cs
+++ b/src/SMAPI.Installer/InteractiveInstaller.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -13,6 +12,9 @@ using StardewModdingAPI.Internal.ConsoleWriting;
using StardewModdingAPI.Toolkit;
using StardewModdingAPI.Toolkit.Framework.ModScanning;
using StardewModdingAPI.Toolkit.Utilities;
+#if !SMAPI_FOR_WINDOWS
+using System.Diagnostics;
+#endif
namespace StardewModdingApi.Installer
{
@@ -20,7 +22,7 @@ namespace StardewModdingApi.Installer
internal class InteractiveInstaller
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The absolute path to the directory containing the files to copy into the game folder.</summary>
private readonly string BundlePath;
@@ -461,6 +463,8 @@ namespace StardewModdingApi.Installer
// mark file executable
// (MSBuild doesn't keep permission flags for files zipped in a build task.)
+ // (Note: exclude from Windows build because antivirus apps can flag the process start code as suspicious.)
+#if !SMAPI_FOR_WINDOWS
new Process
{
StartInfo = new ProcessStartInfo
@@ -470,6 +474,7 @@ namespace StardewModdingApi.Installer
CreateNoWindow = true
}
}.Start();
+#endif
}
// create mods directory (if needed)
diff --git a/src/SMAPI.Installer/Program.cs b/src/SMAPI.Installer/Program.cs
index 0ca5aea0..3c4d8593 100644
--- a/src/SMAPI.Installer/Program.cs
+++ b/src/SMAPI.Installer/Program.cs
@@ -12,7 +12,7 @@ namespace StardewModdingApi.Installer
internal class Program
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The absolute path of the installer folder.</summary>
[SuppressMessage("ReSharper", "AssignNullToNotNullAttribute", Justification = "The assembly location is never null in this context.")]
diff --git a/src/SMAPI.Installer/StardewModdingAPI.Installer.csproj b/src/SMAPI.Installer/StardewModdingAPI.Installer.csproj
index 8000e4e7..083044fb 100644
--- a/src/SMAPI.Installer/StardewModdingAPI.Installer.csproj
+++ b/src/SMAPI.Installer/StardewModdingAPI.Installer.csproj
@@ -12,6 +12,7 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
diff --git a/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs b/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs
index c04cf0e7..cdc729e2 100644
--- a/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs
+++ b/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Internal.ConsoleWriting
internal class ColorfulConsoleWriter
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The console text color for each log level.</summary>
private readonly IDictionary<ConsoleLogLevel, ConsoleColor> Colors;
diff --git a/src/SMAPI.Internal/EnvironmentUtility.cs b/src/SMAPI.Internal/EnvironmentUtility.cs
index a3581898..c4e4678a 100644
--- a/src/SMAPI.Internal/EnvironmentUtility.cs
+++ b/src/SMAPI.Internal/EnvironmentUtility.cs
@@ -12,7 +12,7 @@ namespace StardewModdingAPI.Internal
internal static class EnvironmentUtility
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Get the OS name from the system uname command.</summary>
/// <param name="buffer">The buffer to fill with the resulting string.</param>
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs
index 6f8c8b9b..85a77d15 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs
@@ -11,7 +11,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests
public class NetFieldAnalyzerTests : DiagnosticVerifier
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Sample C# mod code, with a {{test-code}} placeholder for the code in the Entry method to test.</summary>
const string SampleProgram = @"
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs
index 102a80d1..fa9235a3 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs
@@ -11,7 +11,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests
public class ObsoleteFieldAnalyzerTests : DiagnosticVerifier
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Sample C# mod code, with a {{test-code}} placeholder for the code in the Entry method to test.</summary>
const string SampleProgram = @"
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/SMAPI.ModBuildConfig.Analyzer.Tests.csproj b/src/SMAPI.ModBuildConfig.Analyzer.Tests/SMAPI.ModBuildConfig.Analyzer.Tests.csproj
index 4d93df73..26065ec2 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/SMAPI.ModBuildConfig.Analyzer.Tests.csproj
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/SMAPI.ModBuildConfig.Analyzer.Tests.csproj
@@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs b/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs
index e6766e61..f2608348 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs
@@ -14,7 +14,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
public class NetFieldAnalyzer : DiagnosticAnalyzer
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The namespace for Stardew Valley's <c>Netcode</c> types.</summary>
private const string NetcodeNamespace = "Netcode";
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs b/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
index 3d353e52..f1a3ef75 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
@@ -12,7 +12,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
public class ObsoleteFieldAnalyzer : DiagnosticAnalyzer
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Maps obsolete fields/properties to their non-obsolete equivalent.</summary>
private readonly IDictionary<string, string> ReplacedFields = new Dictionary<string, string>
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/StardewModdingAPI.ModBuildConfig.Analyzer.csproj b/src/SMAPI.ModBuildConfig.Analyzer/StardewModdingAPI.ModBuildConfig.Analyzer.csproj
index 9d3f6d5b..9d646e8f 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer/StardewModdingAPI.ModBuildConfig.Analyzer.csproj
+++ b/src/SMAPI.ModBuildConfig.Analyzer/StardewModdingAPI.ModBuildConfig.Analyzer.csproj
@@ -5,6 +5,7 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<IncludeBuildOutput>false</IncludeBuildOutput>
<OutputPath>bin</OutputPath>
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
diff --git a/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs b/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs
index 7ff66695..e03683d0 100644
--- a/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs
+++ b/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs
@@ -12,7 +12,7 @@ namespace StardewModdingAPI.ModBuildConfig.Framework
internal class ModFileManager
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The name of the manifest file.</summary>
private readonly string ManifestFileName = "manifest.json";
diff --git a/src/SMAPI.ModBuildConfig/StardewModdingAPI.ModBuildConfig.csproj b/src/SMAPI.ModBuildConfig/StardewModdingAPI.ModBuildConfig.csproj
index f068b480..9118b043 100644
--- a/src/SMAPI.ModBuildConfig/StardewModdingAPI.ModBuildConfig.csproj
+++ b/src/SMAPI.ModBuildConfig/StardewModdingAPI.ModBuildConfig.csproj
@@ -11,6 +11,7 @@
<AssemblyName>StardewModdingAPI.ModBuildConfig</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs
index 3ad1e168..10007b42 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands
internal class ArgumentParser : IReadOnlyList<string>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The command name for errors.</summary>
private readonly string CommandName;
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs
index 37f4719e..263e126c 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
internal class AddCommand : TrainerCommand
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Provides methods for searching and constructing items.</summary>
private readonly ItemRepository Items = new ItemRepository();
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs
index 34f1760c..a835455e 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
internal class ListItemTypesCommand : TrainerCommand
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Provides methods for searching and constructing items.</summary>
private readonly ItemRepository Items = new ItemRepository();
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs
index 942a50b8..5b52e9a2 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
internal class ListItemsCommand : TrainerCommand
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Provides methods for searching and constructing items.</summary>
private readonly ItemRepository Items = new ItemRepository();
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs
index 2e8f6630..1abb82b5 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
internal class SetHealthCommand : TrainerCommand
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Whether to keep the player's health at its maximum.</summary>
private bool InfiniteHealth;
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs
index 3fc504b1..ad11cc66 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
internal class SetMoneyCommand : TrainerCommand
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Whether to keep the player's money at a set value.</summary>
private bool InfiniteMoney;
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs
index 866c3d22..009cb1de 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
internal class SetStaminaCommand : TrainerCommand
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Whether to keep the player's stamina at its maximum.</summary>
private bool InfiniteStamina;
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs
new file mode 100644
index 00000000..c769b622
--- /dev/null
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs
@@ -0,0 +1,246 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using StardewValley;
+using StardewValley.Locations;
+using StardewValley.Objects;
+using StardewValley.TerrainFeatures;
+using SObject = StardewValley.Object;
+
+namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
+{
+ /// <summary>A command which clears in-game objects.</summary>
+ internal class ClearCommand : TrainerCommand
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>The valid types that can be cleared.</summary>
+ private readonly string[] ValidTypes = { "debris", "fruit-trees", "grass", "trees", "everything" };
+
+ /// <summary>The resource clump IDs to consider debris.</summary>
+ private readonly int[] DebrisClumps = { ResourceClump.stumpIndex, ResourceClump.hollowLogIndex, ResourceClump.meteoriteIndex, ResourceClump.boulderIndex };
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ public ClearCommand()
+ : base(
+ name: "world_clear",
+ description: "Clears in-game entities in a given location.\n\n"
+ + "Usage: world_clear <location> <object type>\n"
+ + "- location: the location name for which to clear objects (like Farm), or 'current' for the current location.\n"
+ + " - object type: the type of object clear. You can specify 'debris' (stones/twigs/weeds and dead crops), 'grass', and 'trees' / 'fruit-trees'. You can also specify 'everything', which includes things not removed by the other types (like furniture or resource clumps)."
+ )
+ { }
+
+ /// <summary>Handle the command.</summary>
+ /// <param name="monitor">Writes messages to the console and log file.</param>
+ /// <param name="command">The command name.</param>
+ /// <param name="args">The command arguments.</param>
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
+ {
+ // check context
+ if (!Context.IsWorldReady)
+ {
+ monitor.Log("You need to load a save to use this command.", LogLevel.Error);
+ return;
+ }
+
+ // parse arguments
+ if (!args.TryGet(0, "location", out string locationName, required: true))
+ return;
+ if (!args.TryGet(1, "object type", out string type, required: true, oneOf: this.ValidTypes))
+ return;
+
+ // get target location
+ GameLocation location = Game1.locations.FirstOrDefault(p => p.Name != null && p.Name.Equals(locationName, StringComparison.InvariantCultureIgnoreCase));
+ if (location == null && locationName == "current")
+ location = Game1.currentLocation;
+ if (location == null)
+ {
+ string[] locationNames = (from loc in Game1.locations where !string.IsNullOrWhiteSpace(loc.Name) orderby loc.Name select loc.Name).ToArray();
+ monitor.Log($"Could not find a location with that name. Must be one of [{string.Join(", ", locationNames)}].", LogLevel.Error);
+ return;
+ }
+
+ // apply
+ switch (type)
+ {
+ case "debris":
+ {
+ int removed = 0;
+ foreach (var pair in location.terrainFeatures.Pairs.ToArray())
+ {
+ TerrainFeature feature = pair.Value;
+ if (feature is HoeDirt dirt && dirt.crop?.dead == true)
+ {
+ dirt.crop = null;
+ removed++;
+ }
+ }
+
+ removed +=
+ this.RemoveObjects(location, obj => obj.Name.ToLower().Contains("weed") || obj.Name == "Twig" || obj.Name == "Stone")
+ + this.RemoveResourceClumps(location, clump => this.DebrisClumps.Contains(clump.parentSheetIndex.Value));
+
+ monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info);
+ break;
+ }
+
+ case "fruit-trees":
+ {
+ int removed = this.RemoveTerrainFeatures(location, feature => feature is FruitTree);
+ monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info);
+ break;
+ }
+
+ case "grass":
+ {
+ int removed = this.RemoveTerrainFeatures(location, feature => feature is Grass);
+ monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info);
+ break;
+ }
+
+ case "trees":
+ {
+ int removed = this.RemoveTerrainFeatures(location, feature => feature is Tree);
+ monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info);
+ break;
+ }
+
+ case "everything":
+ {
+ int removed =
+ this.RemoveFurniture(location, p => true)
+ + this.RemoveObjects(location, p => true)
+ + this.RemoveTerrainFeatures(location, p => true)
+ + this.RemoveLargeTerrainFeatures(location, p => true)
+ + this.RemoveResourceClumps(location, p => true);
+ monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info);
+ break;
+ }
+
+ default:
+ monitor.Log($"Unknown type '{type}'. Must be one [{string.Join(", ", this.ValidTypes)}].", LogLevel.Error);
+ break;
+ }
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Remove objects from a location matching a lambda.</summary>
+ /// <param name="location">The location to search.</param>
+ /// <param name="shouldRemove">Whether an entity should be removed.</param>
+ /// <returns>Returns the number of removed entities.</returns>
+ private int RemoveObjects(GameLocation location, Func<SObject, bool> shouldRemove)
+ {
+ int removed = 0;
+
+ foreach (var pair in location.Objects.Pairs.ToArray())
+ {
+ if (shouldRemove(pair.Value))
+ {
+ location.Objects.Remove(pair.Key);
+ removed++;
+ }
+ }
+
+ return removed;
+ }
+
+ /// <summary>Remove terrain features from a location matching a lambda.</summary>
+ /// <param name="location">The location to search.</param>
+ /// <param name="shouldRemove">Whether an entity should be removed.</param>
+ /// <returns>Returns the number of removed entities.</returns>
+ private int RemoveTerrainFeatures(GameLocation location, Func<TerrainFeature, bool> shouldRemove)
+ {
+ int removed = 0;
+
+ foreach (var pair in location.terrainFeatures.Pairs.ToArray())
+ {
+ if (shouldRemove(pair.Value))
+ {
+ location.terrainFeatures.Remove(pair.Key);
+ removed++;
+ }
+ }
+
+ return removed;
+ }
+
+ /// <summary>Remove large terrain features from a location matching a lambda.</summary>
+ /// <param name="location">The location to search.</param>
+ /// <param name="shouldRemove">Whether an entity should be removed.</param>
+ /// <returns>Returns the number of removed entities.</returns>
+ private int RemoveLargeTerrainFeatures(GameLocation location, Func<LargeTerrainFeature, bool> shouldRemove)
+ {
+ int removed = 0;
+
+ foreach (LargeTerrainFeature feature in location.largeTerrainFeatures.ToArray())
+ {
+ if (shouldRemove(feature))
+ {
+ location.largeTerrainFeatures.Remove(feature);
+ removed++;
+ }
+ }
+
+ return removed;
+ }
+
+ /// <summary>Remove resource clumps from a location matching a lambda.</summary>
+ /// <param name="location">The location to search.</param>
+ /// <param name="shouldRemove">Whether an entity should be removed.</param>
+ /// <returns>Returns the number of removed entities.</returns>
+ private int RemoveResourceClumps(GameLocation location, Func<ResourceClump, bool> shouldRemove)
+ {
+ int removed = 0;
+
+ // get resource clumps
+ IList<ResourceClump> resourceClumps =
+ (location as Farm)?.resourceClumps
+ ?? (IList<ResourceClump>)(location as Woods)?.stumps
+ ?? new List<ResourceClump>();
+
+ // remove matching clumps
+ foreach (var clump in resourceClumps.ToArray())
+ {
+ if (shouldRemove(clump))
+ {
+ resourceClumps.Remove(clump);
+ removed++;
+ }
+ }
+
+ return removed;
+ }
+
+ /// <summary>Remove furniture from a location matching a lambda.</summary>
+ /// <param name="location">The location to search.</param>
+ /// <param name="shouldRemove">Whether an entity should be removed.</param>
+ /// <returns>Returns the number of removed entities.</returns>
+ private int RemoveFurniture(GameLocation location, Func<Furniture, bool> shouldRemove)
+ {
+ int removed = 0;
+
+ if (location is DecoratableLocation decoratableLocation)
+ {
+ foreach (Furniture furniture in decoratableLocation.furniture.ToArray())
+ {
+ if (shouldRemove(furniture))
+ {
+ decoratableLocation.furniture.Remove(furniture);
+ removed++;
+ }
+ }
+ }
+
+ return removed;
+ }
+ }
+}
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs
index 2627b714..6a7ab162 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
internal class FreezeTimeCommand : TrainerCommand
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The time of day at which to freeze time.</summary>
internal static int FrozenTime;
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs
index b5db9c0d..0615afe7 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
internal class SetSeasonCommand : TrainerCommand
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The valid season names.</summary>
private readonly string[] ValidSeasons = { "winter", "spring", "summer", "fall" };
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
index f4a38403..fc631826 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
@@ -12,7 +12,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
internal class ItemRepository
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The custom ID offset for items don't have a unique ID in the game.</summary>
private readonly int CustomIDOffset = 1000;
diff --git a/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs b/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs
index 30951064..77dace26 100644
--- a/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands
public class ModEntry : Mod
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The commands to handle.</summary>
private ITrainerCommand[] Commands;
diff --git a/src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj b/src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj
index d1f16e41..2c958dbc 100644
--- a/src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj
+++ b/src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj
@@ -11,6 +11,7 @@
<AssemblyName>ConsoleCommands</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
@@ -62,6 +63,7 @@
<Compile Include="Framework\Commands\TrainerCommand.cs" />
<Compile Include="Framework\Commands\World\SetMineLevelCommand.cs" />
<Compile Include="Framework\Commands\World\DownMineLevelCommand.cs" />
+ <Compile Include="Framework\Commands\World\ClearCommand.cs" />
<Compile Include="Framework\Commands\World\SetYearCommand.cs" />
<Compile Include="Framework\Commands\World\SetSeasonCommand.cs" />
<Compile Include="Framework\Commands\World\SetDayCommand.cs" />
diff --git a/src/SMAPI.Mods.ConsoleCommands/manifest.json b/src/SMAPI.Mods.ConsoleCommands/manifest.json
index 541d1045..3e7001d5 100644
--- a/src/SMAPI.Mods.ConsoleCommands/manifest.json
+++ b/src/SMAPI.Mods.ConsoleCommands/manifest.json
@@ -1,9 +1,9 @@
{
"Name": "Console Commands",
"Author": "SMAPI",
- "Version": "2.9.3",
+ "Version": "2.10.0",
"Description": "Adds SMAPI console commands that let you manipulate the game.",
"UniqueID": "SMAPI.ConsoleCommands",
"EntryDll": "ConsoleCommands.dll",
- "MinimumApiVersion": "2.9.3"
+ "MinimumApiVersion": "2.10.0"
}
diff --git a/src/SMAPI.Mods.SaveBackup/ModEntry.cs b/src/SMAPI.Mods.SaveBackup/ModEntry.cs
index 4d56789a..56a86cd9 100644
--- a/src/SMAPI.Mods.SaveBackup/ModEntry.cs
+++ b/src/SMAPI.Mods.SaveBackup/ModEntry.cs
@@ -12,7 +12,7 @@ namespace StardewModdingAPI.Mods.SaveBackup
public class ModEntry : Mod
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The number of backups to keep.</summary>
private readonly int BackupsToKeep = 10;
diff --git a/src/SMAPI.Mods.SaveBackup/StardewModdingAPI.Mods.SaveBackup.csproj b/src/SMAPI.Mods.SaveBackup/StardewModdingAPI.Mods.SaveBackup.csproj
index fafa4d25..56b6b7f4 100644
--- a/src/SMAPI.Mods.SaveBackup/StardewModdingAPI.Mods.SaveBackup.csproj
+++ b/src/SMAPI.Mods.SaveBackup/StardewModdingAPI.Mods.SaveBackup.csproj
@@ -11,6 +11,7 @@
<AssemblyName>SaveBackup</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
diff --git a/src/SMAPI.Mods.SaveBackup/manifest.json b/src/SMAPI.Mods.SaveBackup/manifest.json
index d8dea086..0e2601ad 100644
--- a/src/SMAPI.Mods.SaveBackup/manifest.json
+++ b/src/SMAPI.Mods.SaveBackup/manifest.json
@@ -1,9 +1,9 @@
{
"Name": "Save Backup",
"Author": "SMAPI",
- "Version": "2.9.3",
+ "Version": "2.10.0",
"Description": "Automatically backs up all your saves once per day into its folder.",
"UniqueID": "SMAPI.SaveBackup",
"EntryDll": "SaveBackup.dll",
- "MinimumApiVersion": "2.9.3"
+ "MinimumApiVersion": "2.10.0"
}
diff --git a/src/SMAPI.Tests/Sample.cs b/src/SMAPI.Tests/Sample.cs
index 99835d92..6cd27707 100644
--- a/src/SMAPI.Tests/Sample.cs
+++ b/src/SMAPI.Tests/Sample.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace StardewModdingAPI.Tests
{
@@ -6,14 +6,14 @@ namespace StardewModdingAPI.Tests
internal static class Sample
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>A random number generator.</summary>
private static readonly Random Random = new Random();
/*********
- ** Properties
+ ** Accessors
*********/
/// <summary>Get a sample string.</summary>
public static string String()
diff --git a/src/SMAPI.Tests/StardewModdingAPI.Tests.csproj b/src/SMAPI.Tests/StardewModdingAPI.Tests.csproj
index 4ec1a3de..83bd92af 100644
--- a/src/SMAPI.Tests/StardewModdingAPI.Tests.csproj
+++ b/src/SMAPI.Tests/StardewModdingAPI.Tests.csproj
@@ -11,6 +11,7 @@
<AssemblyName>StardewModdingAPI.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
diff --git a/src/SMAPI.Tests/Utilities/SDateTests.cs b/src/SMAPI.Tests/Utilities/SDateTests.cs
index b89d8857..1f31168e 100644
--- a/src/SMAPI.Tests/Utilities/SDateTests.cs
+++ b/src/SMAPI.Tests/Utilities/SDateTests.cs
@@ -13,7 +13,7 @@ namespace StardewModdingAPI.Tests.Utilities
internal class SDateTests
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>All valid seasons.</summary>
private static readonly string[] ValidSeasons = { "spring", "summer", "fall", "winter" };
diff --git a/src/SMAPI.Web/Controllers/IndexController.cs b/src/SMAPI.Web/Controllers/IndexController.cs
index d7be664d..7b3b3e80 100644
--- a/src/SMAPI.Web/Controllers/IndexController.cs
+++ b/src/SMAPI.Web/Controllers/IndexController.cs
@@ -20,7 +20,7 @@ namespace StardewModdingAPI.Web.Controllers
internal class IndexController : Controller
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The site config settings.</summary>
private readonly SiteConfig SiteConfig;
diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs
index 17f8d3aa..21e4a56f 100644
--- a/src/SMAPI.Web/Controllers/LogParserController.cs
+++ b/src/SMAPI.Web/Controllers/LogParserController.cs
@@ -19,7 +19,7 @@ namespace StardewModdingAPI.Web.Controllers
internal class LogParserController : Controller
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The site config settings.</summary>
private readonly SiteConfig Config;
diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs
index 12d349e0..7e6f592c 100644
--- a/src/SMAPI.Web/Controllers/ModsApiController.cs
+++ b/src/SMAPI.Web/Controllers/ModsApiController.cs
@@ -28,7 +28,7 @@ namespace StardewModdingAPI.Web.Controllers
internal class ModsApiController : Controller
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The mod repositories which provide mod metadata.</summary>
private readonly IDictionary<ModRepositoryKey, IModRepository> Repositories;
diff --git a/src/SMAPI.Web/Controllers/ModsController.cs b/src/SMAPI.Web/Controllers/ModsController.cs
index 57aa9da9..ca866a8d 100644
--- a/src/SMAPI.Web/Controllers/ModsController.cs
+++ b/src/SMAPI.Web/Controllers/ModsController.cs
@@ -16,13 +16,13 @@ namespace StardewModdingAPI.Web.Controllers
internal class ModsController : Controller
{
/*********
- ** Properties
+ ** Fields
*********/
/// <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;
+ private readonly int CacheMinutes;
/*********
@@ -31,12 +31,12 @@ namespace StardewModdingAPI.Web.Controllers
/// <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)
+ public ModsController(IMemoryCache cache, IOptions<ModCompatibilityListConfig> configProvider)
{
- ModUpdateCheckConfig config = configProvider.Value;
+ ModCompatibilityListConfig config = configProvider.Value;
this.Cache = cache;
- this.SuccessCacheMinutes = config.SuccessCacheMinutes;
+ this.CacheMinutes = config.CacheMinutes;
}
/// <summary>Display information for all mods.</summary>
@@ -66,7 +66,7 @@ namespace StardewModdingAPI.Web.Controllers
.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);
+ entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.CacheMinutes);
return model;
});
}
diff --git a/src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs b/src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs
index 68ead3c2..5dc0feb6 100644
--- a/src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs
+++ b/src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Web.Framework
public class AllowLargePostsAttribute : Attribute, IAuthorizationFilter, IOrderedFilter
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying form options.</summary>
private readonly FormOptions FormOptions;
diff --git a/src/SMAPI.Web/Framework/BeanstalkEnvPropsConfigProvider.cs b/src/SMAPI.Web/Framework/BeanstalkEnvPropsConfigProvider.cs
index b39a3b61..fe27fe2f 100644
--- a/src/SMAPI.Web/Framework/BeanstalkEnvPropsConfigProvider.cs
+++ b/src/SMAPI.Web/Framework/BeanstalkEnvPropsConfigProvider.cs
@@ -12,7 +12,7 @@ namespace StardewModdingAPI.Web.Framework
internal class BeanstalkEnvPropsConfigProvider : ConfigurationProvider, IConfigurationSource
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The absolute path to the container configuration file on an Amazon EC2 instance.</summary>
private const string ContainerConfigPath = @"C:\Program Files\Amazon\ElasticBeanstalk\config\containerconfiguration";
diff --git a/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs b/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs
index 029553ce..2753e33a 100644
--- a/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.Chucklefish
internal class ChucklefishClient : IChucklefishClient
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The URL for a mod page excluding the base URL, where {0} is the mod ID.</summary>
private readonly string ModPageUrlFormat;
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
index 2cfc6903..22950db9 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
internal class GitHubClient : IGitHubClient
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The URL for a GitHub API query for the latest stable release, excluding the base URL, where {0} is the organisation and project name.</summary>
private readonly string StableReleaseUrlFormat;
diff --git a/src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs b/src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs
index 19b0b24d..5ad2d2f8 100644
--- a/src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.ModDrop
internal class ModDropClient : IModDropClient
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying HTTP client.</summary>
private readonly IClient Client;
diff --git a/src/SMAPI.Web/Framework/Clients/Nexus/NexusWebScrapeClient.cs b/src/SMAPI.Web/Framework/Clients/Nexus/NexusWebScrapeClient.cs
index 1b3fa195..e83a6041 100644
--- a/src/SMAPI.Web/Framework/Clients/Nexus/NexusWebScrapeClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/Nexus/NexusWebScrapeClient.cs
@@ -13,7 +13,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.Nexus
internal class NexusWebScrapeClient : INexusClient
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The URL for a Nexus mod page for the user, excluding the base URL, where {0} is the mod ID.</summary>
private readonly string ModUrlFormat;
diff --git a/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs b/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs
index ef83a91e..12c3e83f 100644
--- a/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs
@@ -14,7 +14,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.Pastebin
internal class PastebinClient : IPastebinClient
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying HTTP client.</summary>
private readonly IClient Client;
diff --git a/src/SMAPI.Web/Framework/ConfigModels/ModCompatibilityListConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ModCompatibilityListConfig.cs
new file mode 100644
index 00000000..d9ac9f02
--- /dev/null
+++ b/src/SMAPI.Web/Framework/ConfigModels/ModCompatibilityListConfig.cs
@@ -0,0 +1,12 @@
+namespace StardewModdingAPI.Web.Framework.ConfigModels
+{
+ /// <summary>The config settings for mod compatibility list.</summary>
+ internal class ModCompatibilityListConfig
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The number of minutes data from the wiki should be cached before refetching it.</summary>
+ public int CacheMinutes { get; set; }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs
index f9b5ba76..6f848469 100644
--- a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs
+++ b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs
@@ -12,7 +12,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
public class LogParser
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>A regex pattern matching the start of a SMAPI message.</summary>
private readonly Regex MessageHeaderPattern = new Regex(@"^\[(?<time>\d\d:\d\d:\d\d) (?<level>[a-z]+) +(?<modName>[^\]]+)\] ", RegexOptions.Compiled | RegexOptions.IgnoreCase);
diff --git a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
index 6e2a8814..87e29a2f 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
internal class ChucklefishRepository : RepositoryBase
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying HTTP client.</summary>
private readonly IChucklefishClient Client;
diff --git a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
index 7ff22d0e..14f44dc0 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
internal class GitHubRepository : RepositoryBase
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying GitHub API client.</summary>
private readonly IGitHubClient Client;
diff --git a/src/SMAPI.Web/Framework/ModRepositories/ModDropRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/ModDropRepository.cs
index 09484aa8..1994f515 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/ModDropRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/ModDropRepository.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
internal class ModDropRepository : RepositoryBase
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying ModDrop API client.</summary>
private readonly IModDropClient Client;
diff --git a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
index 1e242c60..4c5fe9bf 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
internal class NexusRepository : RepositoryBase
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying Nexus Mods API client.</summary>
private readonly INexusClient Client;
diff --git a/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs
index d6a56bb7..36effd82 100644
--- a/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs
+++ b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs
@@ -11,7 +11,7 @@ namespace StardewModdingAPI.Web.Framework.RewriteRules
internal class ConditionalRedirectToHttpsRule : IRule
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>A predicate which indicates when the rule should be applied.</summary>
private readonly Func<HttpRequest, bool> ShouldRewrite;
diff --git a/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs b/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs
index 4bae0b4c..ab9e019c 100644
--- a/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs
+++ b/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Web.Framework.RewriteRules
internal class RedirectToUrlRule : IRule
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Get the new URL to which to redirect (or <c>null</c> to skip).</summary>
private readonly Func<HttpRequest, string> NewUrl;
diff --git a/src/SMAPI.Web/StardewModdingAPI.Web.csproj b/src/SMAPI.Web/StardewModdingAPI.Web.csproj
index 9d1990d9..32fdf135 100644
--- a/src/SMAPI.Web/StardewModdingAPI.Web.csproj
+++ b/src/SMAPI.Web/StardewModdingAPI.Web.csproj
@@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs
index 4e3aaed3..91553513 100644
--- a/src/SMAPI.Web/Startup.cs
+++ b/src/SMAPI.Web/Startup.cs
@@ -50,6 +50,7 @@ namespace StardewModdingAPI.Web
{
// init configuration
services
+ .Configure<ModCompatibilityListConfig>(this.Configuration.GetSection("ModCompatibilityList"))
.Configure<ModUpdateCheckConfig>(this.Configuration.GetSection("ModUpdateCheck"))
.Configure<SiteConfig>(this.Configuration.GetSection("Site"))
.Configure<RouteOptions>(options => options.ConstraintMap.Add("semanticVersion", typeof(VersionConstraint)))
diff --git a/src/SMAPI.Web/ViewModels/LogParserModel.cs b/src/SMAPI.Web/ViewModels/LogParserModel.cs
index df36ca73..41864c99 100644
--- a/src/SMAPI.Web/ViewModels/LogParserModel.cs
+++ b/src/SMAPI.Web/ViewModels/LogParserModel.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Web.ViewModels
public class LogParserModel
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>A regex pattern matching characters to remove from a mod name to create the slug ID.</summary>
private readonly Regex SlugInvalidCharPattern = new Regex("[^a-z0-9]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json
index 89505a45..e97b2339 100644
--- a/src/SMAPI.Web/appsettings.json
+++ b/src/SMAPI.Web/appsettings.json
@@ -47,6 +47,10 @@
"PastebinDevKey": null // see top note
},
+ "ModCompatibilityList": {
+ "WikiCacheMinutes": 10
+ },
+
"ModUpdateCheck": {
"SuccessCacheMinutes": 60,
"ErrorCacheMinutes": 5,
diff --git a/src/SMAPI.sln.DotSettings b/src/SMAPI.sln.DotSettings
index 68d7c72b..5f67fd9e 100644
--- a/src/SMAPI.sln.DotSettings
+++ b/src/SMAPI.sln.DotSettings
@@ -18,6 +18,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs
index 2d67284e..9ceaf11d 100644
--- a/src/SMAPI/Constants.cs
+++ b/src/SMAPI/Constants.cs
@@ -13,7 +13,7 @@ namespace StardewModdingAPI
public static class Constants
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The directory path containing the current save's data (if a save is loaded).</summary>
private static string RawSavePath => Context.IsSaveLoaded ? Path.Combine(Constants.SavesPath, Constants.GetSaveFolderName()) : null;
@@ -29,7 +29,7 @@ namespace StardewModdingAPI
** Public
****/
/// <summary>SMAPI's current semantic version.</summary>
- public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("2.9.3");
+ public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("2.10.0");
/// <summary>The minimum supported version of Stardew Valley.</summary>
public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.3.32");
diff --git a/src/SMAPI/Enums/LoadStage.cs b/src/SMAPI/Enums/LoadStage.cs
new file mode 100644
index 00000000..6ff7de4f
--- /dev/null
+++ b/src/SMAPI/Enums/LoadStage.cs
@@ -0,0 +1,36 @@
+namespace StardewModdingAPI.Enums
+{
+ /// <summary>A low-level stage in the game's loading process.</summary>
+ public enum LoadStage
+ {
+ /// <summary>A save is not loaded or loading.</summary>
+ None,
+
+ /// <summary>The game is creating a new save slot, and has initialised the basic save info.</summary>
+ CreatedBasicInfo,
+
+ /// <summary>The game is creating a new save slot, and has initialised the in-game locations.</summary>
+ CreatedLocations,
+
+ /// <summary>The game is creating a new save slot, and has created the physical save files.</summary>
+ CreatedSaveFile,
+
+ /// <summary>The game is loading a save slot, and has read the raw save data into <see cref="StardewValley.SaveGame.loaded"/>. Not applicable when connecting to a multiplayer host. This is equivalent to <see cref="StardewValley.SaveGame.getLoadEnumerator"/> value 20.</summary>
+ SaveParsed,
+
+ /// <summary>The game is loading a save slot, and has applied the basic save info (including player data). Not applicable when connecting to a multiplayer host. Note that some basic info (like daily luck) is not initialised at this point. This is equivalent to <see cref="StardewValley.SaveGame.getLoadEnumerator"/> value 36.</summary>
+ SaveLoadedBasicInfo,
+
+ /// <summary>The game is loading a save slot, and has applied the in-game location data. Not applicable when connecting to a multiplayer host. This is equivalent to <see cref="StardewValley.SaveGame.getLoadEnumerator"/> value 50.</summary>
+ SaveLoadedLocations,
+
+ /// <summary>The final metadata has been loaded from the save file. This happens before the game applies problem fixes, checks for achievements, starts music, etc. Not applicable when connecting to a multiplayer host.</summary>
+ Preloaded,
+
+ /// <summary>The save is fully loaded, but the world may not be fully initialised yet.</summary>
+ Loaded,
+
+ /// <summary>The save is fully loaded, the world has been initialised, and <see cref="Context.IsWorldReady"/> is now true.</summary>
+ Ready
+ }
+}
diff --git a/src/SMAPI/Events/BuildingListChangedEventArgs.cs b/src/SMAPI/Events/BuildingListChangedEventArgs.cs
index 0237342f..74f37710 100644
--- a/src/SMAPI/Events/BuildingListChangedEventArgs.cs
+++ b/src/SMAPI/Events/BuildingListChangedEventArgs.cs
@@ -21,6 +21,9 @@ namespace StardewModdingAPI.Events
/// <summary>The buildings removed from the location.</summary>
public IEnumerable<Building> Removed { get; }
+ /// <summary>Whether this is the location containing the local player.</summary>
+ public bool IsCurrentLocation => object.ReferenceEquals(this.Location, Game1.player?.currentLocation);
+
/*********
** Public methods
diff --git a/src/SMAPI/Events/ButtonPressedEventArgs.cs b/src/SMAPI/Events/ButtonPressedEventArgs.cs
index 9e6c187f..5d922666 100644
--- a/src/SMAPI/Events/ButtonPressedEventArgs.cs
+++ b/src/SMAPI/Events/ButtonPressedEventArgs.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Events
public class ButtonPressedEventArgs : EventArgs
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The game's current input state.</summary>
private readonly SInputState InputState;
diff --git a/src/SMAPI/Events/ButtonReleasedEventArgs.cs b/src/SMAPI/Events/ButtonReleasedEventArgs.cs
index 2a289bc7..f5282230 100644
--- a/src/SMAPI/Events/ButtonReleasedEventArgs.cs
+++ b/src/SMAPI/Events/ButtonReleasedEventArgs.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Events
public class ButtonReleasedEventArgs : EventArgs
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The game's current input state.</summary>
private readonly SInputState InputState;
diff --git a/src/SMAPI/Events/ContentEvents.cs b/src/SMAPI/Events/ContentEvents.cs
index 1a2dd526..aca76ef7 100644
--- a/src/SMAPI/Events/ContentEvents.cs
+++ b/src/SMAPI/Events/ContentEvents.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Events
public static class ContentEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/ControlEvents.cs b/src/SMAPI/Events/ControlEvents.cs
index be849f95..45aedc9b 100644
--- a/src/SMAPI/Events/ControlEvents.cs
+++ b/src/SMAPI/Events/ControlEvents.cs
@@ -11,7 +11,7 @@ namespace StardewModdingAPI.Events
public static class ControlEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/DebrisListChangedEventArgs.cs b/src/SMAPI/Events/DebrisListChangedEventArgs.cs
index 68328885..61b7590a 100644
--- a/src/SMAPI/Events/DebrisListChangedEventArgs.cs
+++ b/src/SMAPI/Events/DebrisListChangedEventArgs.cs
@@ -20,6 +20,9 @@ namespace StardewModdingAPI.Events
/// <summary>The debris removed from the location.</summary>
public IEnumerable<Debris> Removed { get; }
+ /// <summary>Whether this is the location containing the local player.</summary>
+ public bool IsCurrentLocation => object.ReferenceEquals(this.Location, Game1.player?.currentLocation);
+
/*********
** Public methods
diff --git a/src/SMAPI/Events/EventArgsInput.cs b/src/SMAPI/Events/EventArgsInput.cs
index 837de2f8..5cff3408 100644
--- a/src/SMAPI/Events/EventArgsInput.cs
+++ b/src/SMAPI/Events/EventArgsInput.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Events
public class EventArgsInput : EventArgs
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The buttons to suppress.</summary>
private readonly HashSet<SButton> SuppressButtons;
diff --git a/src/SMAPI/Events/GameEvents.cs b/src/SMAPI/Events/GameEvents.cs
index 6069a185..9d945277 100644
--- a/src/SMAPI/Events/GameEvents.cs
+++ b/src/SMAPI/Events/GameEvents.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Events
public static class GameEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/GraphicsEvents.cs b/src/SMAPI/Events/GraphicsEvents.cs
index 88a32c3f..24a16a29 100644
--- a/src/SMAPI/Events/GraphicsEvents.cs
+++ b/src/SMAPI/Events/GraphicsEvents.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Events
public static class GraphicsEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/IGameLoopEvents.cs b/src/SMAPI/Events/IGameLoopEvents.cs
index e1900f79..6fb56c8b 100644
--- a/src/SMAPI/Events/IGameLoopEvents.cs
+++ b/src/SMAPI/Events/IGameLoopEvents.cs
@@ -14,6 +14,12 @@ namespace StardewModdingAPI.Events
/// <summary>Raised after the game state is updated (≈60 times per second).</summary>
event EventHandler<UpdateTickedEventArgs> UpdateTicked;
+ /// <summary>Raised once per second before the game state is updated.</summary>
+ event EventHandler<OneSecondUpdateTickingEventArgs> OneSecondUpdateTicking;
+
+ /// <summary>Raised once per second after the game state is updated.</summary>
+ event EventHandler<OneSecondUpdateTickedEventArgs> OneSecondUpdateTicked;
+
/// <summary>Raised before the game creates a new save file.</summary>
event EventHandler<SaveCreatingEventArgs> SaveCreating;
@@ -26,7 +32,7 @@ namespace StardewModdingAPI.Events
/// <summary>Raised after the game finishes writing data to the save file (except the initial save creation).</summary>
event EventHandler<SavedEventArgs> Saved;
- /// <summary>Raised after the player loads a save slot.</summary>
+ /// <summary>Raised after the player loads a save slot and the world is initialised.</summary>
event EventHandler<SaveLoadedEventArgs> SaveLoaded;
/// <summary>Raised after the game begins a new day (including when the player loads a save).</summary>
diff --git a/src/SMAPI/Events/ISpecialisedEvents.cs b/src/SMAPI/Events/ISpecialisedEvents.cs
index 928cd05d..ecb109e6 100644
--- a/src/SMAPI/Events/ISpecialisedEvents.cs
+++ b/src/SMAPI/Events/ISpecialisedEvents.cs
@@ -5,6 +5,9 @@ namespace StardewModdingAPI.Events
/// <summary>Events serving specialised edge cases that shouldn't be used by most mods.</summary>
public interface ISpecialisedEvents
{
+ /// <summary>Raised when the low-level stage in the game's loading process has changed. This is an advanced event for mods which need to run code at specific points in the loading process. The available stages or when they happen might change without warning in future versions (e.g. due to changes in the game's load process), so mods using this event are more likely to break or have bugs. Most mods should use <see cref="IGameLoopEvents"/> instead.</summary>
+ event EventHandler<LoadStageChangedEventArgs> LoadStageChanged;
+
/// <summary>Raised before the game state is updated (≈60 times per second), regardless of normal SMAPI validation. This event is not thread-safe and may be invoked while game logic is running asynchronously. Changes to game state in this method may crash the game or corrupt an in-progress save. Do not use this event unless you're fully aware of the context in which your code will be run. Mods using this event will trigger a stability warning in the SMAPI console.</summary>
event EventHandler<UnvalidatedUpdateTickingEventArgs> UnvalidatedUpdateTicking;
diff --git a/src/SMAPI/Events/InputEvents.cs b/src/SMAPI/Events/InputEvents.cs
index 900e53ea..c5ab8c83 100644
--- a/src/SMAPI/Events/InputEvents.cs
+++ b/src/SMAPI/Events/InputEvents.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Events
public static class InputEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/LargeTerrainFeatureListChangedEventArgs.cs b/src/SMAPI/Events/LargeTerrainFeatureListChangedEventArgs.cs
index c7d55bf8..59d79f0f 100644
--- a/src/SMAPI/Events/LargeTerrainFeatureListChangedEventArgs.cs
+++ b/src/SMAPI/Events/LargeTerrainFeatureListChangedEventArgs.cs
@@ -21,6 +21,9 @@ namespace StardewModdingAPI.Events
/// <summary>The large terrain features removed from the location.</summary>
public IEnumerable<LargeTerrainFeature> Removed { get; }
+ /// <summary>Whether this is the location containing the local player.</summary>
+ public bool IsCurrentLocation => object.ReferenceEquals(this.Location, Game1.player?.currentLocation);
+
/*********
** Public methods
diff --git a/src/SMAPI/Events/LoadStageChangedEventArgs.cs b/src/SMAPI/Events/LoadStageChangedEventArgs.cs
new file mode 100644
index 00000000..e837a5f1
--- /dev/null
+++ b/src/SMAPI/Events/LoadStageChangedEventArgs.cs
@@ -0,0 +1,31 @@
+using System;
+using StardewModdingAPI.Enums;
+
+namespace StardewModdingAPI.Events
+{
+ /// <summary>Event arguments for an <see cref="ISpecialisedEvents.LoadStageChanged"/> event.</summary>
+ public class LoadStageChangedEventArgs : EventArgs
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The previous load stage.</summary>
+ public LoadStage OldStage { get; }
+
+ /// <summary>The new load stage.</summary>
+ public LoadStage NewStage { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="old">The previous load stage.</param>
+ /// <param name="current">The new load stage.</param>
+ public LoadStageChangedEventArgs(LoadStage old, LoadStage current)
+ {
+ this.OldStage = old;
+ this.NewStage = current;
+ }
+ }
+}
diff --git a/src/SMAPI/Events/LocationEvents.cs b/src/SMAPI/Events/LocationEvents.cs
index 5eb228b7..0761bdd8 100644
--- a/src/SMAPI/Events/LocationEvents.cs
+++ b/src/SMAPI/Events/LocationEvents.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Events
public static class LocationEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/MenuEvents.cs b/src/SMAPI/Events/MenuEvents.cs
index 914948dd..8647c268 100644
--- a/src/SMAPI/Events/MenuEvents.cs
+++ b/src/SMAPI/Events/MenuEvents.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Events
public static class MenuEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/MineEvents.cs b/src/SMAPI/Events/MineEvents.cs
index fd35237e..929da35b 100644
--- a/src/SMAPI/Events/MineEvents.cs
+++ b/src/SMAPI/Events/MineEvents.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Events
public static class MineEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/ModMessageReceivedEventArgs.cs b/src/SMAPI/Events/ModMessageReceivedEventArgs.cs
index 49366ec6..d4370028 100644
--- a/src/SMAPI/Events/ModMessageReceivedEventArgs.cs
+++ b/src/SMAPI/Events/ModMessageReceivedEventArgs.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Events
public class ModMessageReceivedEventArgs : EventArgs
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying message model.</summary>
private readonly ModMessageModel Message;
diff --git a/src/SMAPI/Events/MultiplayerEvents.cs b/src/SMAPI/Events/MultiplayerEvents.cs
index 5e6a22dc..0650a8e2 100644
--- a/src/SMAPI/Events/MultiplayerEvents.cs
+++ b/src/SMAPI/Events/MultiplayerEvents.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Events
public static class MultiplayerEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/NpcListChangedEventArgs.cs b/src/SMAPI/Events/NpcListChangedEventArgs.cs
index a9ec2a3b..3a37f1e7 100644
--- a/src/SMAPI/Events/NpcListChangedEventArgs.cs
+++ b/src/SMAPI/Events/NpcListChangedEventArgs.cs
@@ -20,6 +20,9 @@ namespace StardewModdingAPI.Events
/// <summary>The NPCs removed from the location.</summary>
public IEnumerable<NPC> Removed { get; }
+ /// <summary>Whether this is the location containing the local player.</summary>
+ public bool IsCurrentLocation => object.ReferenceEquals(this.Location, Game1.player?.currentLocation);
+
/*********
** Public methods
diff --git a/src/SMAPI/Events/ObjectListChangedEventArgs.cs b/src/SMAPI/Events/ObjectListChangedEventArgs.cs
index d0cf9e7b..b21d2867 100644
--- a/src/SMAPI/Events/ObjectListChangedEventArgs.cs
+++ b/src/SMAPI/Events/ObjectListChangedEventArgs.cs
@@ -22,6 +22,9 @@ namespace StardewModdingAPI.Events
/// <summary>The objects removed from the location.</summary>
public IEnumerable<KeyValuePair<Vector2, Object>> Removed { get; }
+ /// <summary>Whether this is the location containing the local player.</summary>
+ public bool IsCurrentLocation => object.ReferenceEquals(this.Location, Game1.player?.currentLocation);
+
/*********
** Public methods
diff --git a/src/SMAPI/Events/OneSecondUpdateTickedEventArgs.cs b/src/SMAPI/Events/OneSecondUpdateTickedEventArgs.cs
new file mode 100644
index 00000000..dadbb71a
--- /dev/null
+++ b/src/SMAPI/Events/OneSecondUpdateTickedEventArgs.cs
@@ -0,0 +1,26 @@
+using System;
+using StardewValley;
+
+namespace StardewModdingAPI.Events
+{
+ /// <summary>Event arguments for an <see cref="IGameLoopEvents.OneSecondUpdateTicked"/> event.</summary>
+ public class OneSecondUpdateTickedEventArgs : EventArgs
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The number of ticks elapsed since the game started, including the current tick.</summary>
+ public uint Ticks => (uint)Game1.ticks;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Get whether <see cref="Ticks"/> is a multiple of the given <paramref name="number"/>. This is mainly useful if you want to run logic intermittently (e.g. <code>e.IsMultipleOf(30)</code> for every half-second).</summary>
+ /// <param name="number">The factor to check.</param>
+ public bool IsMultipleOf(uint number)
+ {
+ return this.Ticks % number == 0;
+ }
+ }
+}
diff --git a/src/SMAPI/Events/OneSecondUpdateTickingEventArgs.cs b/src/SMAPI/Events/OneSecondUpdateTickingEventArgs.cs
new file mode 100644
index 00000000..e9bb46c6
--- /dev/null
+++ b/src/SMAPI/Events/OneSecondUpdateTickingEventArgs.cs
@@ -0,0 +1,26 @@
+using System;
+using StardewValley;
+
+namespace StardewModdingAPI.Events
+{
+ /// <summary>Event arguments for an <see cref="IGameLoopEvents.OneSecondUpdateTicking"/> event.</summary>
+ public class OneSecondUpdateTickingEventArgs : EventArgs
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The number of ticks elapsed since the game started, including the current tick.</summary>
+ public uint Ticks => (uint)Game1.ticks;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Get whether <see cref="Ticks"/> is a multiple of the given <paramref name="number"/>. This is mainly useful if you want to run logic intermittently (e.g. <code>e.IsMultipleOf(30)</code> for every half-second).</summary>
+ /// <param name="number">The factor to check.</param>
+ public bool IsMultipleOf(uint number)
+ {
+ return this.Ticks % number == 0;
+ }
+ }
+}
diff --git a/src/SMAPI/Events/PlayerEvents.cs b/src/SMAPI/Events/PlayerEvents.cs
index 98bc3da5..11ba1e54 100644
--- a/src/SMAPI/Events/PlayerEvents.cs
+++ b/src/SMAPI/Events/PlayerEvents.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Events
public static class PlayerEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/SaveEvents.cs b/src/SMAPI/Events/SaveEvents.cs
index 959fb5d2..da276d22 100644
--- a/src/SMAPI/Events/SaveEvents.cs
+++ b/src/SMAPI/Events/SaveEvents.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Events
public static class SaveEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/SpecialisedEvents.cs b/src/SMAPI/Events/SpecialisedEvents.cs
index 2c6d0230..4f16e4da 100644
--- a/src/SMAPI/Events/SpecialisedEvents.cs
+++ b/src/SMAPI/Events/SpecialisedEvents.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Events
public static class SpecialisedEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/TerrainFeatureListChangedEventArgs.cs b/src/SMAPI/Events/TerrainFeatureListChangedEventArgs.cs
index 0992633e..cdf1e6dc 100644
--- a/src/SMAPI/Events/TerrainFeatureListChangedEventArgs.cs
+++ b/src/SMAPI/Events/TerrainFeatureListChangedEventArgs.cs
@@ -22,6 +22,9 @@ namespace StardewModdingAPI.Events
/// <summary>The terrain features removed from the location.</summary>
public IEnumerable<KeyValuePair<Vector2, TerrainFeature>> Removed { get; }
+ /// <summary>Whether this is the location containing the local player.</summary>
+ public bool IsCurrentLocation => object.ReferenceEquals(this.Location, Game1.player?.currentLocation);
+
/*********
** Public methods
diff --git a/src/SMAPI/Events/TimeEvents.cs b/src/SMAPI/Events/TimeEvents.cs
index 40892491..389532d9 100644
--- a/src/SMAPI/Events/TimeEvents.cs
+++ b/src/SMAPI/Events/TimeEvents.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Events
public static class TimeEvents
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The core event manager.</summary>
private static EventManager EventManager;
diff --git a/src/SMAPI/Events/UnvalidatedUpdateTickedEventArgs.cs b/src/SMAPI/Events/UnvalidatedUpdateTickedEventArgs.cs
index 95ae59d8..d15e9531 100644
--- a/src/SMAPI/Events/UnvalidatedUpdateTickedEventArgs.cs
+++ b/src/SMAPI/Events/UnvalidatedUpdateTickedEventArgs.cs
@@ -1,4 +1,5 @@
using System;
+using StardewValley;
namespace StardewModdingAPI.Events
{
@@ -9,23 +10,15 @@ namespace StardewModdingAPI.Events
** Accessors
*********/
/// <summary>The number of ticks elapsed since the game started, including the current tick.</summary>
- public uint Ticks { get; }
+ public uint Ticks => (uint)Game1.ticks;
/// <summary>Whether <see cref="Ticks"/> is a multiple of 60, which happens approximately once per second.</summary>
- public bool IsOneSecond { get; }
+ public bool IsOneSecond => Game1.ticks % 60 == 0;
/*********
** Public methods
*********/
- /// <summary>Construct an instance.</summary>
- /// <param name="ticks">The number of ticks elapsed since the game started, including the current tick.</param>
- internal UnvalidatedUpdateTickedEventArgs(uint ticks)
- {
- this.Ticks = ticks;
- this.IsOneSecond = this.IsMultipleOf(60);
- }
-
/// <summary>Get whether <see cref="Ticks"/> is a multiple of the given <paramref name="number"/>. This is mainly useful if you want to run logic intermittently (e.g. <code>e.IsMultipleOf(30)</code> for every half-second).</summary>
/// <param name="number">The factor to check.</param>
public bool IsMultipleOf(uint number)
diff --git a/src/SMAPI/Events/UnvalidatedUpdateTickingEventArgs.cs b/src/SMAPI/Events/UnvalidatedUpdateTickingEventArgs.cs
index 4ed781e0..577f0776 100644
--- a/src/SMAPI/Events/UnvalidatedUpdateTickingEventArgs.cs
+++ b/src/SMAPI/Events/UnvalidatedUpdateTickingEventArgs.cs
@@ -1,4 +1,5 @@
using System;
+using StardewValley;
namespace StardewModdingAPI.Events
{
@@ -9,23 +10,15 @@ namespace StardewModdingAPI.Events
** Accessors
*********/
/// <summary>The number of ticks elapsed since the game started, including the current tick.</summary>
- public uint Ticks { get; }
+ public uint Ticks => (uint)Game1.ticks;
/// <summary>Whether <see cref="Ticks"/> is a multiple of 60, which happens approximately once per second.</summary>
- public bool IsOneSecond { get; }
+ public bool IsOneSecond => Game1.ticks % 60 == 0;
/*********
** Public methods
*********/
- /// <summary>Construct an instance.</summary>
- /// <param name="ticks">The number of ticks elapsed since the game started, including the current tick.</param>
- internal UnvalidatedUpdateTickingEventArgs(uint ticks)
- {
- this.Ticks = ticks;
- this.IsOneSecond = this.IsMultipleOf(60);
- }
-
/// <summary>Get whether <see cref="Ticks"/> is a multiple of the given <paramref name="number"/>. This is mainly useful if you want to run logic intermittently (e.g. <code>e.IsMultipleOf(30)</code> for every half-second).</summary>
/// <param name="number">The factor to check.</param>
public bool IsMultipleOf(uint number)
diff --git a/src/SMAPI/Events/UpdateTickedEventArgs.cs b/src/SMAPI/Events/UpdateTickedEventArgs.cs
index 3466b731..aa710b44 100644
--- a/src/SMAPI/Events/UpdateTickedEventArgs.cs
+++ b/src/SMAPI/Events/UpdateTickedEventArgs.cs
@@ -1,4 +1,5 @@
using System;
+using StardewValley;
namespace StardewModdingAPI.Events
{
@@ -9,23 +10,15 @@ namespace StardewModdingAPI.Events
** Accessors
*********/
/// <summary>The number of ticks elapsed since the game started, including the current tick.</summary>
- public uint Ticks { get; }
+ public uint Ticks => (uint)Game1.ticks;
/// <summary>Whether <see cref="Ticks"/> is a multiple of 60, which happens approximately once per second.</summary>
- public bool IsOneSecond { get; }
+ public bool IsOneSecond => Game1.ticks % 60 == 0;
/*********
** Public methods
*********/
- /// <summary>Construct an instance.</summary>
- /// <param name="ticks">The number of ticks elapsed since the game started, including the current tick.</param>
- internal UpdateTickedEventArgs(uint ticks)
- {
- this.Ticks = ticks;
- this.IsOneSecond = this.IsMultipleOf(60);
- }
-
/// <summary>Get whether <see cref="Ticks"/> is a multiple of the given <paramref name="number"/>. This is mainly useful if you want to run logic intermittently (e.g. <code>e.IsMultipleOf(30)</code> for every half-second).</summary>
/// <param name="number">The factor to check.</param>
public bool IsMultipleOf(uint number)
diff --git a/src/SMAPI/Events/UpdateTickingEventArgs.cs b/src/SMAPI/Events/UpdateTickingEventArgs.cs
index d4913268..cacf5a54 100644
--- a/src/SMAPI/Events/UpdateTickingEventArgs.cs
+++ b/src/SMAPI/Events/UpdateTickingEventArgs.cs
@@ -1,4 +1,5 @@
using System;
+using StardewValley;
namespace StardewModdingAPI.Events
{
@@ -9,23 +10,15 @@ namespace StardewModdingAPI.Events
** Accessors
*********/
/// <summary>The number of ticks elapsed since the game started, including the current tick.</summary>
- public uint Ticks { get; }
+ public uint Ticks => (uint)Game1.ticks;
/// <summary>Whether <see cref="Ticks"/> is a multiple of 60, which happens approximately once per second.</summary>
- public bool IsOneSecond { get; }
+ public bool IsOneSecond => Game1.ticks % 60 == 0;
/*********
** Public methods
*********/
- /// <summary>Construct an instance.</summary>
- /// <param name="ticks">The number of ticks elapsed since the game started, including the current tick.</param>
- internal UpdateTickingEventArgs(uint ticks)
- {
- this.Ticks = ticks;
- this.IsOneSecond = this.IsMultipleOf(60);
- }
-
/// <summary>Get whether <see cref="Ticks"/> is a multiple of the given <paramref name="number"/>. This is mainly useful if you want to run logic intermittently (e.g. <code>e.IsMultipleOf(30)</code> for every half-second).</summary>
/// <param name="number">The factor to check.</param>
public bool IsMultipleOf(uint number)
diff --git a/src/SMAPI/Framework/CommandManager.cs b/src/SMAPI/Framework/CommandManager.cs
index aabe99c3..fdaafff1 100644
--- a/src/SMAPI/Framework/CommandManager.cs
+++ b/src/SMAPI/Framework/CommandManager.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework
internal class CommandManager
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The commands registered with SMAPI.</summary>
private readonly IDictionary<string, Command> Commands = new Dictionary<string, Command>(StringComparer.InvariantCultureIgnoreCase);
diff --git a/src/SMAPI/Framework/Content/AssetData.cs b/src/SMAPI/Framework/Content/AssetData.cs
index 0a86f54d..553404d3 100644
--- a/src/SMAPI/Framework/Content/AssetData.cs
+++ b/src/SMAPI/Framework/Content/AssetData.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Framework.Content
internal class AssetData<TValue> : AssetInfo, IAssetData<TValue>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>A callback to invoke when the data is replaced (if any).</summary>
private readonly Action<TValue> OnDataReplaced;
diff --git a/src/SMAPI/Framework/Content/AssetDataForDictionary.cs b/src/SMAPI/Framework/Content/AssetDataForDictionary.cs
index 9bd70711..fd3edd5d 100644
--- a/src/SMAPI/Framework/Content/AssetDataForDictionary.cs
+++ b/src/SMAPI/Framework/Content/AssetDataForDictionary.cs
@@ -26,7 +26,7 @@ namespace StardewModdingAPI.Framework.Content
[Obsolete("Access " + nameof(AssetData<IDictionary<TKey, TValue>>.Data) + "field directly.")]
public void Set(TKey key, TValue value)
{
- SCore.DeprecationManager.Warn($"AssetDataForDictionary.{nameof(Set)}", "2.10", DeprecationLevel.Notice);
+ SCore.DeprecationManager.Warn($"AssetDataForDictionary.{nameof(Set)}", "2.10", DeprecationLevel.Info);
this.Data[key] = value;
}
@@ -36,7 +36,7 @@ namespace StardewModdingAPI.Framework.Content
[Obsolete("Access " + nameof(AssetData<IDictionary<TKey, TValue>>.Data) + "field directly.")]
public void Set(TKey key, Func<TValue, TValue> value)
{
- SCore.DeprecationManager.Warn($"AssetDataForDictionary.{nameof(Set)}", "2.10", DeprecationLevel.Notice);
+ SCore.DeprecationManager.Warn($"AssetDataForDictionary.{nameof(Set)}", "2.10", DeprecationLevel.Info);
this.Data[key] = value(this.Data[key]);
}
@@ -45,7 +45,7 @@ namespace StardewModdingAPI.Framework.Content
[Obsolete("Access " + nameof(AssetData<IDictionary<TKey, TValue>>.Data) + "field directly.")]
public void Set(Func<TKey, TValue, TValue> replacer)
{
- SCore.DeprecationManager.Warn($"AssetDataForDictionary.{nameof(Set)}", "2.10", DeprecationLevel.Notice);
+ SCore.DeprecationManager.Warn($"AssetDataForDictionary.{nameof(Set)}", "2.10", DeprecationLevel.Info);
foreach (var pair in this.Data.ToArray())
this.Data[pair.Key] = replacer(pair.Key, pair.Value);
}
diff --git a/src/SMAPI/Framework/Content/AssetDataForImage.cs b/src/SMAPI/Framework/Content/AssetDataForImage.cs
index f970762a..f2d21b5e 100644
--- a/src/SMAPI/Framework/Content/AssetDataForImage.cs
+++ b/src/SMAPI/Framework/Content/AssetDataForImage.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.Content
internal class AssetDataForImage : AssetData<Texture2D>, IAssetDataForImage
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The minimum value to consider non-transparent.</summary>
/// <remarks>On Linux/Mac, fully transparent pixels may have an alpha up to 4 for some reason.</remarks>
diff --git a/src/SMAPI/Framework/Content/AssetInfo.cs b/src/SMAPI/Framework/Content/AssetInfo.cs
index d580dc06..e5211290 100644
--- a/src/SMAPI/Framework/Content/AssetInfo.cs
+++ b/src/SMAPI/Framework/Content/AssetInfo.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Framework.Content
internal class AssetInfo : IAssetInfo
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Normalises an asset key to match the cache key.</summary>
protected readonly Func<string, string> GetNormalisedPath;
diff --git a/src/SMAPI/Framework/Content/ContentCache.cs b/src/SMAPI/Framework/Content/ContentCache.cs
index a5dfac9d..55a96ed2 100644
--- a/src/SMAPI/Framework/Content/ContentCache.cs
+++ b/src/SMAPI/Framework/Content/ContentCache.cs
@@ -14,7 +14,7 @@ namespace StardewModdingAPI.Framework.Content
internal class ContentCache
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying asset cache.</summary>
private readonly IDictionary<string, object> Cache;
diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs
index 7e256939..4dd1b6e1 100644
--- a/src/SMAPI/Framework/ContentCoordinator.cs
+++ b/src/SMAPI/Framework/ContentCoordinator.cs
@@ -19,7 +19,7 @@ namespace StardewModdingAPI.Framework
internal class ContentCoordinator : IDisposable
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>An asset key prefix for assets from SMAPI mod folders.</summary>
private readonly string ManagedPrefix = "SMAPI";
diff --git a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
index 724a6e1c..7821e454 100644
--- a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
@@ -18,7 +18,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
internal abstract class BaseContentManager : LocalizedContentManager, IContentManager
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The central coordinator which manages content managers.</summary>
protected readonly ContentCoordinator Coordinator;
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
index 81732d3f..ee940cc7 100644
--- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
@@ -14,7 +14,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
internal class GameContentManager : BaseContentManager
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The assets currently being intercepted by <see cref="IAssetLoader"/> instances. This is used to prevent infinite loops when a loader loads a new asset.</summary>
private readonly ContextHash<string> AssetsBeingLoaded = new ContextHash<string>();
diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
index ed76a925..6485b3d4 100644
--- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
@@ -14,7 +14,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
internal class ModContentManager : BaseContentManager
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
private readonly JsonHelper JsonHelper;
diff --git a/src/SMAPI/Framework/ContentPack.cs b/src/SMAPI/Framework/ContentPack.cs
index 49285388..e39d03a1 100644
--- a/src/SMAPI/Framework/ContentPack.cs
+++ b/src/SMAPI/Framework/ContentPack.cs
@@ -12,7 +12,7 @@ namespace StardewModdingAPI.Framework
internal class ContentPack : IContentPack
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Provides an API for loading content assets.</summary>
private readonly IContentHelper Content;
diff --git a/src/SMAPI/Framework/DeprecationManager.cs b/src/SMAPI/Framework/DeprecationManager.cs
index be564c22..76c2f616 100644
--- a/src/SMAPI/Framework/DeprecationManager.cs
+++ b/src/SMAPI/Framework/DeprecationManager.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework
internal class DeprecationManager
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The deprecations which have already been logged (as 'mod name::noun phrase::version').</summary>
private readonly HashSet<string> LoggedDeprecations = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
@@ -38,7 +38,7 @@ namespace StardewModdingAPI.Framework
/// <summary>Log a deprecation warning for the old-style events.</summary>
public void WarnForOldEvents()
{
- this.Warn("legacy events", "2.9", DeprecationLevel.Notice);
+ this.Warn("legacy events", "2.9", DeprecationLevel.Info);
}
/// <summary>Log a deprecation warning.</summary>
@@ -71,7 +71,13 @@ namespace StardewModdingAPI.Framework
foreach (DeprecationWarning warning in this.QueuedWarnings.OrderBy(p => p.ModName).ThenBy(p => p.NounPhrase))
{
// build message
+#if SMAPI_3_0_STRICT
string message = $"{warning.ModName ?? "An unknown mod"} uses deprecated code ({warning.NounPhrase} is deprecated since SMAPI {warning.Version}).";
+#else
+ string message = warning.NounPhrase == "legacy events"
+ ? $"{warning.ModName ?? "An unknown mod"} uses deprecated code (legacy events are deprecated since SMAPI {warning.Version})."
+ : $"{warning.ModName ?? "An unknown mod"} uses deprecated code ({warning.NounPhrase} is deprecated since SMAPI {warning.Version}).";
+#endif
if (warning.ModName == null)
message += $"{Environment.NewLine}{Environment.StackTrace}";
diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs
index 0ad85adf..13244601 100644
--- a/src/SMAPI/Framework/Events/EventManager.cs
+++ b/src/SMAPI/Framework/Events/EventManager.cs
@@ -58,6 +58,12 @@ namespace StardewModdingAPI.Framework.Events
/// <summary>Raised after the game performs its overall update tick (≈60 times per second).</summary>
public readonly ManagedEvent<UpdateTickedEventArgs> UpdateTicked;
+ /// <summary>Raised once per second before the game performs its overall update tick.</summary>
+ public readonly ManagedEvent<OneSecondUpdateTickingEventArgs> OneSecondUpdateTicking;
+
+ /// <summary>Raised once per second after the game performs its overall update tick.</summary>
+ public readonly ManagedEvent<OneSecondUpdateTickedEventArgs> OneSecondUpdateTicked;
+
/// <summary>Raised before the game creates the save file.</summary>
public readonly ManagedEvent<SaveCreatingEventArgs> SaveCreating;
@@ -70,7 +76,7 @@ namespace StardewModdingAPI.Framework.Events
/// <summary>Raised after the game finishes writing data to the save file (except the initial save creation).</summary>
public readonly ManagedEvent<SavedEventArgs> Saved;
- /// <summary>Raised after the player loads a save slot.</summary>
+ /// <summary>Raised after the player loads a save slot and the world is initialised.</summary>
public readonly ManagedEvent<SaveLoadedEventArgs> SaveLoaded;
/// <summary>Raised after the game begins a new day, including when loading a save.</summary>
@@ -151,6 +157,9 @@ namespace StardewModdingAPI.Framework.Events
/****
** Specialised
****/
+ /// <summary>Raised when the low-level stage in the game's loading process has changed. See notes on <see cref="ISpecialisedEvents.LoadStageChanged"/>.</summary>
+ public readonly ManagedEvent<LoadStageChangedEventArgs> LoadStageChanged;
+
/// <summary>Raised before the game performs its overall update tick (≈60 times per second). See notes on <see cref="ISpecialisedEvents.UnvalidatedUpdateTicking"/>.</summary>
public readonly ManagedEvent<UnvalidatedUpdateTickingEventArgs> UnvalidatedUpdateTicking;
@@ -377,6 +386,8 @@ namespace StardewModdingAPI.Framework.Events
this.GameLaunched = ManageEventOf<GameLaunchedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.GameLaunched));
this.UpdateTicking = ManageEventOf<UpdateTickingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.UpdateTicking));
this.UpdateTicked = ManageEventOf<UpdateTickedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.UpdateTicked));
+ this.OneSecondUpdateTicking = ManageEventOf<OneSecondUpdateTickingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.OneSecondUpdateTicking));
+ this.OneSecondUpdateTicked = ManageEventOf<OneSecondUpdateTickedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.OneSecondUpdateTicked));
this.SaveCreating = ManageEventOf<SaveCreatingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.SaveCreating));
this.SaveCreated = ManageEventOf<SaveCreatedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.SaveCreated));
this.Saving = ManageEventOf<SavingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.Saving));
@@ -408,6 +419,7 @@ namespace StardewModdingAPI.Framework.Events
this.ObjectListChanged = ManageEventOf<ObjectListChangedEventArgs>(nameof(IModEvents.World), nameof(IWorldEvents.ObjectListChanged));
this.TerrainFeatureListChanged = ManageEventOf<TerrainFeatureListChangedEventArgs>(nameof(IModEvents.World), nameof(IWorldEvents.TerrainFeatureListChanged));
+ this.LoadStageChanged = ManageEventOf<LoadStageChangedEventArgs>(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.LoadStageChanged));
this.UnvalidatedUpdateTicking = ManageEventOf<UnvalidatedUpdateTickingEventArgs>(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicking));
this.UnvalidatedUpdateTicked = ManageEventOf<UnvalidatedUpdateTickedEventArgs>(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicked));
diff --git a/src/SMAPI/Framework/Events/ManagedEvent.cs b/src/SMAPI/Framework/Events/ManagedEvent.cs
index 65f6e38e..f9e7f6ec 100644
--- a/src/SMAPI/Framework/Events/ManagedEvent.cs
+++ b/src/SMAPI/Framework/Events/ManagedEvent.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.Events
internal class ManagedEvent<TEventArgs> : ManagedEventBase<EventHandler<TEventArgs>>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying event.</summary>
private event EventHandler<TEventArgs> Event;
@@ -93,11 +93,12 @@ namespace StardewModdingAPI.Framework.Events
}
}
+#if !SMAPI_3_0_STRICT
/// <summary>An event wrapper which intercepts and logs errors in handler code.</summary>
internal class ManagedEvent : ManagedEventBase<EventHandler>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying event.</summary>
private event EventHandler Event;
@@ -156,4 +157,5 @@ namespace StardewModdingAPI.Framework.Events
}
}
}
+#endif
}
diff --git a/src/SMAPI/Framework/Events/ManagedEventBase.cs b/src/SMAPI/Framework/Events/ManagedEventBase.cs
index defd903a..c8c3516b 100644
--- a/src/SMAPI/Framework/Events/ManagedEventBase.cs
+++ b/src/SMAPI/Framework/Events/ManagedEventBase.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.Events
internal abstract class ManagedEventBase<TEventHandler>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>A human-readable name for the event.</summary>
private readonly string EventName;
diff --git a/src/SMAPI/Framework/Events/ModEventsBase.cs b/src/SMAPI/Framework/Events/ModEventsBase.cs
index 545c58a8..77708fc1 100644
--- a/src/SMAPI/Framework/Events/ModEventsBase.cs
+++ b/src/SMAPI/Framework/Events/ModEventsBase.cs
@@ -4,7 +4,7 @@ namespace StardewModdingAPI.Framework.Events
internal abstract class ModEventsBase
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying event manager.</summary>
protected readonly EventManager EventManager;
diff --git a/src/SMAPI/Framework/Events/ModGameLoopEvents.cs b/src/SMAPI/Framework/Events/ModGameLoopEvents.cs
index a5beac99..0177c22e 100644
--- a/src/SMAPI/Framework/Events/ModGameLoopEvents.cs
+++ b/src/SMAPI/Framework/Events/ModGameLoopEvents.cs
@@ -30,6 +30,20 @@ namespace StardewModdingAPI.Framework.Events
remove => this.EventManager.UpdateTicked.Remove(value);
}
+ /// <summary>Raised once per second before the game state is updated.</summary>
+ public event EventHandler<OneSecondUpdateTickingEventArgs> OneSecondUpdateTicking
+ {
+ add => this.EventManager.OneSecondUpdateTicking.Add(value);
+ remove => this.EventManager.OneSecondUpdateTicking.Remove(value);
+ }
+
+ /// <summary>Raised once per second after the game state is updated.</summary>
+ public event EventHandler<OneSecondUpdateTickedEventArgs> OneSecondUpdateTicked
+ {
+ add => this.EventManager.OneSecondUpdateTicked.Add(value);
+ remove => this.EventManager.OneSecondUpdateTicked.Remove(value);
+ }
+
/// <summary>Raised before the game creates a new save file.</summary>
public event EventHandler<SaveCreatingEventArgs> SaveCreating
{
@@ -58,7 +72,7 @@ namespace StardewModdingAPI.Framework.Events
remove => this.EventManager.Saved.Remove(value);
}
- /// <summary>Raised after the player loads a save slot.</summary>
+ /// <summary>Raised after the player loads a save slot and the world is initialised.</summary>
public event EventHandler<SaveLoadedEventArgs> SaveLoaded
{
add => this.EventManager.SaveLoaded.Add(value);
diff --git a/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs b/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs
index 17c32bb8..7c3e9dee 100644
--- a/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs
+++ b/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs
@@ -9,6 +9,13 @@ namespace StardewModdingAPI.Framework.Events
/*********
** Accessors
*********/
+ /// <summary>Raised when the low-level stage in the game's loading process has changed. This is an advanced event for mods which need to run code at specific points in the loading process. The available stages or when they happen might change without warning in future versions (e.g. due to changes in the game's load process), so mods using this event are more likely to break or have bugs. Most mods should use <see cref="IGameLoopEvents"/> instead.</summary>
+ public event EventHandler<LoadStageChangedEventArgs> LoadStageChanged
+ {
+ add => this.EventManager.LoadStageChanged.Add(value);
+ remove => this.EventManager.LoadStageChanged.Remove(value);
+ }
+
/// <summary>Raised before the game state is updated (≈60 times per second), regardless of normal SMAPI validation. This event is not thread-safe and may be invoked while game logic is running asynchronously. Changes to game state in this method may crash the game or corrupt an in-progress save. Do not use this event unless you're fully aware of the context in which your code will be run. Mods using this event will trigger a stability warning in the SMAPI console.</summary>
public event EventHandler<UnvalidatedUpdateTickingEventArgs> UnvalidatedUpdateTicking
{
diff --git a/src/SMAPI/Framework/Input/GamePadStateBuilder.cs b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs
index 33557385..a20e1248 100644
--- a/src/SMAPI/Framework/Input/GamePadStateBuilder.cs
+++ b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.Input
internal class GamePadStateBuilder
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The current button states.</summary>
private readonly IDictionary<SButton, ButtonState> ButtonStates;
diff --git a/src/SMAPI/Framework/Logging/ConsoleInterceptionManager.cs b/src/SMAPI/Framework/Logging/ConsoleInterceptionManager.cs
index c04bcd1a..ef42e536 100644
--- a/src/SMAPI/Framework/Logging/ConsoleInterceptionManager.cs
+++ b/src/SMAPI/Framework/Logging/ConsoleInterceptionManager.cs
@@ -6,7 +6,7 @@ namespace StardewModdingAPI.Framework.Logging
internal class ConsoleInterceptionManager : IDisposable
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The intercepting console writer.</summary>
private readonly InterceptingTextWriter Output;
diff --git a/src/SMAPI/Framework/Logging/LogFileManager.cs b/src/SMAPI/Framework/Logging/LogFileManager.cs
index 8cfe0527..6b5babcd 100644
--- a/src/SMAPI/Framework/Logging/LogFileManager.cs
+++ b/src/SMAPI/Framework/Logging/LogFileManager.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Framework.Logging
internal class LogFileManager : IDisposable
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying stream writer.</summary>
private readonly StreamWriter Stream;
diff --git a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
index a4fd21c1..e9d53d84 100644
--- a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
@@ -6,7 +6,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
internal class CommandHelper : BaseHelper, ICommandHelper
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The mod using this instance.</summary>
private readonly IModMetadata Mod;
diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
index a8b24a13..dac627ba 100644
--- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
@@ -21,7 +21,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
internal class ContentHelper : BaseHelper, IContentHelper
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>SMAPI's core content logic.</summary>
private readonly ContentCoordinator ContentCore;
diff --git a/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs
index 26c4648c..34f24d65 100644
--- a/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
internal class ContentPackHelper : BaseHelper, IContentPackHelper
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The content packs loaded for this mod.</summary>
private readonly Lazy<IContentPack[]> ContentPacks;
diff --git a/src/SMAPI/Framework/ModHelpers/DataHelper.cs b/src/SMAPI/Framework/ModHelpers/DataHelper.cs
index e5100aed..2cb886ba 100644
--- a/src/SMAPI/Framework/ModHelpers/DataHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/DataHelper.cs
@@ -11,7 +11,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
internal class DataHelper : BaseHelper, IDataHelper
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
private readonly JsonHelper JsonHelper;
@@ -77,9 +77,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <exception cref="InvalidOperationException">The player hasn't loaded a save file yet or isn't the main player.</exception>
public TModel ReadSaveData<TModel>(string key) where TModel : class
{
- if (!Context.IsSaveLoaded)
+ if (!Game1.hasLoadedGame)
throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.ReadSaveData)} when a save file isn't loaded.");
- if (!Context.IsMainPlayer)
+ if (!Game1.IsMasterGame)
throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.ReadSaveData)} because this isn't the main player. (Save files are stored on the main player's computer.)");
return Game1.CustomData.TryGetValue(this.GetSaveFileKey(key), out string value)
@@ -94,9 +94,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <exception cref="InvalidOperationException">The player hasn't loaded a save file yet or isn't the main player.</exception>
public void WriteSaveData<TModel>(string key, TModel data) where TModel : class
{
- if (!Context.IsSaveLoaded)
+ if (!Game1.hasLoadedGame)
throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteSaveData)} when a save file isn't loaded.");
- if (!Context.IsMainPlayer)
+ if (!Game1.IsMasterGame)
throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.ReadSaveData)} because this isn't the main player. (Save files are stored on the main player's computer.)");
string internalKey = this.GetSaveFileKey(key);
diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
index ca872e32..18040a78 100644
--- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
@@ -166,7 +166,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
[Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.ContentPacks) + "." + nameof(IContentPackHelper.CreateTemporary) + " instead")]
public IContentPack CreateTransitionalContentPack(string directoryPath, string id, string name, string description, string author, ISemanticVersion version)
{
- SCore.DeprecationManager.Warn($"{nameof(IModHelper)}.{nameof(IModHelper.CreateTransitionalContentPack)}", "2.5", DeprecationLevel.Notice);
+ SCore.DeprecationManager.Warn($"{nameof(IModHelper)}.{nameof(IModHelper.CreateTransitionalContentPack)}", "2.5", DeprecationLevel.Info);
return this.ContentPacks.CreateTemporary(directoryPath, id, name, description, author, version);
}
diff --git a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs
index 5cc2a20f..8330e078 100644
--- a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
internal class ModRegistryHelper : BaseHelper, IModRegistry
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying mod registry.</summary>
private readonly ModRegistry Registry;
diff --git a/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs b/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs
index eedad0bc..c62dd121 100644
--- a/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
internal class MultiplayerHelper : BaseHelper, IMultiplayerHelper
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>SMAPI's core multiplayer utility.</summary>
private readonly SMultiplayer Multiplayer;
diff --git a/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs b/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs
index cfe2ddbe..0ce72a9e 100644
--- a/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
internal class ReflectionHelper : BaseHelper, IReflectionHelper
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying reflection helper.</summary>
private readonly Reflector Reflector;
diff --git a/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs b/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs
index bbe3a81a..3252e047 100644
--- a/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
internal class TranslationHelper : BaseHelper, ITranslationHelper
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The name of the relevant mod for error messages.</summary>
private readonly string ModName;
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs b/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
index 91c9e192..aefb0126 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Framework.ModLoading
internal class AssemblyDefinitionResolver : DefaultAssemblyResolver
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The known assemblies.</summary>
private readonly IDictionary<string, AssemblyDefinition> Lookup = new Dictionary<string, AssemblyDefinition>();
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
index 7292cf3f..5e0571a0 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
@@ -15,7 +15,7 @@ namespace StardewModdingAPI.Framework.ModLoading
internal class AssemblyLoader : IDisposable
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Encapsulates monitoring and logging.</summary>
private readonly IMonitor Monitor;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
index e4beb7a9..898bafb4 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
internal class EventFinder : IInstructionHandler
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The full type name for which to find references.</summary>
private readonly string FullTypeName;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
index 00805815..606ca8b7 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
internal class FieldFinder : IInstructionHandler
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The full type name for which to find references.</summary>
private readonly string FullTypeName;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
index 5358f181..9ca246ff 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
internal class MethodFinder : IInstructionHandler
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The full type name for which to find references.</summary>
private readonly string FullTypeName;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
index e54c86cf..0677aa88 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
internal class PropertyFinder : IInstructionHandler
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The full type name for which to find references.</summary>
private readonly string FullTypeName;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
index 3a26660f..82c4920a 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
internal class ReferenceToMemberWithUnexpectedTypeFinder : IInstructionHandler
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The assembly names to which to heuristically detect broken references.</summary>
private readonly HashSet<string> ValidateReferencesToAssemblies;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
index b95dd79c..44b531a5 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
internal class ReferenceToMissingMemberFinder : IInstructionHandler
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The assembly names to which to heuristically detect broken references.</summary>
private readonly HashSet<string> ValidateReferencesToAssemblies;
diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs
index ace84054..835b0a54 100644
--- a/src/SMAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs
@@ -400,7 +400,7 @@ namespace StardewModdingAPI.Framework.ModLoading
** Private models
*********/
/// <summary>Represents a dependency from one mod to another.</summary>
- private struct ModDependency
+ private readonly struct ModDependency
{
/*********
** Accessors
diff --git a/src/SMAPI/Framework/ModLoading/RewriteHelper.cs b/src/SMAPI/Framework/ModLoading/RewriteHelper.cs
index 9ff43d45..f8f10dc4 100644
--- a/src/SMAPI/Framework/ModLoading/RewriteHelper.cs
+++ b/src/SMAPI/Framework/ModLoading/RewriteHelper.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Framework.ModLoading
internal static class RewriteHelper
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The comparer which heuristically compares type definitions.</summary>
private static readonly TypeReferenceComparer TypeDefinitionComparer = new TypeReferenceComparer();
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs
index 806a074f..ff86c6e2 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
internal class FieldReplaceRewriter : FieldFinder
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The new field to reference.</summary>
private readonly FieldInfo ToField;
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/FieldToPropertyRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/FieldToPropertyRewriter.cs
index e6ede9e3..a43c5e9a 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/FieldToPropertyRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/FieldToPropertyRewriter.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
internal class FieldToPropertyRewriter : FieldFinder
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The type whose field to which references should be rewritten.</summary>
private readonly Type Type;
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs
index 99bd9125..6b8c2de1 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
internal class MethodParentRewriter : IInstructionHandler
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The type whose methods to remap.</summary>
private readonly Type FromType;
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/StaticFieldToConstantRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/StaticFieldToConstantRewriter.cs
index 5e12b46a..7e7c0efa 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/StaticFieldToConstantRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/StaticFieldToConstantRewriter.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
internal class StaticFieldToConstantRewriter<TValue> : FieldFinder
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The constant value to replace with.</summary>
private readonly TValue Value;
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs
index 62e15731..fade082b 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
internal class TypeReferenceRewriter : TypeFinder
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The full type name to which to find references.</summary>
private readonly string FromTypeName;
diff --git a/src/SMAPI/Framework/ModRegistry.cs b/src/SMAPI/Framework/ModRegistry.cs
index 8ce3172c..e9ceb66e 100644
--- a/src/SMAPI/Framework/ModRegistry.cs
+++ b/src/SMAPI/Framework/ModRegistry.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Framework
internal class ModRegistry
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The registered mod data.</summary>
private readonly List<IModMetadata> Mods = new List<IModMetadata>();
diff --git a/src/SMAPI/Framework/Monitor.cs b/src/SMAPI/Framework/Monitor.cs
index a4d92e4b..47ebc2d7 100644
--- a/src/SMAPI/Framework/Monitor.cs
+++ b/src/SMAPI/Framework/Monitor.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Framework
internal class Monitor : IMonitor
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The name of the module which logs messages using this instance.</summary>
private readonly string Source;
diff --git a/src/SMAPI/Framework/Networking/MultiplayerPeer.cs b/src/SMAPI/Framework/Networking/MultiplayerPeer.cs
index 44a71978..b4e39379 100644
--- a/src/SMAPI/Framework/Networking/MultiplayerPeer.cs
+++ b/src/SMAPI/Framework/Networking/MultiplayerPeer.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.Networking
internal class MultiplayerPeer : IMultiplayerPeer
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>A method which sends a message to the peer.</summary>
private readonly Action<OutgoingMessage> SendMessageImpl;
diff --git a/src/SMAPI/Framework/Networking/SGalaxyNetClient.cs b/src/SMAPI/Framework/Networking/SGalaxyNetClient.cs
index fddd423d..01095c66 100644
--- a/src/SMAPI/Framework/Networking/SGalaxyNetClient.cs
+++ b/src/SMAPI/Framework/Networking/SGalaxyNetClient.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.Networking
internal class SGalaxyNetClient : GalaxyNetClient
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>A callback to raise when receiving a message. This receives the incoming message, a method to send an arbitrary message, and a callback to run the default logic.</summary>
private readonly Action<IncomingMessage, Action<OutgoingMessage>, Action> OnProcessingMessage;
diff --git a/src/SMAPI/Framework/Networking/SGalaxyNetServer.cs b/src/SMAPI/Framework/Networking/SGalaxyNetServer.cs
index 82d11938..bb67f70e 100644
--- a/src/SMAPI/Framework/Networking/SGalaxyNetServer.cs
+++ b/src/SMAPI/Framework/Networking/SGalaxyNetServer.cs
@@ -11,7 +11,7 @@ namespace StardewModdingAPI.Framework.Networking
internal class SGalaxyNetServer : GalaxyNetServer
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>A callback to raise when receiving a message. This receives the incoming message, a method to send a message, and a callback to run the default logic.</summary>
private readonly Action<IncomingMessage, Action<OutgoingMessage>, Action> OnProcessingMessage;
diff --git a/src/SMAPI/Framework/Networking/SLidgrenClient.cs b/src/SMAPI/Framework/Networking/SLidgrenClient.cs
index 02d9d68f..39876744 100644
--- a/src/SMAPI/Framework/Networking/SLidgrenClient.cs
+++ b/src/SMAPI/Framework/Networking/SLidgrenClient.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Framework.Networking
internal class SLidgrenClient : LidgrenClient
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>A callback to raise when receiving a message. This receives the incoming message, a method to send an arbitrary message, and a callback to run the default logic.</summary>
private readonly Action<IncomingMessage, Action<OutgoingMessage>, Action> OnProcessingMessage;
diff --git a/src/SMAPI/Framework/Networking/SLidgrenServer.cs b/src/SMAPI/Framework/Networking/SLidgrenServer.cs
index 251e5268..1bce47fe 100644
--- a/src/SMAPI/Framework/Networking/SLidgrenServer.cs
+++ b/src/SMAPI/Framework/Networking/SLidgrenServer.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Framework.Networking
internal class SLidgrenServer : LidgrenServer
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>SMAPI's implementation of the game's core multiplayer logic.</summary>
private readonly SMultiplayer Multiplayer;
diff --git a/src/SMAPI/Framework/Patching/GamePatcher.cs b/src/SMAPI/Framework/Patching/GamePatcher.cs
index 71ca8e55..f82159d0 100644
--- a/src/SMAPI/Framework/Patching/GamePatcher.cs
+++ b/src/SMAPI/Framework/Patching/GamePatcher.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Framework.Patching
internal class GamePatcher
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Encapsulates monitoring and logging.</summary>
private readonly IMonitor Monitor;
diff --git a/src/SMAPI/Framework/Reflection/CacheEntry.cs b/src/SMAPI/Framework/Reflection/CacheEntry.cs
index 30faca37..912662e3 100644
--- a/src/SMAPI/Framework/Reflection/CacheEntry.cs
+++ b/src/SMAPI/Framework/Reflection/CacheEntry.cs
@@ -3,16 +3,16 @@ using System.Reflection;
namespace StardewModdingAPI.Framework.Reflection
{
/// <summary>A cached member reflection result.</summary>
- internal struct CacheEntry
+ internal readonly struct CacheEntry
{
/*********
** Accessors
*********/
/// <summary>Whether the lookup found a valid match.</summary>
- public bool IsValid;
+ public bool IsValid { get; }
/// <summary>The reflection data for this member (or <c>null</c> if invalid).</summary>
- public MemberInfo MemberInfo;
+ public MemberInfo MemberInfo { get; }
/*********
diff --git a/src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs b/src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs
index 7a2958fb..70ef81f8 100644
--- a/src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs
+++ b/src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.Reflection
internal class InterfaceProxyBuilder
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The target class type.</summary>
private readonly Type TargetType;
diff --git a/src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs b/src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs
index e14a9f08..464367b6 100644
--- a/src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs
+++ b/src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.Reflection
internal class InterfaceProxyFactory
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The CLR module in which to create proxy classes.</summary>
private readonly ModuleBuilder ModuleBuilder;
diff --git a/src/SMAPI/Framework/Reflection/ReflectedField.cs b/src/SMAPI/Framework/Reflection/ReflectedField.cs
index 09638b1d..d771422c 100644
--- a/src/SMAPI/Framework/Reflection/ReflectedField.cs
+++ b/src/SMAPI/Framework/Reflection/ReflectedField.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.Reflection
internal class ReflectedField<TValue> : IReflectedField<TValue>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The type that has the field.</summary>
private readonly Type ParentType;
diff --git a/src/SMAPI/Framework/Reflection/ReflectedMethod.cs b/src/SMAPI/Framework/Reflection/ReflectedMethod.cs
index 7d9072a0..039f27c3 100644
--- a/src/SMAPI/Framework/Reflection/ReflectedMethod.cs
+++ b/src/SMAPI/Framework/Reflection/ReflectedMethod.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Framework.Reflection
internal class ReflectedMethod : IReflectedMethod
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The type that has the method.</summary>
private readonly Type ParentType;
diff --git a/src/SMAPI/Framework/Reflection/ReflectedProperty.cs b/src/SMAPI/Framework/Reflection/ReflectedProperty.cs
index d59b71ac..8a10ff9a 100644
--- a/src/SMAPI/Framework/Reflection/ReflectedProperty.cs
+++ b/src/SMAPI/Framework/Reflection/ReflectedProperty.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.Reflection
internal class ReflectedProperty<TValue> : IReflectedProperty<TValue>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The display name shown in error messages.</summary>
private readonly string DisplayName;
diff --git a/src/SMAPI/Framework/Reflection/Reflector.cs b/src/SMAPI/Framework/Reflection/Reflector.cs
index 910e3a54..ed1a4381 100644
--- a/src/SMAPI/Framework/Reflection/Reflector.cs
+++ b/src/SMAPI/Framework/Reflection/Reflector.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Framework.Reflection
internal class Reflector
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The cached fields and methods found via reflection.</summary>
private readonly MemoryCache Cache = new MemoryCache(typeof(Reflector).FullName);
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 679838ba..27c0c40b 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -42,7 +42,7 @@ namespace StardewModdingAPI.Framework
internal class SCore : IDisposable
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The log file to which to write messages.</summary>
private readonly LogFileManager LogFile;
@@ -181,12 +181,6 @@ namespace StardewModdingAPI.Framework
return;
}
#endif
-
- // apply game patches
- new GamePatcher(this.Monitor).Apply(
- new DialogueErrorPatch(this.MonitorForGame, this.Reflection),
- new ObjectErrorPatch()
- );
}
/// <summary>Launch SMAPI.</summary>
@@ -237,6 +231,13 @@ namespace StardewModdingAPI.Framework
this.GameInstance = new SGame(this.Monitor, this.MonitorForGame, this.Reflection, this.EventManager, this.Toolkit.JsonHelper, this.ModRegistry, SCore.DeprecationManager, this.OnLocaleChanged, this.InitialiseAfterGameStart, this.Dispose);
StardewValley.Program.gamePtr = this.GameInstance;
+ // apply game patches
+ new GamePatcher(this.Monitor).Apply(
+ new DialogueErrorPatch(this.MonitorForGame, this.Reflection),
+ new ObjectErrorPatch(),
+ new LoadForNewGamePatch(this.Reflection, this.GameInstance.OnLoadStageChanged)
+ );
+
// add exit handler
new Thread(() =>
{
@@ -928,7 +929,7 @@ namespace StardewModdingAPI.Framework
// add deprecation warning for old version format
{
if (mod.Manifest?.Version is Toolkit.SemanticVersion version && version.IsLegacyFormat)
- SCore.DeprecationManager.Warn(mod.DisplayName, "non-string manifest version", "2.8", DeprecationLevel.Notice);
+ SCore.DeprecationManager.Warn(mod.DisplayName, "non-string manifest version", "2.8", DeprecationLevel.Info);
}
#endif
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index d515d3ad..e2835a70 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -39,7 +39,7 @@ namespace StardewModdingAPI.Framework
internal class SGame : Game1
{
/*********
- ** Properties
+ ** Fields
*********/
/****
** SMAPI state
@@ -69,8 +69,8 @@ namespace StardewModdingAPI.Framework
/// <remarks>Skipping a few frames ensures the game finishes initialising the world before mods try to change it.</remarks>
private readonly Countdown AfterLoadTimer = new Countdown(5);
- /// <summary>Whether the after-load events were raised for this session.</summary>
- private bool RaisedAfterLoadEvent;
+ /// <summary>The current stage in the game's loading process.</summary>
+ private LoadStage LoadStage = LoadStage.None;
/// <summary>Whether the game is saving and SMAPI has already raised <see cref="IGameLoopEvents.Saving"/>.</summary>
private bool IsBetweenSaveEvents;
@@ -96,15 +96,9 @@ namespace StardewModdingAPI.Framework
/// <summary>Monitors the entire game state for changes.</summary>
private WatcherCore Watchers;
- /// <summary>An index incremented on every tick and reset every 60th tick (0–59).</summary>
- private int CurrentUpdateTick;
-
/// <summary>Whether post-game-startup initialisation has been performed.</summary>
private bool IsInitialised;
- /// <summary>The number of update ticks which have already executed.</summary>
- private uint TicksElapsed;
-
/// <summary>Whether the next content manager requested by the game will be for <see cref="Game1.content"/>.</summary>
private bool NextContentManagerIsMain;
@@ -213,15 +207,33 @@ namespace StardewModdingAPI.Framework
this.Events.ModMessageReceived.RaiseForMods(new ModMessageReceivedEventArgs(message), mod => mod != null && modIDs.Contains(mod.Manifest.UniqueID));
}
- /// <summary>A callback raised when the player quits a save and returns to the title screen.</summary>
- private void OnReturnedToTitle()
+ /// <summary>A callback invoked when the game's low-level load stage changes.</summary>
+ /// <param name="newStage">The new load stage.</param>
+ internal void OnLoadStageChanged(LoadStage newStage)
{
- this.Monitor.Log("Context: returned to title", LogLevel.Trace);
- this.Multiplayer.CleanupOnMultiplayerExit();
- this.Events.ReturnedToTitle.RaiseEmpty();
+ // nothing to do
+ if (newStage == this.LoadStage)
+ return;
+
+ // update data
+ LoadStage oldStage = this.LoadStage;
+ this.LoadStage = newStage;
+ if (newStage == LoadStage.None)
+ {
+ this.Monitor.Log("Context: returned to title", LogLevel.Trace);
+ this.Multiplayer.CleanupOnMultiplayerExit();
+ }
+ this.Monitor.VerboseLog($"Context: load stage changed to {newStage}");
+
+ // raise events
+ this.Events.LoadStageChanged.Raise(new LoadStageChangedEventArgs(oldStage, newStage));
+ if (newStage == LoadStage.None)
+ {
+ this.Events.ReturnedToTitle.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_AfterReturnToTitle.Raise();
+ this.Events.Legacy_AfterReturnToTitle.Raise();
#endif
+ }
}
/// <summary>Constructor a content manager to read XNB files.</summary>
@@ -253,6 +265,8 @@ namespace StardewModdingAPI.Framework
/// <param name="gameTime">A snapshot of the game timing state.</param>
protected override void Update(GameTime gameTime)
{
+ var events = this.Events;
+
try
{
this.DeprecationManager.PrintQueued();
@@ -280,7 +294,29 @@ namespace StardewModdingAPI.Framework
{
this.Monitor.Log("Game loader synchronising...", LogLevel.Trace);
while (Game1.currentLoader?.MoveNext() == true)
- ;
+ {
+ // raise load stage changed
+ switch (Game1.currentLoader.Current)
+ {
+ case 20:
+ this.OnLoadStageChanged(LoadStage.SaveParsed);
+ break;
+
+ case 36:
+ this.OnLoadStageChanged(LoadStage.SaveLoadedBasicInfo);
+ break;
+
+ case 50:
+ this.OnLoadStageChanged(LoadStage.SaveLoadedLocations);
+ break;
+
+ default:
+ if (Game1.gameMode == Game1.playingGameMode)
+ this.OnLoadStageChanged(LoadStage.Preloaded);
+ break;
+ }
+ }
+
Game1.currentLoader = null;
this.Monitor.Log("Game loader done.", LogLevel.Trace);
}
@@ -302,11 +338,11 @@ namespace StardewModdingAPI.Framework
// update tick are neglible and not worth the complications of bypassing Game1.Update.
if (Game1._newDayTask != null || Game1.gameMode == Game1.loadingMode)
{
- this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed));
+ events.UnvalidatedUpdateTicking.RaiseEmpty();
base.Update(gameTime);
- this.Events.UnvalidatedUpdateTicked.Raise(new UnvalidatedUpdateTickedEventArgs(this.TicksElapsed));
+ events.UnvalidatedUpdateTicked.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_UnvalidatedUpdateTick.Raise();
+ events.Legacy_UnvalidatedUpdateTick.Raise();
#endif
return;
}
@@ -376,9 +412,9 @@ namespace StardewModdingAPI.Framework
{
this.IsBetweenCreateEvents = true;
this.Monitor.Log("Context: before save creation.", LogLevel.Trace);
- this.Events.SaveCreating.RaiseEmpty();
+ events.SaveCreating.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_BeforeCreateSave.Raise();
+ events.Legacy_BeforeCreateSave.Raise();
#endif
}
@@ -387,18 +423,18 @@ namespace StardewModdingAPI.Framework
{
this.IsBetweenSaveEvents = true;
this.Monitor.Log("Context: before save.", LogLevel.Trace);
- this.Events.Saving.RaiseEmpty();
+ events.Saving.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_BeforeSave.Raise();
+ events.Legacy_BeforeSave.Raise();
#endif
}
// suppress non-save events
- this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed));
+ events.UnvalidatedUpdateTicking.RaiseEmpty();
base.Update(gameTime);
- this.Events.UnvalidatedUpdateTicked.Raise(new UnvalidatedUpdateTickedEventArgs(this.TicksElapsed));
+ events.UnvalidatedUpdateTicked.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_UnvalidatedUpdateTick.Raise();
+ events.Legacy_UnvalidatedUpdateTick.Raise();
#endif
return;
}
@@ -407,9 +443,10 @@ namespace StardewModdingAPI.Framework
// raise after-create
this.IsBetweenCreateEvents = false;
this.Monitor.Log($"Context: after save creation, starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace);
- this.Events.SaveCreated.RaiseEmpty();
+ this.OnLoadStageChanged(LoadStage.CreatedSaveFile);
+ events.SaveCreated.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_AfterCreateSave.Raise();
+ events.Legacy_AfterCreateSave.Raise();
#endif
}
if (this.IsBetweenSaveEvents)
@@ -417,11 +454,11 @@ namespace StardewModdingAPI.Framework
// raise after-save
this.IsBetweenSaveEvents = false;
this.Monitor.Log($"Context: after save, starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace);
- this.Events.Saved.RaiseEmpty();
- this.Events.DayStarted.RaiseEmpty();
+ events.Saved.RaiseEmpty();
+ events.DayStarted.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_AfterSave.Raise();
- this.Events.Legacy_AfterDayStarted.Raise();
+ events.Legacy_AfterSave.Raise();
+ events.Legacy_AfterDayStarted.Raise();
#endif
}
@@ -430,7 +467,10 @@ namespace StardewModdingAPI.Framework
*********/
bool wasWorldReady = Context.IsWorldReady;
if ((Context.IsWorldReady && !Context.IsSaveLoaded) || Game1.exitToTitle)
- this.MarkWorldNotReady();
+ {
+ Context.IsWorldReady = false;
+ this.AfterLoadTimer.Reset();
+ }
else if (Context.IsSaveLoaded && this.AfterLoadTimer.Current > 0 && Game1.currentLocation != null)
{
if (Game1.dayOfMonth != 0) // wait until new-game intro finishes (world not fully initialised yet)
@@ -455,7 +495,7 @@ namespace StardewModdingAPI.Framework
this.OnLocaleChanged();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_LocaleChanged.Raise(new EventArgsValueChanged<string>(was.ToString(), now.ToString()));
+ events.Legacy_LocaleChanged.Raise(new EventArgsValueChanged<string>(was.ToString(), now.ToString()));
#endif
this.Watchers.LocaleWatcher.Reset();
@@ -465,8 +505,8 @@ namespace StardewModdingAPI.Framework
** Load / return-to-title events
*********/
if (wasWorldReady && !Context.IsWorldReady)
- this.OnReturnedToTitle();
- else if (!this.RaisedAfterLoadEvent && Context.IsWorldReady)
+ this.OnLoadStageChanged(LoadStage.None);
+ else if (Context.IsWorldReady && this.LoadStage != LoadStage.Ready)
{
// print context
string context = $"Context: loaded saved game '{Constants.SaveFolderName}', starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.";
@@ -480,12 +520,12 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log(context, LogLevel.Trace);
// raise events
- this.RaisedAfterLoadEvent = true;
- this.Events.SaveLoaded.RaiseEmpty();
- this.Events.DayStarted.RaiseEmpty();
+ this.OnLoadStageChanged(LoadStage.Ready);
+ events.SaveLoaded.RaiseEmpty();
+ events.DayStarted.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_AfterLoad.Raise();
- this.Events.Legacy_AfterDayStarted.Raise();
+ events.Legacy_AfterLoad.Raise();
+ events.Legacy_AfterDayStarted.Raise();
#endif
}
@@ -504,9 +544,9 @@ namespace StardewModdingAPI.Framework
Point oldSize = this.Watchers.WindowSizeWatcher.PreviousValue;
Point newSize = this.Watchers.WindowSizeWatcher.CurrentValue;
- this.Events.WindowResized.Raise(new WindowResizedEventArgs(oldSize, newSize));
+ events.WindowResized.Raise(new WindowResizedEventArgs(oldSize, newSize));
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_Resize.Raise();
+ events.Legacy_Resize.Raise();
#endif
this.Watchers.WindowSizeWatcher.Reset();
}
@@ -525,23 +565,33 @@ namespace StardewModdingAPI.Framework
// raise cursor moved event
if (this.Watchers.CursorWatcher.IsChanged)
{
- ICursorPosition was = this.Watchers.CursorWatcher.PreviousValue;
- ICursorPosition now = this.Watchers.CursorWatcher.CurrentValue;
- this.Watchers.CursorWatcher.Reset();
+ if (events.CursorMoved.HasListeners())
+ {
+ ICursorPosition was = this.Watchers.CursorWatcher.PreviousValue;
+ ICursorPosition now = this.Watchers.CursorWatcher.CurrentValue;
+ this.Watchers.CursorWatcher.Reset();
- this.Events.CursorMoved.Raise(new CursorMovedEventArgs(was, now));
+ events.CursorMoved.Raise(new CursorMovedEventArgs(was, now));
+ }
+ else
+ this.Watchers.CursorWatcher.Reset();
}
// raise mouse wheel scrolled
if (this.Watchers.MouseWheelScrollWatcher.IsChanged)
{
- int was = this.Watchers.MouseWheelScrollWatcher.PreviousValue;
- int now = this.Watchers.MouseWheelScrollWatcher.CurrentValue;
- this.Watchers.MouseWheelScrollWatcher.Reset();
+ if (events.MouseWheelScrolled.HasListeners() || this.Monitor.IsVerbose)
+ {
+ int was = this.Watchers.MouseWheelScrollWatcher.PreviousValue;
+ int now = this.Watchers.MouseWheelScrollWatcher.CurrentValue;
+ this.Watchers.MouseWheelScrollWatcher.Reset();
- if (this.Monitor.IsVerbose)
- this.Monitor.Log($"Events: mouse wheel scrolled to {now}.", LogLevel.Trace);
- this.Events.MouseWheelScrolled.Raise(new MouseWheelScrolledEventArgs(cursor, was, now));
+ if (this.Monitor.IsVerbose)
+ this.Monitor.Log($"Events: mouse wheel scrolled to {now}.", LogLevel.Trace);
+ events.MouseWheelScrolled.Raise(new MouseWheelScrolledEventArgs(cursor, was, now));
+ }
+ else
+ this.Watchers.MouseWheelScrollWatcher.Reset();
}
// raise input button events
@@ -555,22 +605,22 @@ namespace StardewModdingAPI.Framework
if (this.Monitor.IsVerbose)
this.Monitor.Log($"Events: button {button} pressed.", LogLevel.Trace);
- this.Events.ButtonPressed.Raise(new ButtonPressedEventArgs(button, cursor, inputState));
+ events.ButtonPressed.Raise(new ButtonPressedEventArgs(button, cursor, inputState));
#if !SMAPI_3_0_STRICT
// legacy events
- this.Events.Legacy_ButtonPressed.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons));
+ events.Legacy_ButtonPressed.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons));
if (button.TryGetKeyboard(out Keys key))
{
if (key != Keys.None)
- this.Events.Legacy_KeyPressed.Raise(new EventArgsKeyPressed(key));
+ events.Legacy_KeyPressed.Raise(new EventArgsKeyPressed(key));
}
else if (button.TryGetController(out Buttons controllerButton))
{
if (controllerButton == Buttons.LeftTrigger || controllerButton == Buttons.RightTrigger)
- this.Events.Legacy_ControllerTriggerPressed.Raise(new EventArgsControllerTriggerPressed(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.RealController.Triggers.Left : inputState.RealController.Triggers.Right));
+ events.Legacy_ControllerTriggerPressed.Raise(new EventArgsControllerTriggerPressed(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.RealController.Triggers.Left : inputState.RealController.Triggers.Right));
else
- this.Events.Legacy_ControllerButtonPressed.Raise(new EventArgsControllerButtonPressed(PlayerIndex.One, controllerButton));
+ events.Legacy_ControllerButtonPressed.Raise(new EventArgsControllerButtonPressed(PlayerIndex.One, controllerButton));
}
#endif
}
@@ -579,22 +629,22 @@ namespace StardewModdingAPI.Framework
if (this.Monitor.IsVerbose)
this.Monitor.Log($"Events: button {button} released.", LogLevel.Trace);
- this.Events.ButtonReleased.Raise(new ButtonReleasedEventArgs(button, cursor, inputState));
+ events.ButtonReleased.Raise(new ButtonReleasedEventArgs(button, cursor, inputState));
#if !SMAPI_3_0_STRICT
// legacy events
- this.Events.Legacy_ButtonReleased.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons));
+ events.Legacy_ButtonReleased.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons));
if (button.TryGetKeyboard(out Keys key))
{
if (key != Keys.None)
- this.Events.Legacy_KeyReleased.Raise(new EventArgsKeyPressed(key));
+ events.Legacy_KeyReleased.Raise(new EventArgsKeyPressed(key));
}
else if (button.TryGetController(out Buttons controllerButton))
{
if (controllerButton == Buttons.LeftTrigger || controllerButton == Buttons.RightTrigger)
- this.Events.Legacy_ControllerTriggerReleased.Raise(new EventArgsControllerTriggerReleased(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.RealController.Triggers.Left : inputState.RealController.Triggers.Right));
+ events.Legacy_ControllerTriggerReleased.Raise(new EventArgsControllerTriggerReleased(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.RealController.Triggers.Left : inputState.RealController.Triggers.Right));
else
- this.Events.Legacy_ControllerButtonReleased.Raise(new EventArgsControllerButtonReleased(PlayerIndex.One, controllerButton));
+ events.Legacy_ControllerButtonReleased.Raise(new EventArgsControllerButtonReleased(PlayerIndex.One, controllerButton));
}
#endif
}
@@ -603,9 +653,9 @@ namespace StardewModdingAPI.Framework
#if !SMAPI_3_0_STRICT
// raise legacy state-changed events
if (inputState.RealKeyboard != previousInputState.RealKeyboard)
- this.Events.Legacy_KeyboardChanged.Raise(new EventArgsKeyboardStateChanged(previousInputState.RealKeyboard, inputState.RealKeyboard));
+ events.Legacy_KeyboardChanged.Raise(new EventArgsKeyboardStateChanged(previousInputState.RealKeyboard, inputState.RealKeyboard));
if (inputState.RealMouse != previousInputState.RealMouse)
- this.Events.Legacy_MouseChanged.Raise(new EventArgsMouseStateChanged(previousInputState.RealMouse, inputState.RealMouse, new Point((int)previousInputState.CursorPosition.ScreenPixels.X, (int)previousInputState.CursorPosition.ScreenPixels.Y), new Point((int)inputState.CursorPosition.ScreenPixels.X, (int)inputState.CursorPosition.ScreenPixels.Y)));
+ events.Legacy_MouseChanged.Raise(new EventArgsMouseStateChanged(previousInputState.RealMouse, inputState.RealMouse, new Point((int)previousInputState.CursorPosition.ScreenPixels.X, (int)previousInputState.CursorPosition.ScreenPixels.Y), new Point((int)inputState.CursorPosition.ScreenPixels.X, (int)inputState.CursorPosition.ScreenPixels.Y)));
#endif
}
}
@@ -623,12 +673,12 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Context: menu changed from {was?.GetType().FullName ?? "none"} to {now?.GetType().FullName ?? "none"}.", LogLevel.Trace);
// raise menu events
- this.Events.MenuChanged.Raise(new MenuChangedEventArgs(was, now));
+ events.MenuChanged.Raise(new MenuChangedEventArgs(was, now));
#if !SMAPI_3_0_STRICT
if (now != null)
- this.Events.Legacy_MenuChanged.Raise(new EventArgsClickableMenuChanged(was, now));
+ events.Legacy_MenuChanged.Raise(new EventArgsClickableMenuChanged(was, now));
else
- this.Events.Legacy_MenuClosed.Raise(new EventArgsClickableMenuClosed(was));
+ events.Legacy_MenuClosed.Raise(new EventArgsClickableMenuClosed(was));
#endif
}
@@ -656,9 +706,9 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Context: location list changed (added {addedText}; removed {removedText}).", LogLevel.Trace);
}
- this.Events.LocationListChanged.Raise(new LocationListChangedEventArgs(added, removed));
+ events.LocationListChanged.Raise(new LocationListChangedEventArgs(added, removed));
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_LocationsChanged.Raise(new EventArgsLocationsChanged(added, removed));
+ events.Legacy_LocationsChanged.Raise(new EventArgsLocationsChanged(added, removed));
#endif
}
@@ -675,9 +725,9 @@ namespace StardewModdingAPI.Framework
Building[] removed = watcher.BuildingsWatcher.Removed.ToArray();
watcher.BuildingsWatcher.Reset();
- this.Events.BuildingListChanged.Raise(new BuildingListChangedEventArgs(location, added, removed));
+ events.BuildingListChanged.Raise(new BuildingListChangedEventArgs(location, added, removed));
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_BuildingsChanged.Raise(new EventArgsLocationBuildingsChanged(location, added, removed));
+ events.Legacy_BuildingsChanged.Raise(new EventArgsLocationBuildingsChanged(location, added, removed));
#endif
}
@@ -689,7 +739,7 @@ namespace StardewModdingAPI.Framework
Debris[] removed = watcher.DebrisWatcher.Removed.ToArray();
watcher.DebrisWatcher.Reset();
- this.Events.DebrisListChanged.Raise(new DebrisListChangedEventArgs(location, added, removed));
+ events.DebrisListChanged.Raise(new DebrisListChangedEventArgs(location, added, removed));
}
// large terrain features changed
@@ -700,7 +750,7 @@ namespace StardewModdingAPI.Framework
LargeTerrainFeature[] removed = watcher.LargeTerrainFeaturesWatcher.Removed.ToArray();
watcher.LargeTerrainFeaturesWatcher.Reset();
- this.Events.LargeTerrainFeatureListChanged.Raise(new LargeTerrainFeatureListChangedEventArgs(location, added, removed));
+ events.LargeTerrainFeatureListChanged.Raise(new LargeTerrainFeatureListChangedEventArgs(location, added, removed));
}
// NPCs changed
@@ -711,7 +761,7 @@ namespace StardewModdingAPI.Framework
NPC[] removed = watcher.NpcsWatcher.Removed.ToArray();
watcher.NpcsWatcher.Reset();
- this.Events.NpcListChanged.Raise(new NpcListChangedEventArgs(location, added, removed));
+ events.NpcListChanged.Raise(new NpcListChangedEventArgs(location, added, removed));
}
// objects changed
@@ -722,9 +772,9 @@ namespace StardewModdingAPI.Framework
KeyValuePair<Vector2, SObject>[] removed = watcher.ObjectsWatcher.Removed.ToArray();
watcher.ObjectsWatcher.Reset();
- this.Events.ObjectListChanged.Raise(new ObjectListChangedEventArgs(location, added, removed));
+ events.ObjectListChanged.Raise(new ObjectListChangedEventArgs(location, added, removed));
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_ObjectsChanged.Raise(new EventArgsLocationObjectsChanged(location, added, removed));
+ events.Legacy_ObjectsChanged.Raise(new EventArgsLocationObjectsChanged(location, added, removed));
#endif
}
@@ -736,7 +786,7 @@ namespace StardewModdingAPI.Framework
KeyValuePair<Vector2, TerrainFeature>[] removed = watcher.TerrainFeaturesWatcher.Removed.ToArray();
watcher.TerrainFeaturesWatcher.Reset();
- this.Events.TerrainFeatureListChanged.Raise(new TerrainFeatureListChangedEventArgs(location, added, removed));
+ events.TerrainFeatureListChanged.Raise(new TerrainFeatureListChangedEventArgs(location, added, removed));
}
}
}
@@ -754,9 +804,9 @@ namespace StardewModdingAPI.Framework
if (this.Monitor.IsVerbose)
this.Monitor.Log($"Events: time changed from {was} to {now}.", LogLevel.Trace);
- this.Events.TimeChanged.Raise(new TimeChangedEventArgs(was, now));
+ events.TimeChanged.Raise(new TimeChangedEventArgs(was, now));
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_TimeOfDayChanged.Raise(new EventArgsIntChanged(was, now));
+ events.Legacy_TimeOfDayChanged.Raise(new EventArgsIntChanged(was, now));
#endif
}
else
@@ -774,9 +824,9 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Context: set location to {newLocation.Name}.", LogLevel.Trace);
GameLocation oldLocation = playerTracker.LocationWatcher.PreviousValue;
- this.Events.Warped.Raise(new WarpedEventArgs(playerTracker.Player, oldLocation, newLocation));
+ events.Warped.Raise(new WarpedEventArgs(playerTracker.Player, oldLocation, newLocation));
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_PlayerWarped.Raise(new EventArgsPlayerWarped(oldLocation, newLocation));
+ events.Legacy_PlayerWarped.Raise(new EventArgsPlayerWarped(oldLocation, newLocation));
#endif
}
@@ -786,9 +836,9 @@ namespace StardewModdingAPI.Framework
if (this.Monitor.IsVerbose)
this.Monitor.Log($"Events: player skill '{pair.Key}' changed from {pair.Value.PreviousValue} to {pair.Value.CurrentValue}.", LogLevel.Trace);
- this.Events.LevelChanged.Raise(new LevelChangedEventArgs(playerTracker.Player, pair.Key, pair.Value.PreviousValue, pair.Value.CurrentValue));
+ events.LevelChanged.Raise(new LevelChangedEventArgs(playerTracker.Player, pair.Key, pair.Value.PreviousValue, pair.Value.CurrentValue));
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_LeveledUp.Raise(new EventArgsLevelUp((EventArgsLevelUp.LevelType)pair.Key, pair.Value.CurrentValue));
+ events.Legacy_LeveledUp.Raise(new EventArgsLevelUp((EventArgsLevelUp.LevelType)pair.Key, pair.Value.CurrentValue));
#endif
}
@@ -798,9 +848,9 @@ namespace StardewModdingAPI.Framework
{
if (this.Monitor.IsVerbose)
this.Monitor.Log("Events: player inventory changed.", LogLevel.Trace);
- this.Events.InventoryChanged.Raise(new InventoryChangedEventArgs(playerTracker.Player, changedItems));
+ events.InventoryChanged.Raise(new InventoryChangedEventArgs(playerTracker.Player, changedItems));
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_InventoryChanged.Raise(new EventArgsInventoryChanged(Game1.player.Items, changedItems));
+ events.Legacy_InventoryChanged.Raise(new EventArgsInventoryChanged(Game1.player.Items, changedItems));
#endif
}
@@ -810,7 +860,7 @@ namespace StardewModdingAPI.Framework
if (this.Monitor.IsVerbose)
this.Monitor.Log($"Context: mine level changed to {mineLevel}.", LogLevel.Trace);
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_MineLevelChanged.Raise(new EventArgsMineLevelChanged(playerTracker.MineLevelWatcher.PreviousValue, mineLevel));
+ events.Legacy_MineLevelChanged.Raise(new EventArgsMineLevelChanged(playerTracker.MineLevelWatcher.PreviousValue, mineLevel));
#endif
}
}
@@ -823,11 +873,21 @@ namespace StardewModdingAPI.Framework
/*********
** Game update
*********/
- this.TicksElapsed++;
- if (this.TicksElapsed == 1)
- this.Events.GameLaunched.Raise(new GameLaunchedEventArgs());
- this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed));
- this.Events.UpdateTicking.Raise(new UpdateTickingEventArgs(this.TicksElapsed));
+ // game launched
+ bool isFirstTick = Game1.ticks == 0;
+ if (isFirstTick)
+ events.GameLaunched.Raise(new GameLaunchedEventArgs());
+
+ // preloaded
+ if (Context.IsSaveLoaded && this.LoadStage != LoadStage.Loaded && this.LoadStage != LoadStage.Ready)
+ this.OnLoadStageChanged(LoadStage.Loaded);
+
+ // update tick
+ bool isOneSecond = Game1.ticks % 60 == 0;
+ events.UnvalidatedUpdateTicking.RaiseEmpty();
+ events.UpdateTicking.RaiseEmpty();
+ if (isOneSecond)
+ events.OneSecondUpdateTicking.RaiseEmpty();
try
{
this.Input.UpdateSuppression();
@@ -837,33 +897,32 @@ namespace StardewModdingAPI.Framework
{
this.MonitorForGame.Log($"An error occured in the base update loop: {ex.GetLogSummary()}", LogLevel.Error);
}
- this.Events.UnvalidatedUpdateTicked.Raise(new UnvalidatedUpdateTickedEventArgs(this.TicksElapsed));
- this.Events.UpdateTicked.Raise(new UpdateTickedEventArgs(this.TicksElapsed));
+ events.UnvalidatedUpdateTicked.RaiseEmpty();
+ events.UpdateTicked.RaiseEmpty();
+ if (isOneSecond)
+ events.OneSecondUpdateTicked.RaiseEmpty();
/*********
** Update events
*********/
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_UnvalidatedUpdateTick.Raise();
- if (this.TicksElapsed == 1)
- this.Events.Legacy_FirstUpdateTick.Raise();
- this.Events.Legacy_UpdateTick.Raise();
- if (this.CurrentUpdateTick % 2 == 0)
- this.Events.Legacy_SecondUpdateTick.Raise();
- if (this.CurrentUpdateTick % 4 == 0)
- this.Events.Legacy_FourthUpdateTick.Raise();
- if (this.CurrentUpdateTick % 8 == 0)
- this.Events.Legacy_EighthUpdateTick.Raise();
- if (this.CurrentUpdateTick % 15 == 0)
- this.Events.Legacy_QuarterSecondTick.Raise();
- if (this.CurrentUpdateTick % 30 == 0)
- this.Events.Legacy_HalfSecondTick.Raise();
- if (this.CurrentUpdateTick % 60 == 0)
- this.Events.Legacy_OneSecondTick.Raise();
+ events.Legacy_UnvalidatedUpdateTick.Raise();
+ if (isFirstTick)
+ events.Legacy_FirstUpdateTick.Raise();
+ events.Legacy_UpdateTick.Raise();
+ if (Game1.ticks % 2 == 0)
+ events.Legacy_SecondUpdateTick.Raise();
+ if (Game1.ticks % 4 == 0)
+ events.Legacy_FourthUpdateTick.Raise();
+ if (Game1.ticks % 8 == 0)
+ events.Legacy_EighthUpdateTick.Raise();
+ if (Game1.ticks % 15 == 0)
+ events.Legacy_QuarterSecondTick.Raise();
+ if (Game1.ticks % 30 == 0)
+ events.Legacy_HalfSecondTick.Raise();
+ if (Game1.ticks % 60 == 0)
+ events.Legacy_OneSecondTick.Raise();
#endif
- this.CurrentUpdateTick += 1;
- if (this.CurrentUpdateTick >= 60)
- this.CurrentUpdateTick = 0;
this.UpdateCrashTimer.Reset();
}
@@ -931,10 +990,10 @@ namespace StardewModdingAPI.Framework
[SuppressMessage("SMAPI.CommonErrors", "AvoidImplicitNetFieldCast", Justification = "copied from game code as-is")]
private void DrawImpl(GameTime gameTime)
{
+ var events = this.Events;
+
if (Game1._newDayTask != null)
- {
this.GraphicsDevice.Clear(this.bgColor);
- }
else
{
if ((double)Game1.options.zoomLevel != 1.0)
@@ -946,17 +1005,17 @@ namespace StardewModdingAPI.Framework
if (activeClickableMenu != null)
{
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, (DepthStencilState)null, (RasterizerState)null);
- this.Events.Rendering.RaiseEmpty();
+ events.Rendering.RaiseEmpty();
try
{
- this.Events.RenderingActiveMenu.RaiseEmpty();
+ events.RenderingActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPreRenderGuiEvent.Raise();
+ events.Legacy_OnPreRenderGuiEvent.Raise();
#endif
activeClickableMenu.draw(Game1.spriteBatch);
- this.Events.RenderedActiveMenu.RaiseEmpty();
+ events.RenderedActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPostRenderGuiEvent.Raise();
+ events.Legacy_OnPostRenderGuiEvent.Raise();
#endif
}
catch (Exception ex)
@@ -964,9 +1023,9 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"The {activeClickableMenu.GetType().FullName} menu crashed while drawing itself during save. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error);
activeClickableMenu.exitThisMenu();
}
- this.Events.Rendered.RaiseEmpty();
+ events.Rendered.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPostRenderEvent.Raise();
+ events.Legacy_OnPostRenderEvent.Raise();
#endif
Game1.spriteBatch.End();
@@ -986,18 +1045,18 @@ namespace StardewModdingAPI.Framework
{
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, (DepthStencilState)null, (RasterizerState)null);
- this.Events.Rendering.RaiseEmpty();
+ events.Rendering.RaiseEmpty();
try
{
Game1.activeClickableMenu.drawBackground(Game1.spriteBatch);
- this.Events.RenderingActiveMenu.RaiseEmpty();
+ events.RenderingActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPreRenderGuiEvent.Raise();
+ events.Legacy_OnPreRenderGuiEvent.Raise();
#endif
Game1.activeClickableMenu.draw(Game1.spriteBatch);
- this.Events.RenderedActiveMenu.RaiseEmpty();
+ events.RenderedActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPostRenderGuiEvent.Raise();
+ events.Legacy_OnPostRenderGuiEvent.Raise();
#endif
}
catch (Exception ex)
@@ -1005,9 +1064,9 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error);
Game1.activeClickableMenu.exitThisMenu();
}
- this.Events.Rendered.RaiseEmpty();
+ events.Rendered.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPostRenderEvent.Raise();
+ events.Legacy_OnPostRenderEvent.Raise();
#endif
Game1.spriteBatch.End();
this.drawOverlays(Game1.spriteBatch);
@@ -1028,13 +1087,13 @@ namespace StardewModdingAPI.Framework
else if (Game1.gameMode == (byte)11)
{
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, (DepthStencilState)null, (RasterizerState)null);
- this.Events.Rendering.RaiseEmpty();
+ events.Rendering.RaiseEmpty();
Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.content.LoadString("Strings\\StringsFromCSFiles:Game1.cs.3685"), new Vector2(16f, 16f), Color.HotPink);
Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.content.LoadString("Strings\\StringsFromCSFiles:Game1.cs.3686"), new Vector2(16f, 32f), new Color(0, (int)byte.MaxValue, 0));
Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.parseText(Game1.errorMessage, Game1.dialogueFont, Game1.graphics.GraphicsDevice.Viewport.Width), new Vector2(16f, 48f), Color.White);
- this.Events.Rendered.RaiseEmpty();
+ events.Rendered.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPostRenderEvent.Raise();
+ events.Legacy_OnPostRenderEvent.Raise();
#endif
Game1.spriteBatch.End();
}
@@ -1062,19 +1121,19 @@ namespace StardewModdingAPI.Framework
else if (Game1.showingEndOfNightStuff)
{
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, (DepthStencilState)null, (RasterizerState)null);
- this.Events.Rendering.RaiseEmpty();
+ events.Rendering.RaiseEmpty();
if (Game1.activeClickableMenu != null)
{
try
{
- this.Events.RenderingActiveMenu.RaiseEmpty();
+ events.RenderingActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPreRenderGuiEvent.Raise();
+ events.Legacy_OnPreRenderGuiEvent.Raise();
#endif
Game1.activeClickableMenu.draw(Game1.spriteBatch);
- this.Events.RenderedActiveMenu.RaiseEmpty();
+ events.RenderedActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPostRenderGuiEvent.Raise();
+ events.Legacy_OnPostRenderGuiEvent.Raise();
#endif
}
catch (Exception ex)
@@ -1083,7 +1142,7 @@ namespace StardewModdingAPI.Framework
Game1.activeClickableMenu.exitThisMenu();
}
}
- this.Events.Rendered.RaiseEmpty();
+ events.Rendered.RaiseEmpty();
Game1.spriteBatch.End();
this.drawOverlays(Game1.spriteBatch);
if ((double)Game1.options.zoomLevel == 1.0)
@@ -1097,7 +1156,7 @@ namespace StardewModdingAPI.Framework
else if (Game1.gameMode == (byte)6 || Game1.gameMode == (byte)3 && Game1.currentLocation == null)
{
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, (DepthStencilState)null, (RasterizerState)null);
- this.Events.Rendering.RaiseEmpty();
+ events.Rendering.RaiseEmpty();
string str1 = "";
for (int index = 0; (double)index < gameTime.TotalGameTime.TotalMilliseconds % 999.0 / 333.0; ++index)
str1 += ".";
@@ -1109,7 +1168,7 @@ namespace StardewModdingAPI.Framework
int x = 64;
int y = Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Bottom - height;
SpriteText.drawString(Game1.spriteBatch, s, x, y, 999999, widthOfString, height, 1f, 0.88f, false, 0, str3, -1);
- this.Events.Rendered.RaiseEmpty();
+ events.Rendered.RaiseEmpty();
Game1.spriteBatch.End();
this.drawOverlays(Game1.spriteBatch);
if ((double)Game1.options.zoomLevel != 1.0)
@@ -1138,7 +1197,7 @@ namespace StardewModdingAPI.Framework
{
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, (DepthStencilState)null, (RasterizerState)null);
if (++batchOpens == 1)
- this.Events.Rendering.RaiseEmpty();
+ events.Rendering.RaiseEmpty();
}
else
{
@@ -1148,7 +1207,7 @@ namespace StardewModdingAPI.Framework
this.GraphicsDevice.Clear(Color.White * 0.0f);
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, (DepthStencilState)null, (RasterizerState)null);
if (++batchOpens == 1)
- this.Events.Rendering.RaiseEmpty();
+ events.Rendering.RaiseEmpty();
Game1.spriteBatch.Draw(Game1.staminaRect, Game1.lightmap.Bounds, Game1.currentLocation.Name.StartsWith("UndergroundMine") ? Game1.mine.getLightingColor(gameTime) : (Game1.ambientLight.Equals(Color.White) || Game1.isRaining && (bool)((NetFieldBase<bool, NetBool>)Game1.currentLocation.isOutdoors) ? Game1.outdoorLight : Game1.ambientLight));
for (int index = 0; index < Game1.currentLightSources.Count; ++index)
{
@@ -1163,10 +1222,10 @@ namespace StardewModdingAPI.Framework
this.GraphicsDevice.Clear(this.bgColor);
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, (DepthStencilState)null, (RasterizerState)null);
if (++batchOpens == 1)
- this.Events.Rendering.RaiseEmpty();
- this.Events.RenderingWorld.RaiseEmpty();
+ events.Rendering.RaiseEmpty();
+ events.RenderingWorld.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPreRenderEvent.Raise();
+ events.Legacy_OnPreRenderEvent.Raise();
#endif
if (Game1.background != null)
Game1.background.draw(Game1.spriteBatch);
@@ -1423,7 +1482,7 @@ namespace StardewModdingAPI.Framework
Game1.spriteBatch.End();
}
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, (DepthStencilState)null, (RasterizerState)null);
- this.Events.RenderedWorld.RaiseEmpty();
+ events.RenderedWorld.RaiseEmpty();
if (Game1.drawGrid)
{
int num1 = -Game1.viewport.X % 64;
@@ -1479,14 +1538,14 @@ namespace StardewModdingAPI.Framework
this.drawBillboard();
if ((Game1.displayHUD || Game1.eventUp) && (Game1.currentBillboard == 0 && Game1.gameMode == (byte)3) && (!Game1.freezeControls && !Game1.panMode && !Game1.HostPaused))
{
- this.Events.RenderingHud.RaiseEmpty();
+ events.RenderingHud.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPreRenderHudEvent.Raise();
+ events.Legacy_OnPreRenderHudEvent.Raise();
#endif
this.drawHUD();
- this.Events.RenderedHud.RaiseEmpty();
+ events.RenderedHud.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPostRenderHudEvent.Raise();
+ events.Legacy_OnPostRenderHudEvent.Raise();
#endif
}
else if (Game1.activeClickableMenu == null && Game1.farmEvent == null)
@@ -1595,14 +1654,14 @@ namespace StardewModdingAPI.Framework
{
try
{
- this.Events.RenderingActiveMenu.RaiseEmpty();
+ events.RenderingActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPreRenderGuiEvent.Raise();
+ events.Legacy_OnPreRenderGuiEvent.Raise();
#endif
Game1.activeClickableMenu.draw(Game1.spriteBatch);
- this.Events.RenderedActiveMenu.RaiseEmpty();
+ events.RenderedActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPostRenderGuiEvent.Raise();
+ events.Legacy_OnPostRenderGuiEvent.Raise();
#endif
}
catch (Exception ex)
@@ -1619,9 +1678,9 @@ namespace StardewModdingAPI.Framework
SpriteText.drawStringWithScrollBackground(Game1.spriteBatch, s, 96, 32, "", 1f, -1);
}
- this.Events.Rendered.RaiseEmpty();
+ events.Rendered.RaiseEmpty();
#if !SMAPI_3_0_STRICT
- this.Events.Legacy_OnPostRenderEvent.Raise();
+ events.Legacy_OnPostRenderEvent.Raise();
#endif
Game1.spriteBatch.End();
this.drawOverlays(Game1.spriteBatch);
@@ -1634,14 +1693,6 @@ namespace StardewModdingAPI.Framework
/****
** Methods
****/
- /// <summary>Perform any cleanup needed when a save is unloaded.</summary>
- private void MarkWorldNotReady()
- {
- Context.IsWorldReady = false;
- this.AfterLoadTimer.Reset();
- this.RaisedAfterLoadEvent = false;
- }
-
#if !SMAPI_3_0_STRICT
/// <summary>Raise the <see cref="GraphicsEvents.OnPostRenderEvent"/> if there are any listeners.</summary>
/// <param name="needsNewBatch">Whether to create a new sprite batch.</param>
diff --git a/src/SMAPI/Framework/SModHooks.cs b/src/SMAPI/Framework/SModHooks.cs
index 9f0201c8..7dafc746 100644
--- a/src/SMAPI/Framework/SModHooks.cs
+++ b/src/SMAPI/Framework/SModHooks.cs
@@ -7,7 +7,7 @@ namespace StardewModdingAPI.Framework
internal class SModHooks : ModHooks
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>A callback to invoke before <see cref="Game1.newDayAfterFade"/> runs.</summary>
private readonly Action BeforeNewDayAfterFade;
diff --git a/src/SMAPI/Framework/SMultiplayer.cs b/src/SMAPI/Framework/SMultiplayer.cs
index 29d9b2b8..0241ef02 100644
--- a/src/SMAPI/Framework/SMultiplayer.cs
+++ b/src/SMAPI/Framework/SMultiplayer.cs
@@ -31,7 +31,7 @@ namespace StardewModdingAPI.Framework
internal class SMultiplayer : Multiplayer
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Encapsulates monitoring and logging.</summary>
private readonly IMonitor Monitor;
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/BaseDisposableWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/BaseDisposableWatcher.cs
index 40ec6c57..60006c51 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/BaseDisposableWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/BaseDisposableWatcher.cs
@@ -6,7 +6,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
internal abstract class BaseDisposableWatcher : IDisposable
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Whether the watcher has been disposed.</summary>
protected bool IsDisposed { get; private set; }
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs
index 2ea6609a..6550f950 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
internal class ComparableListWatcher<TValue> : BaseDisposableWatcher, ICollectionWatcher<TValue>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The collection to watch.</summary>
private readonly ICollection<TValue> CurrentValues;
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs
index dda30a15..5ca4b9f4 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
internal class ComparableWatcher<TValue> : IValueWatcher<TValue>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Get the current value.</summary>
private readonly Func<TValue> GetValue;
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs
index d3022a69..21e84c47 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
where TValue : class, INetObject<INetSerializable>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The field being watched.</summary>
private readonly NetCollection<TValue> Field;
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs
index 7a7ab89d..e6882f7e 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs
@@ -15,7 +15,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
where TSelf : NetDictionary<TKey, TValue, TField, TSerialDict, TSelf>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The pairs added since the last reset.</summary>
private readonly IDictionary<TKey, TValue> PairsAdded = new Dictionary<TKey, TValue>();
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs
index 85099988..48d5d681 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
internal class NetValueWatcher<TValue, TNetField> : BaseDisposableWatcher, IValueWatcher<TValue> where TNetField : NetFieldBase<TValue, TNetField>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The field being watched.</summary>
private readonly NetFieldBase<TValue, TNetField> Field;
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs
index 0c65789f..883b1023 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
internal class ObservableCollectionWatcher<TValue> : BaseDisposableWatcher, ICollectionWatcher<TValue>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The field being watched.</summary>
private readonly ObservableCollection<TValue> Field;
diff --git a/src/SMAPI/Framework/StateTracking/LocationTracker.cs b/src/SMAPI/Framework/StateTracking/LocationTracker.cs
index 708c0716..2249e41b 100644
--- a/src/SMAPI/Framework/StateTracking/LocationTracker.cs
+++ b/src/SMAPI/Framework/StateTracking/LocationTracker.cs
@@ -15,7 +15,7 @@ namespace StardewModdingAPI.Framework.StateTracking
internal class LocationTracker : IWatcher
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying watchers.</summary>
private readonly List<IWatcher> Watchers = new List<IWatcher>();
diff --git a/src/SMAPI/Framework/StateTracking/PlayerTracker.cs b/src/SMAPI/Framework/StateTracking/PlayerTracker.cs
index 6a705e50..abb4fa24 100644
--- a/src/SMAPI/Framework/StateTracking/PlayerTracker.cs
+++ b/src/SMAPI/Framework/StateTracking/PlayerTracker.cs
@@ -14,7 +14,7 @@ namespace StardewModdingAPI.Framework.StateTracking
internal class PlayerTracker : IDisposable
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The player's inventory as of the last reset.</summary>
private IDictionary<Item, int> PreviousInventory;
diff --git a/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs b/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs
index 930a8102..f09c69c1 100644
--- a/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs
+++ b/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs
@@ -13,7 +13,7 @@ namespace StardewModdingAPI.Framework.StateTracking
internal class WorldLocationsTracker : IWatcher
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Tracks changes to the location list.</summary>
private readonly ICollectionWatcher<GameLocation> LocationListWatcher;
diff --git a/src/SMAPI/Framework/WatcherCore.cs b/src/SMAPI/Framework/WatcherCore.cs
index 8d29cf18..32b7fdc6 100644
--- a/src/SMAPI/Framework/WatcherCore.cs
+++ b/src/SMAPI/Framework/WatcherCore.cs
@@ -14,7 +14,7 @@ namespace StardewModdingAPI.Framework
internal class WatcherCore
{
/*********
- ** Properties
+ ** Fields
*********/
/// <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/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs
index a44ab7d1..d83fc748 100644
--- a/src/SMAPI/Metadata/CoreAssetPropagator.cs
+++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs
@@ -23,7 +23,7 @@ namespace StardewModdingAPI.Metadata
internal class CoreAssetPropagator
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>Normalises an asset key to match the cache key.</summary>
private readonly Func<string, string> GetNormalisedPath;
diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs
index 7c840b2f..9ff99440 100644
--- a/src/SMAPI/Metadata/InstructionMetadata.cs
+++ b/src/SMAPI/Metadata/InstructionMetadata.cs
@@ -13,7 +13,7 @@ namespace StardewModdingAPI.Metadata
internal class InstructionMetadata
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The assembly names to which to heuristically detect broken references.</summary>
/// <remarks>The current implementation only works correctly with assemblies that should always be present.</remarks>
diff --git a/src/SMAPI/Patches/LoadForNewGamePatch.cs b/src/SMAPI/Patches/LoadForNewGamePatch.cs
new file mode 100644
index 00000000..9e788e84
--- /dev/null
+++ b/src/SMAPI/Patches/LoadForNewGamePatch.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Reflection;
+using Harmony;
+using StardewModdingAPI.Enums;
+using StardewModdingAPI.Framework.Patching;
+using StardewModdingAPI.Framework.Reflection;
+using StardewValley;
+using StardewValley.Menus;
+
+namespace StardewModdingAPI.Patches
+{
+ /// <summary>A Harmony patch for <see cref="Game1.loadForNewGame"/> which notifies SMAPI for save creation load stages.</summary>
+ /// <remarks>This patch hooks into <see cref="Game1.loadForNewGame"/>, checks if <c>TitleMenu.transitioningCharacterCreationMenu</c> is true (which means the player is creating a new save file), then raises <see cref="LoadStage.CreatedBasicInfo"/> after the location list is cleared twice (the second clear happens right before locations are created), and <see cref="LoadStage.CreatedLocations"/> when the method ends.</remarks>
+ internal class LoadForNewGamePatch : IHarmonyPatch
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>Simplifies access to private code.</summary>
+ private static Reflector Reflection;
+
+ /// <summary>A callback to invoke when the load stage changes.</summary>
+ private static Action<LoadStage> OnStageChanged;
+
+ /// <summary>Whether <see cref="Game1.loadForNewGame"/> was called as part of save creation.</summary>
+ private static bool IsCreating;
+
+ /// <summary>The number of times that <see cref="Game1.locations"/> has been cleared since <see cref="Game1.loadForNewGame"/> started.</summary>
+ private static int TimesLocationsCleared = 0;
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>A unique name for this patch.</summary>
+ public string Name => $"{nameof(LoadForNewGamePatch)}";
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="reflection">Simplifies access to private code.</param>
+ /// <param name="onStageChanged">A callback to invoke when the load stage changes.</param>
+ public LoadForNewGamePatch(Reflector reflection, Action<LoadStage> onStageChanged)
+ {
+ LoadForNewGamePatch.Reflection = reflection;
+ LoadForNewGamePatch.OnStageChanged = onStageChanged;
+ }
+
+ /// <summary>Apply the Harmony patch.</summary>
+ /// <param name="harmony">The Harmony instance.</param>
+ public void Apply(HarmonyInstance harmony)
+ {
+ MethodInfo method = AccessTools.Method(typeof(Game1), nameof(Game1.loadForNewGame));
+ MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(LoadForNewGamePatch.Prefix));
+ MethodInfo postfix = AccessTools.Method(this.GetType(), nameof(LoadForNewGamePatch.Postfix));
+
+ harmony.Patch(method, new HarmonyMethod(prefix), new HarmonyMethod(postfix));
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>The method to call instead of <see cref="Game1.loadForNewGame"/>.</summary>
+ /// <returns>Returns whether to execute the original method.</returns>
+ /// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
+ private static bool Prefix()
+ {
+ LoadForNewGamePatch.IsCreating = Game1.activeClickableMenu is TitleMenu menu && LoadForNewGamePatch.Reflection.GetField<bool>(menu, "transitioningCharacterCreationMenu").GetValue();
+ LoadForNewGamePatch.TimesLocationsCleared = 0;
+ if (LoadForNewGamePatch.IsCreating)
+ {
+ // raise CreatedBasicInfo after locations are cleared twice
+ ObservableCollection<GameLocation> locations = (ObservableCollection<GameLocation>)Game1.locations;
+ locations.CollectionChanged += LoadForNewGamePatch.OnLocationListChanged;
+ }
+
+ return true;
+ }
+
+ /// <summary>The method to call instead after <see cref="Game1.loadForNewGame"/>.</summary>
+ /// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
+ private static void Postfix()
+ {
+ if (LoadForNewGamePatch.IsCreating)
+ {
+ // clean up
+ ObservableCollection<GameLocation> locations = (ObservableCollection<GameLocation>) Game1.locations;
+ locations.CollectionChanged -= LoadForNewGamePatch.OnLocationListChanged;
+
+ // raise stage changed
+ LoadForNewGamePatch.OnStageChanged(LoadStage.CreatedLocations);
+ }
+ }
+
+ /// <summary>Raised when <see cref="Game1.locations"/> changes.</summary>
+ /// <param name="sender">The event sender.</param>
+ /// <param name="e">The event arguments.</param>
+ private static void OnLocationListChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (++LoadForNewGamePatch.TimesLocationsCleared == 2)
+ LoadForNewGamePatch.OnStageChanged(LoadStage.CreatedBasicInfo);
+ }
+ }
+}
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index 2efcfecb..2eec371c 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -15,7 +15,7 @@ namespace StardewModdingAPI
internal class Program
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The absolute path to search for SMAPI's internal DLLs.</summary>
/// <remarks>We can't use <see cref="Constants.ExecutionPath"/> directly, since <see cref="Constants"/> depends on DLLs loaded from this folder.</remarks>
diff --git a/src/SMAPI/SemanticVersion.cs b/src/SMAPI/SemanticVersion.cs
index 4fc6c219..e8e5dfa4 100644
--- a/src/SMAPI/SemanticVersion.cs
+++ b/src/SMAPI/SemanticVersion.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI
public class SemanticVersion : ISemanticVersion
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying semantic version implementation.</summary>
private readonly ISemanticVersion Version;
@@ -33,7 +33,7 @@ namespace StardewModdingAPI
{
get
{
- SCore.DeprecationManager?.Warn($"{nameof(ISemanticVersion)}.{nameof(ISemanticVersion.Build)}", "2.8", DeprecationLevel.Notice);
+ SCore.DeprecationManager?.Warn($"{nameof(ISemanticVersion)}.{nameof(ISemanticVersion.Build)}", "2.8", DeprecationLevel.Info);
return this.Version.PrereleaseTag;
}
}
diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj
index 9b00e777..6692bc02 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>
+ <LangVersion>latest</LangVersion>
<LargeAddressAware Condition="'$(OS)' == 'Windows_NT'">true</LargeAddressAware>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
@@ -76,6 +77,7 @@
<Compile Include="..\..\build\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
+ <Compile Include="Enums\LoadStage.cs" />
<Compile Include="Enums\SkillType.cs" />
<Compile Include="Events\BuildingListChangedEventArgs.cs" />
<Compile Include="Events\ButtonPressedEventArgs.cs" />
@@ -123,6 +125,7 @@
<Compile Include="Events\IWorldEvents.cs" />
<Compile Include="Events\LargeTerrainFeatureListChangedEventArgs.cs" />
<Compile Include="Events\LevelChangedEventArgs.cs" />
+ <Compile Include="Events\LoadStageChangedEventArgs.cs" />
<Compile Include="Events\LocationEvents.cs" />
<Compile Include="Events\LocationListChangedEventArgs.cs" />
<Compile Include="Events\MenuChangedEventArgs.cs" />
@@ -133,6 +136,8 @@
<Compile Include="Events\MultiplayerEvents.cs" />
<Compile Include="Events\NpcListChangedEventArgs.cs" />
<Compile Include="Events\ObjectListChangedEventArgs.cs" />
+ <Compile Include="Events\OneSecondUpdateTickedEventArgs.cs" />
+ <Compile Include="Events\OneSecondUpdateTickingEventArgs.cs" />
<Compile Include="Events\PeerContextReceivedEventArgs.cs" />
<Compile Include="Events\PeerDisconnectedEventArgs.cs" />
<Compile Include="Events\PlayerEvents.cs" />
@@ -326,6 +331,7 @@
<Compile Include="Framework\Monitor.cs" />
<Compile Include="Metadata\InstructionMetadata.cs" />
<Compile Include="Mod.cs" />
+ <Compile Include="Patches\LoadForNewGamePatch.cs" />
<Compile Include="Patches\ObjectErrorPatch.cs" />
<Compile Include="Patches\DialogueErrorPatch.cs" />
<Compile Include="PatchMode.cs" />
diff --git a/src/SMAPI/Translation.cs b/src/SMAPI/Translation.cs
index ce344f81..abcdb336 100644
--- a/src/SMAPI/Translation.cs
+++ b/src/SMAPI/Translation.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI
public class Translation
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The placeholder text when the translation is <c>null</c> or empty, where <c>{0}</c> is the translation key.</summary>
internal const string PlaceholderText = "(no translation:{0})";
diff --git a/src/SMAPI/Utilities/SDate.cs b/src/SMAPI/Utilities/SDate.cs
index e589e9a4..ec54f84a 100644
--- a/src/SMAPI/Utilities/SDate.cs
+++ b/src/SMAPI/Utilities/SDate.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Utilities
public class SDate : IEquatable<SDate>
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The internal season names in order.</summary>
private readonly string[] Seasons = { "spring", "summer", "fall", "winter" };
diff --git a/src/StardewModdingAPI.Toolkit.CoreInterfaces/StardewModdingAPI.Toolkit.CoreInterfaces.csproj b/src/StardewModdingAPI.Toolkit.CoreInterfaces/StardewModdingAPI.Toolkit.CoreInterfaces.csproj
index 525931e5..67adbd67 100644
--- a/src/StardewModdingAPI.Toolkit.CoreInterfaces/StardewModdingAPI.Toolkit.CoreInterfaces.csproj
+++ b/src/StardewModdingAPI.Toolkit.CoreInterfaces/StardewModdingAPI.Toolkit.CoreInterfaces.csproj
@@ -6,6 +6,7 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputPath>..\..\bin\$(Configuration)\SMAPI.Toolkit.CoreInterfaces</OutputPath>
<DocumentationFile>..\..\bin\$(Configuration)\SMAPI.Toolkit.CoreInterfaces\$(TargetFramework)\StardewModdingAPI.Toolkit.CoreInterfaces.xml</DocumentationFile>
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
diff --git a/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs b/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs
index 0ecd9664..7c3df384 100644
--- a/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs
+++ b/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs
@@ -11,7 +11,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
public class WebApiClient
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The base URL for the web API.</summary>
private readonly Uri BaseUrl;
diff --git a/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs b/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs
index 19a4292f..d3a25845 100644
--- a/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs
+++ b/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs
@@ -13,7 +13,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
public class WikiClient : IDisposable
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying HTTP client.</summary>
private readonly IClient Client;
diff --git a/src/StardewModdingAPI.Toolkit/Framework/ModData/ModDatabase.cs b/src/StardewModdingAPI.Toolkit/Framework/ModData/ModDatabase.cs
index 3b98bcf1..a9da884a 100644
--- a/src/StardewModdingAPI.Toolkit/Framework/ModData/ModDatabase.cs
+++ b/src/StardewModdingAPI.Toolkit/Framework/ModData/ModDatabase.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
public class ModDatabase
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The underlying mod data records indexed by default display name.</summary>
private readonly ModDataRecord[] Records;
diff --git a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs
index 61d0d6f2..7624894c 100644
--- a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs
+++ b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs
@@ -11,7 +11,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
public class ModScanner
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The JSON helper with which to read manifests.</summary>
private readonly JsonHelper JsonHelper;
diff --git a/src/StardewModdingAPI.Toolkit/ModToolkit.cs b/src/StardewModdingAPI.Toolkit/ModToolkit.cs
index c55f6c70..1b53e59e 100644
--- a/src/StardewModdingAPI.Toolkit/ModToolkit.cs
+++ b/src/StardewModdingAPI.Toolkit/ModToolkit.cs
@@ -15,7 +15,7 @@ namespace StardewModdingAPI.Toolkit
public class ModToolkit
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The default HTTP user agent for the toolkit.</summary>
private readonly string UserAgent;
diff --git a/src/StardewModdingAPI.Toolkit/SemanticVersion.cs b/src/StardewModdingAPI.Toolkit/SemanticVersion.cs
index 19da0a76..ba9ca6c6 100644
--- a/src/StardewModdingAPI.Toolkit/SemanticVersion.cs
+++ b/src/StardewModdingAPI.Toolkit/SemanticVersion.cs
@@ -14,7 +14,7 @@ namespace StardewModdingAPI.Toolkit
public class SemanticVersion : ISemanticVersion
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>A regex pattern matching a valid prerelease tag.</summary>
internal const string TagPattern = @"(?>[a-z0-9]+[\-\.]?)+";
diff --git a/src/StardewModdingAPI.Toolkit/StardewModdingAPI.Toolkit.csproj b/src/StardewModdingAPI.Toolkit/StardewModdingAPI.Toolkit.csproj
index 3fa28d19..351b36b6 100644
--- a/src/StardewModdingAPI.Toolkit/StardewModdingAPI.Toolkit.csproj
+++ b/src/StardewModdingAPI.Toolkit/StardewModdingAPI.Toolkit.csproj
@@ -5,6 +5,7 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputPath>..\..\bin\$(Configuration)\SMAPI.Toolkit</OutputPath>
<DocumentationFile>..\..\bin\$(Configuration)\SMAPI.Toolkit\$(TargetFramework)\StardewModdingAPI.Toolkit.xml</DocumentationFile>
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
diff --git a/src/StardewModdingAPI.Toolkit/Utilities/PathUtilities.cs b/src/StardewModdingAPI.Toolkit/Utilities/PathUtilities.cs
index 79748c25..8a3c2b03 100644
--- a/src/StardewModdingAPI.Toolkit/Utilities/PathUtilities.cs
+++ b/src/StardewModdingAPI.Toolkit/Utilities/PathUtilities.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI.Toolkit.Utilities
public static class PathUtilities
{
/*********
- ** Properties
+ ** Fields
*********/
/// <summary>The possible directory separator characters in a file path.</summary>
private static readonly char[] PossiblePathSeparators = new[] { '/', '\\', Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }.Distinct().ToArray();