diff --git a/docs/technical-docs.md b/docs/technical-docs.md
index 5883ee00..1d69f868 100644
--- a/docs/technical-docs.md
+++ b/docs/technical-docs.md
@@ -105,8 +105,8 @@ SMAPI uses a small number of conditional compilation constants, which you can se
flag | purpose
---- | -------
-`SMAPI_FOR_WINDOWS` | Indicates that SMAPI is being compiled on Windows for players on Windows. Set automatically in `crossplatform.targets`.
-
+`SMAPI_FOR_WINDOWS` | Whether SMAPI is being compiled on Windows for players on Windows. Set automatically in `crossplatform.targets`.
+`SMAPI_3_0_STRICT` | Whether to exclude all deprecated APIs from compilation. This is useful for testing mods for SMAPI 3.0 compatibility.
# SMAPI web services
## Overview
diff --git a/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs b/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs
index 7588043d..30951064 100644
--- a/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs
@@ -29,7 +29,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands
helper.ConsoleCommands.Add(command.Name, command.Description, (name, args) => this.HandleCommand(command, name, args));
// hook events
- GameEvents.UpdateTick += this.GameEvents_UpdateTick;
+ helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked;
}
@@ -39,7 +39,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands
/// The method invoked when the game updates its state.
/// The event sender.
/// The event arguments.
- private void GameEvents_UpdateTick(object sender, EventArgs e)
+ private void OnUpdateTicked(object sender, EventArgs e)
{
if (!Context.IsWorldReady)
return;
diff --git a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs
index 1782308b..2e7719eb 100644
--- a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs
+++ b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs
@@ -47,7 +47,7 @@ namespace StardewModdingAPI.Tests.Utilities
Assert.AreEqual(major, version.MajorVersion, "The major version doesn't match the given value.");
Assert.AreEqual(minor, version.MinorVersion, "The minor version doesn't match the given value.");
Assert.AreEqual(patch, version.PatchVersion, "The patch version doesn't match the given value.");
- Assert.AreEqual(string.IsNullOrWhiteSpace(tag) ? null : tag.Trim(), version.Build, "The tag doesn't match the given value.");
+ Assert.AreEqual(string.IsNullOrWhiteSpace(tag) ? null : tag.Trim(), version.PrereleaseTag, "The tag doesn't match the given value.");
return version.ToString();
}
diff --git a/src/SMAPI/Context.cs b/src/SMAPI/Context.cs
index c7aed81d..cd1cf1c2 100644
--- a/src/SMAPI/Context.cs
+++ b/src/SMAPI/Context.cs
@@ -22,7 +22,7 @@ namespace StardewModdingAPI
/// Whether is true and the player is free to move (e.g. not using a tool).
public static bool CanPlayerMove => Context.IsPlayerFree && Game1.player.CanMove;
- /// Whether the game is currently running the draw loop. This isn't relevant to most mods, since you should use to draw to the screen.
+ /// Whether the game is currently running the draw loop. This isn't relevant to most mods, since you should use events to draw to the screen.
public static bool IsInDrawLoop { get; internal set; }
/// Whether and the player loaded the save in multiplayer mode (regardless of whether any other players are connected).
diff --git a/src/SMAPI/Events/ContentEvents.cs b/src/SMAPI/Events/ContentEvents.cs
index 3bf3881b..99369cae 100644
--- a/src/SMAPI/Events/ContentEvents.cs
+++ b/src/SMAPI/Events/ContentEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@@ -46,3 +47,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/ControlEvents.cs b/src/SMAPI/Events/ControlEvents.cs
index c50d5bea..5626ff81 100644
--- a/src/SMAPI/Events/ControlEvents.cs
+++ b/src/SMAPI/Events/ControlEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework.Input;
using StardewModdingAPI.Framework;
@@ -124,3 +125,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsClickableMenuChanged.cs b/src/SMAPI/Events/EventArgsClickableMenuChanged.cs
index 5e00b86d..a0b903b7 100644
--- a/src/SMAPI/Events/EventArgsClickableMenuChanged.cs
+++ b/src/SMAPI/Events/EventArgsClickableMenuChanged.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewValley.Menus;
@@ -29,3 +30,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsClickableMenuClosed.cs b/src/SMAPI/Events/EventArgsClickableMenuClosed.cs
index 65751da7..77db69ea 100644
--- a/src/SMAPI/Events/EventArgsClickableMenuClosed.cs
+++ b/src/SMAPI/Events/EventArgsClickableMenuClosed.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewValley.Menus;
@@ -24,3 +25,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsControllerButtonPressed.cs b/src/SMAPI/Events/EventArgsControllerButtonPressed.cs
index 3243b80b..949446e1 100644
--- a/src/SMAPI/Events/EventArgsControllerButtonPressed.cs
+++ b/src/SMAPI/Events/EventArgsControllerButtonPressed.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@@ -30,3 +31,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsControllerButtonReleased.cs b/src/SMAPI/Events/EventArgsControllerButtonReleased.cs
index e05a080b..d6d6d840 100644
--- a/src/SMAPI/Events/EventArgsControllerButtonReleased.cs
+++ b/src/SMAPI/Events/EventArgsControllerButtonReleased.cs
@@ -1,4 +1,5 @@
-using System;
+#if !SMAPI_3_0_STRICT
+using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@@ -30,3 +31,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsControllerTriggerPressed.cs b/src/SMAPI/Events/EventArgsControllerTriggerPressed.cs
index a2087733..33be2fa3 100644
--- a/src/SMAPI/Events/EventArgsControllerTriggerPressed.cs
+++ b/src/SMAPI/Events/EventArgsControllerTriggerPressed.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@@ -35,3 +36,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsControllerTriggerReleased.cs b/src/SMAPI/Events/EventArgsControllerTriggerReleased.cs
index d2eecbec..e90ff712 100644
--- a/src/SMAPI/Events/EventArgsControllerTriggerReleased.cs
+++ b/src/SMAPI/Events/EventArgsControllerTriggerReleased.cs
@@ -1,4 +1,5 @@
-using System;
+#if !SMAPI_3_0_STRICT
+using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@@ -35,3 +36,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsInput.cs b/src/SMAPI/Events/EventArgsInput.cs
index 0cafdba5..837de2f8 100644
--- a/src/SMAPI/Events/EventArgsInput.cs
+++ b/src/SMAPI/Events/EventArgsInput.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using System.Collections.Generic;
@@ -60,3 +61,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsIntChanged.cs b/src/SMAPI/Events/EventArgsIntChanged.cs
index a018695c..76ec6d08 100644
--- a/src/SMAPI/Events/EventArgsIntChanged.cs
+++ b/src/SMAPI/Events/EventArgsIntChanged.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
namespace StardewModdingAPI.Events
@@ -28,3 +29,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsInventoryChanged.cs b/src/SMAPI/Events/EventArgsInventoryChanged.cs
index 3a2354b6..488dd23f 100644
--- a/src/SMAPI/Events/EventArgsInventoryChanged.cs
+++ b/src/SMAPI/Events/EventArgsInventoryChanged.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using System.Collections.Generic;
using System.Linq;
@@ -39,3 +40,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsKeyPressed.cs b/src/SMAPI/Events/EventArgsKeyPressed.cs
index d9d81e10..6204d821 100644
--- a/src/SMAPI/Events/EventArgsKeyPressed.cs
+++ b/src/SMAPI/Events/EventArgsKeyPressed.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework.Input;
@@ -24,3 +25,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsKeyboardStateChanged.cs b/src/SMAPI/Events/EventArgsKeyboardStateChanged.cs
index 14e397ce..2c3203b1 100644
--- a/src/SMAPI/Events/EventArgsKeyboardStateChanged.cs
+++ b/src/SMAPI/Events/EventArgsKeyboardStateChanged.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework.Input;
@@ -29,3 +30,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsLevelUp.cs b/src/SMAPI/Events/EventArgsLevelUp.cs
index e9a697e7..06c70088 100644
--- a/src/SMAPI/Events/EventArgsLevelUp.cs
+++ b/src/SMAPI/Events/EventArgsLevelUp.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Enums;
@@ -51,3 +52,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsLocationBuildingsChanged.cs b/src/SMAPI/Events/EventArgsLocationBuildingsChanged.cs
index e8184ebe..25e84722 100644
--- a/src/SMAPI/Events/EventArgsLocationBuildingsChanged.cs
+++ b/src/SMAPI/Events/EventArgsLocationBuildingsChanged.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using System.Collections.Generic;
using System.Linq;
@@ -37,3 +38,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsLocationObjectsChanged.cs b/src/SMAPI/Events/EventArgsLocationObjectsChanged.cs
index 3bb387d5..9ca2e3e2 100644
--- a/src/SMAPI/Events/EventArgsLocationObjectsChanged.cs
+++ b/src/SMAPI/Events/EventArgsLocationObjectsChanged.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using System.Collections.Generic;
using System.Linq;
@@ -38,3 +39,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsLocationsChanged.cs b/src/SMAPI/Events/EventArgsLocationsChanged.cs
index 20984f45..1a59e612 100644
--- a/src/SMAPI/Events/EventArgsLocationsChanged.cs
+++ b/src/SMAPI/Events/EventArgsLocationsChanged.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using System.Collections.Generic;
using System.Linq;
@@ -31,3 +32,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsMineLevelChanged.cs b/src/SMAPI/Events/EventArgsMineLevelChanged.cs
index c82fed35..c63b04e9 100644
--- a/src/SMAPI/Events/EventArgsMineLevelChanged.cs
+++ b/src/SMAPI/Events/EventArgsMineLevelChanged.cs
@@ -1,4 +1,5 @@
-using System;
+#if !SMAPI_3_0_STRICT
+using System;
namespace StardewModdingAPI.Events
{
@@ -28,3 +29,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsMouseStateChanged.cs b/src/SMAPI/Events/EventArgsMouseStateChanged.cs
index 57298164..09f3f759 100644
--- a/src/SMAPI/Events/EventArgsMouseStateChanged.cs
+++ b/src/SMAPI/Events/EventArgsMouseStateChanged.cs
@@ -1,4 +1,5 @@
-using System;
+#if !SMAPI_3_0_STRICT
+using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@@ -40,3 +41,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsPlayerWarped.cs b/src/SMAPI/Events/EventArgsPlayerWarped.cs
index 93026aea..d1aa1588 100644
--- a/src/SMAPI/Events/EventArgsPlayerWarped.cs
+++ b/src/SMAPI/Events/EventArgsPlayerWarped.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewValley;
@@ -30,3 +31,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/EventArgsValueChanged.cs b/src/SMAPI/Events/EventArgsValueChanged.cs
index 1d25af49..7bfac7a2 100644
--- a/src/SMAPI/Events/EventArgsValueChanged.cs
+++ b/src/SMAPI/Events/EventArgsValueChanged.cs
@@ -1,4 +1,5 @@
-using System;
+#if !SMAPI_3_0_STRICT
+using System;
namespace StardewModdingAPI.Events
{
@@ -28,4 +29,5 @@ namespace StardewModdingAPI.Events
this.NewValue = newValue;
}
}
-}
\ No newline at end of file
+}
+#endif
diff --git a/src/SMAPI/Events/GameEvents.cs b/src/SMAPI/Events/GameEvents.cs
index 13f6bfb0..39b77f99 100644
--- a/src/SMAPI/Events/GameEvents.cs
+++ b/src/SMAPI/Events/GameEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@@ -123,3 +124,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/GraphicsEvents.cs b/src/SMAPI/Events/GraphicsEvents.cs
index de79e42e..be29bf11 100644
--- a/src/SMAPI/Events/GraphicsEvents.cs
+++ b/src/SMAPI/Events/GraphicsEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@@ -121,3 +122,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/InputEvents.cs b/src/SMAPI/Events/InputEvents.cs
index 788dde62..255b9c8a 100644
--- a/src/SMAPI/Events/InputEvents.cs
+++ b/src/SMAPI/Events/InputEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@@ -57,3 +58,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/LocationEvents.cs b/src/SMAPI/Events/LocationEvents.cs
index 4aad5387..e0bcd853 100644
--- a/src/SMAPI/Events/LocationEvents.cs
+++ b/src/SMAPI/Events/LocationEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@@ -68,3 +69,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/MenuEvents.cs b/src/SMAPI/Events/MenuEvents.cs
index 502ec340..e36cb498 100644
--- a/src/SMAPI/Events/MenuEvents.cs
+++ b/src/SMAPI/Events/MenuEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@@ -57,3 +58,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/MineEvents.cs b/src/SMAPI/Events/MineEvents.cs
index 617c8013..954c844a 100644
--- a/src/SMAPI/Events/MineEvents.cs
+++ b/src/SMAPI/Events/MineEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@@ -46,3 +47,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/MultiplayerEvents.cs b/src/SMAPI/Events/MultiplayerEvents.cs
index 14c58031..7e8328a4 100644
--- a/src/SMAPI/Events/MultiplayerEvents.cs
+++ b/src/SMAPI/Events/MultiplayerEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@@ -79,3 +80,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/PlayerEvents.cs b/src/SMAPI/Events/PlayerEvents.cs
index 4f5f4a37..1193675f 100644
--- a/src/SMAPI/Events/PlayerEvents.cs
+++ b/src/SMAPI/Events/PlayerEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@@ -69,3 +70,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/SaveEvents.cs b/src/SMAPI/Events/SaveEvents.cs
index 5b83efc8..156d3047 100644
--- a/src/SMAPI/Events/SaveEvents.cs
+++ b/src/SMAPI/Events/SaveEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@@ -101,3 +102,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/SpecialisedEvents.cs b/src/SMAPI/Events/SpecialisedEvents.cs
index 482ac62e..0dd726e9 100644
--- a/src/SMAPI/Events/SpecialisedEvents.cs
+++ b/src/SMAPI/Events/SpecialisedEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@@ -46,3 +47,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Events/TimeEvents.cs b/src/SMAPI/Events/TimeEvents.cs
index cca6a056..61491dc8 100644
--- a/src/SMAPI/Events/TimeEvents.cs
+++ b/src/SMAPI/Events/TimeEvents.cs
@@ -1,3 +1,4 @@
+#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@@ -57,3 +58,4 @@ namespace StardewModdingAPI.Events
}
}
}
+#endif
diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs
index b9d1c453..0ad85adf 100644
--- a/src/SMAPI/Framework/Events/EventManager.cs
+++ b/src/SMAPI/Framework/Events/EventManager.cs
@@ -1,5 +1,7 @@
using System.Diagnostics.CodeAnalysis;
+#if !SMAPI_3_0_STRICT
using Microsoft.Xna.Framework.Input;
+#endif
using StardewModdingAPI.Events;
namespace StardewModdingAPI.Framework.Events
@@ -156,6 +158,7 @@ namespace StardewModdingAPI.Framework.Events
public readonly ManagedEvent UnvalidatedUpdateTicked;
+#if !SMAPI_3_0_STRICT
/*********
** Events (old)
*********/
@@ -342,6 +345,7 @@ namespace StardewModdingAPI.Framework.Events
/// Raised after the in-game clock changes.
public readonly ManagedEvent Legacy_TimeOfDayChanged;
+#endif
/*********
@@ -354,7 +358,9 @@ namespace StardewModdingAPI.Framework.Events
{
// create shortcut initialisers
ManagedEvent ManageEventOf(string typeName, string eventName) => new ManagedEvent($"{typeName}.{eventName}", monitor, modRegistry);
+#if !SMAPI_3_0_STRICT
ManagedEvent ManageEvent(string typeName, string eventName) => new ManagedEvent($"{typeName}.{eventName}", monitor, modRegistry);
+#endif
// init events (new)
this.MenuChanged = ManageEventOf(nameof(IModEvents.Display), nameof(IDisplayEvents.MenuChanged));
@@ -405,6 +411,7 @@ namespace StardewModdingAPI.Framework.Events
this.UnvalidatedUpdateTicking = ManageEventOf(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicking));
this.UnvalidatedUpdateTicked = ManageEventOf(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicked));
+#if !SMAPI_3_0_STRICT
// init events (old)
this.Legacy_LocaleChanged = ManageEventOf>(nameof(ContentEvents), nameof(ContentEvents.AfterLocaleChanged));
@@ -466,6 +473,7 @@ namespace StardewModdingAPI.Framework.Events
this.Legacy_AfterDayStarted = ManageEvent(nameof(TimeEvents), nameof(TimeEvents.AfterDayStarted));
this.Legacy_TimeOfDayChanged = ManageEventOf(nameof(TimeEvents), nameof(TimeEvents.TimeOfDayChanged));
+#endif
}
}
}
diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
index 5e190e55..cd7ac8ea 100644
--- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
@@ -131,6 +131,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
this.Data.WriteJsonFile("config.json", config);
}
+#if !SMAPI_3_0_STRICT
/****
** Generic JSON files
****/
@@ -199,6 +200,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
// create content pack
return this.CreateContentPack(directoryPath, manifest);
}
+#endif
/// Get all content packs loaded for this mod.
public IEnumerable GetContentPacks()
diff --git a/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs b/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs
index f3555c2d..6592760e 100644
--- a/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs
+++ b/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs
@@ -23,7 +23,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// The instruction is compatible, but uses the dynamic keyword which won't work on Linux/Mac.
DetectedDynamic,
- /// The instruction is compatible, but references which may impact stability.
+ /// The instruction is compatible, but references or which may impact stability.
DetectedUnvalidatedUpdateTick,
/// The instruction accesses the filesystem directly.
diff --git a/src/SMAPI/Framework/ModLoading/ModWarning.cs b/src/SMAPI/Framework/ModLoading/ModWarning.cs
index c62199b2..e643cb05 100644
--- a/src/SMAPI/Framework/ModLoading/ModWarning.cs
+++ b/src/SMAPI/Framework/ModLoading/ModWarning.cs
@@ -22,7 +22,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// The mod uses the dynamic keyword which won't work on Linux/Mac.
UsesDynamic = 8,
- /// The mod references which may impact stability.
+ /// The mod references or which may impact stability.
UsesUnvalidatedUpdateTick = 16,
/// The mod has no update keys set.
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 827ed82c..eff7cb3b 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -180,6 +180,7 @@ namespace StardewModdingAPI.Framework
// initialise SMAPI
try
{
+#if !SMAPI_3_0_STRICT
// hook up events
ContentEvents.Init(this.EventManager, this.DeprecationManager);
ControlEvents.Init(this.EventManager, this.DeprecationManager);
@@ -194,6 +195,7 @@ namespace StardewModdingAPI.Framework
SaveEvents.Init(this.EventManager, this.DeprecationManager);
SpecialisedEvents.Init(this.EventManager, this.DeprecationManager);
TimeEvents.Init(this.EventManager, this.DeprecationManager);
+#endif
// init JSON parser
JsonConverter[] converters = {
@@ -216,7 +218,7 @@ namespace StardewModdingAPI.Framework
// override game
SGame.ConstructorHack = new SGameConstructorHack(this.Monitor, this.Reflection, this.Toolkit.JsonHelper);
- this.GameInstance = new SGame(this.Monitor, this.MonitorForGame, this.Reflection, this.EventManager, this.Toolkit.JsonHelper, this.ModRegistry, this.DeprecationManager, this.InitialiseAfterGameStart, this.Dispose);
+ this.GameInstance = new SGame(this.Monitor, this.MonitorForGame, this.Reflection, this.EventManager, this.Toolkit.JsonHelper, this.ModRegistry, this.DeprecationManager, this.OnLocaleChanged, this.InitialiseAfterGameStart, this.Dispose);
StardewValley.Program.gamePtr = this.GameInstance;
// add exit handler
@@ -239,12 +241,13 @@ namespace StardewModdingAPI.Framework
}
}).Start();
- // hook into game events
- ContentEvents.AfterLocaleChanged += (sender, e) => this.OnLocaleChanged();
-
// set window titles
this.GameInstance.Window.Title = $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion}";
Console.Title = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion}";
+#if SMAPI_3_0_STRICT
+ this.GameInstance.Window.Title += " [SMAPI 3.0 strict mode]";
+ Console.Title += " [SMAPI 3.0 strict mode]";
+#endif
}
catch (Exception ex)
{
@@ -348,8 +351,11 @@ namespace StardewModdingAPI.Framework
private void InitialiseAfterGameStart()
{
// add headers
+#if SMAPI_3_0_STRICT
+ this.Monitor.Log($"You're running SMAPI 3.0 strict mode, so most mods won't work correctly. If that wasn't intended, install the normal version of SMAPI from https://smapi.io instead.", LogLevel.Warn);
+#endif
if (this.Settings.DeveloperMode)
- this.Monitor.Log($"You configured SMAPI to run in developer mode. The console may be much more verbose. You can disable developer mode by installing the non-developer version of SMAPI, or by editing {Constants.ApiConfigPath}.", LogLevel.Info);
+ this.Monitor.Log($"You have SMAPI for developers, so the console will be much more verbose. You can disable developer mode by installing the non-developer version of SMAPI, or by editing {Constants.ApiConfigPath}.", LogLevel.Info);
if (!this.Settings.CheckForUpdates)
this.Monitor.Log($"You configured SMAPI to not check for updates. Running an old version of SMAPI is not recommended. You can enable update checks by reinstalling SMAPI or editing {Constants.ApiConfigPath}.", LogLevel.Warn);
if (!this.Monitor.WriteToConsole)
@@ -409,6 +415,11 @@ namespace StardewModdingAPI.Framework
int modsLoaded = this.ModRegistry.GetAll().Count();
this.GameInstance.Window.Title = $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion} with {modsLoaded} mods";
Console.Title = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion} with {modsLoaded} mods";
+#if SMAPI_3_0_STRICT
+ this.GameInstance.Window.Title += " [SMAPI 3.0 strict mode]";
+ Console.Title += " [SMAPI 3.0 strict mode]";
+#endif
+
// start SMAPI console
new Thread(this.RunConsoleLoop).Start();
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index f76245a2..d15c5c3e 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -9,7 +9,6 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
-using Microsoft.Xna.Framework.Input;
using Netcode;
using StardewModdingAPI.Enums;
using StardewModdingAPI.Events;
@@ -70,12 +69,15 @@ namespace StardewModdingAPI.Framework
/// Whether the after-load events were raised for this session.
private bool RaisedAfterLoadEvent;
- /// Whether the game is saving and SMAPI has already raised .
+ /// Whether the game is saving and SMAPI has already raised .
private bool IsBetweenSaveEvents;
- /// Whether the game is creating the save file and SMAPI has already raised .
+ /// Whether the game is creating the save file and SMAPI has already raised .
private bool IsBetweenCreateEvents;
+ /// A callback to invoke after the content language changes.
+ private readonly Action OnLocaleChanged;
+
/// A callback to invoke after the game finishes initialising.
private readonly Action OnGameInitialised;
@@ -138,9 +140,10 @@ namespace StardewModdingAPI.Framework
/// Encapsulates SMAPI's JSON file parsing.
/// Tracks the installed mods.
/// Manages deprecation warnings.
+ /// A callback to invoke after the content language changes.
/// A callback to invoke after the game finishes initialising.
/// A callback to invoke when the game exits.
- internal SGame(IMonitor monitor, IMonitor monitorForGame, Reflector reflection, EventManager eventManager, JsonHelper jsonHelper, ModRegistry modRegistry, DeprecationManager deprecationManager, Action onGameInitialised, Action onGameExiting)
+ internal SGame(IMonitor monitor, IMonitor monitorForGame, Reflector reflection, EventManager eventManager, JsonHelper jsonHelper, ModRegistry modRegistry, DeprecationManager deprecationManager, Action onLocaleChanged, Action onGameInitialised, Action onGameExiting)
{
SGame.ConstructorHack = null;
@@ -158,6 +161,7 @@ namespace StardewModdingAPI.Framework
this.ModRegistry = modRegistry;
this.Reflection = reflection;
this.DeprecationManager = deprecationManager;
+ this.OnLocaleChanged = onLocaleChanged;
this.OnGameInitialised = onGameInitialised;
this.OnGameExiting = onGameExiting;
Game1.input = new SInputState();
@@ -212,7 +216,9 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log("Context: returned to title", LogLevel.Trace);
this.Multiplayer.Peers.Clear();
this.Events.ReturnedToTitle.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_AfterReturnToTitle.Raise();
+#endif
}
/// Constructor a content manager to read XNB files.
@@ -296,7 +302,9 @@ namespace StardewModdingAPI.Framework
this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed));
base.Update(gameTime);
this.Events.UnvalidatedUpdateTicked.Raise(new UnvalidatedUpdateTickedEventArgs(this.TicksElapsed));
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_UnvalidatedUpdateTick.Raise();
+#endif
return;
}
@@ -343,7 +351,9 @@ namespace StardewModdingAPI.Framework
// This should *always* run, even when suppressing mod events, since the game uses
// this too. For example, doing this after mod event suppression would prevent the
// user from doing anything on the overnight shipping screen.
+#if !SMAPI_3_0_STRICT
SInputState previousInputState = this.Input.Clone();
+#endif
SInputState inputState = this.Input;
if (this.IsActive)
inputState.TrueUpdate();
@@ -364,7 +374,9 @@ namespace StardewModdingAPI.Framework
this.IsBetweenCreateEvents = true;
this.Monitor.Log("Context: before save creation.", LogLevel.Trace);
this.Events.SaveCreating.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_BeforeCreateSave.Raise();
+#endif
}
// raise before-save
@@ -373,14 +385,18 @@ namespace StardewModdingAPI.Framework
this.IsBetweenSaveEvents = true;
this.Monitor.Log("Context: before save.", LogLevel.Trace);
this.Events.Saving.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_BeforeSave.Raise();
+#endif
}
// suppress non-save events
this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed));
base.Update(gameTime);
this.Events.UnvalidatedUpdateTicked.Raise(new UnvalidatedUpdateTickedEventArgs(this.TicksElapsed));
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_UnvalidatedUpdateTick.Raise();
+#endif
return;
}
if (this.IsBetweenCreateEvents)
@@ -389,7 +405,9 @@ namespace StardewModdingAPI.Framework
this.IsBetweenCreateEvents = false;
this.Monitor.Log($"Context: after save creation, starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace);
this.Events.SaveCreated.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_AfterCreateSave.Raise();
+#endif
}
if (this.IsBetweenSaveEvents)
{
@@ -398,9 +416,10 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Context: after save, starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace);
this.Events.Saved.RaiseEmpty();
this.Events.DayStarted.RaiseEmpty();
-
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_AfterSave.Raise();
this.Events.Legacy_AfterDayStarted.Raise();
+#endif
}
/*********
@@ -430,7 +449,11 @@ namespace StardewModdingAPI.Framework
var now = this.Watchers.LocaleWatcher.CurrentValue;
this.Monitor.Log($"Context: locale set to {now}.", LogLevel.Trace);
+
+ this.OnLocaleChanged();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_LocaleChanged.Raise(new EventArgsValueChanged(was.ToString(), now.ToString()));
+#endif
this.Watchers.LocaleWatcher.Reset();
}
@@ -457,9 +480,10 @@ namespace StardewModdingAPI.Framework
this.RaisedAfterLoadEvent = true;
this.Events.SaveLoaded.RaiseEmpty();
this.Events.DayStarted.RaiseEmpty();
-
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_AfterLoad.Raise();
this.Events.Legacy_AfterDayStarted.Raise();
+#endif
}
/*********
@@ -478,7 +502,9 @@ namespace StardewModdingAPI.Framework
Point newSize = this.Watchers.WindowSizeWatcher.CurrentValue;
this.Events.WindowResized.Raise(new WindowResizedEventArgs(oldSize, newSize));
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_Resize.Raise();
+#endif
this.Watchers.WindowSizeWatcher.Reset();
}
@@ -527,9 +553,10 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Events: button {button} pressed.", LogLevel.Trace);
this.Events.ButtonPressed.Raise(new ButtonPressedEventArgs(button, cursor, inputState));
- this.Events.Legacy_ButtonPressed.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons));
+#if !SMAPI_3_0_STRICT
// legacy events
+ this.Events.Legacy_ButtonPressed.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons));
if (button.TryGetKeyboard(out Keys key))
{
if (key != Keys.None)
@@ -542,6 +569,7 @@ namespace StardewModdingAPI.Framework
else
this.Events.Legacy_ControllerButtonPressed.Raise(new EventArgsControllerButtonPressed(PlayerIndex.One, controllerButton));
}
+#endif
}
else if (status == InputStatus.Released)
{
@@ -549,9 +577,10 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Events: button {button} released.", LogLevel.Trace);
this.Events.ButtonReleased.Raise(new ButtonReleasedEventArgs(button, cursor, inputState));
- this.Events.Legacy_ButtonReleased.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons));
+#if !SMAPI_3_0_STRICT
// legacy events
+ this.Events.Legacy_ButtonReleased.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons));
if (button.TryGetKeyboard(out Keys key))
{
if (key != Keys.None)
@@ -564,14 +593,17 @@ namespace StardewModdingAPI.Framework
else
this.Events.Legacy_ControllerButtonReleased.Raise(new EventArgsControllerButtonReleased(PlayerIndex.One, controllerButton));
}
+#endif
}
}
+#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));
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)));
+#endif
}
}
@@ -589,10 +621,12 @@ namespace StardewModdingAPI.Framework
// raise menu events
this.Events.MenuChanged.Raise(new MenuChangedEventArgs(was, now));
+#if !SMAPI_3_0_STRICT
if (now != null)
this.Events.Legacy_MenuChanged.Raise(new EventArgsClickableMenuChanged(was, now));
else
this.Events.Legacy_MenuClosed.Raise(new EventArgsClickableMenuClosed(was));
+#endif
}
/*********
@@ -620,7 +654,9 @@ namespace StardewModdingAPI.Framework
}
this.Events.LocationListChanged.Raise(new LocationListChangedEventArgs(added, removed));
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_LocationsChanged.Raise(new EventArgsLocationsChanged(added, removed));
+#endif
}
// raise location contents changed
@@ -637,7 +673,9 @@ namespace StardewModdingAPI.Framework
watcher.BuildingsWatcher.Reset();
this.Events.BuildingListChanged.Raise(new BuildingListChangedEventArgs(location, added, removed));
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_BuildingsChanged.Raise(new EventArgsLocationBuildingsChanged(location, added, removed));
+#endif
}
// debris changed
@@ -682,7 +720,9 @@ namespace StardewModdingAPI.Framework
watcher.ObjectsWatcher.Reset();
this.Events.ObjectListChanged.Raise(new ObjectListChangedEventArgs(location, added, removed));
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_ObjectsChanged.Raise(new EventArgsLocationObjectsChanged(location, added, removed));
+#endif
}
// terrain features changed
@@ -712,7 +752,9 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Events: time changed from {was} to {now}.", LogLevel.Trace);
this.Events.TimeChanged.Raise(new TimeChangedEventArgs(was, now));
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_TimeOfDayChanged.Raise(new EventArgsIntChanged(was, now));
+#endif
}
else
this.Watchers.TimeWatcher.Reset();
@@ -730,7 +772,9 @@ namespace StardewModdingAPI.Framework
GameLocation oldLocation = playerTracker.LocationWatcher.PreviousValue;
this.Events.Warped.Raise(new WarpedEventArgs(playerTracker.Player, oldLocation, newLocation));
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_PlayerWarped.Raise(new EventArgsPlayerWarped(oldLocation, newLocation));
+#endif
}
// raise player leveled up a skill
@@ -740,7 +784,9 @@ namespace StardewModdingAPI.Framework
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));
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_LeveledUp.Raise(new EventArgsLevelUp((EventArgsLevelUp.LevelType)pair.Key, pair.Value.CurrentValue));
+#endif
}
// raise player inventory changed
@@ -750,7 +796,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));
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_InventoryChanged.Raise(new EventArgsInventoryChanged(Game1.player.Items, changedItems));
+#endif
}
// raise mine level changed
@@ -758,7 +806,9 @@ 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));
+#endif
}
}
this.Watchers.CurrentPlayerTracker?.Reset();
@@ -790,6 +840,7 @@ namespace StardewModdingAPI.Framework
/*********
** Update events
*********/
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_UnvalidatedUpdateTick.Raise();
if (this.TicksElapsed == 1)
this.Events.Legacy_FirstUpdateTick.Raise();
@@ -806,6 +857,7 @@ namespace StardewModdingAPI.Framework
this.Events.Legacy_HalfSecondTick.Raise();
if (this.CurrentUpdateTick % 60 == 0)
this.Events.Legacy_OneSecondTick.Raise();
+#endif
this.CurrentUpdateTick += 1;
if (this.CurrentUpdateTick >= 60)
this.CurrentUpdateTick = 0;
@@ -895,10 +947,14 @@ namespace StardewModdingAPI.Framework
try
{
this.Events.RenderingActiveMenu.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPreRenderGuiEvent.Raise();
+#endif
activeClickableMenu.draw(Game1.spriteBatch);
this.Events.RenderedActiveMenu.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderGuiEvent.Raise();
+#endif
}
catch (Exception ex)
{
@@ -906,7 +962,9 @@ namespace StardewModdingAPI.Framework
activeClickableMenu.exitThisMenu();
}
this.Events.Rendered.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderEvent.Raise();
+#endif
Game1.spriteBatch.End();
}
@@ -930,10 +988,14 @@ namespace StardewModdingAPI.Framework
{
Game1.activeClickableMenu.drawBackground(Game1.spriteBatch);
this.Events.RenderingActiveMenu.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPreRenderGuiEvent.Raise();
+#endif
Game1.activeClickableMenu.draw(Game1.spriteBatch);
this.Events.RenderedActiveMenu.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderGuiEvent.Raise();
+#endif
}
catch (Exception ex)
{
@@ -941,7 +1003,9 @@ namespace StardewModdingAPI.Framework
Game1.activeClickableMenu.exitThisMenu();
}
this.Events.Rendered.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderEvent.Raise();
+#endif
Game1.spriteBatch.End();
this.drawOverlays(Game1.spriteBatch);
if ((double)Game1.options.zoomLevel != 1.0)
@@ -966,7 +1030,9 @@ namespace StardewModdingAPI.Framework
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();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderEvent.Raise();
+#endif
Game1.spriteBatch.End();
}
else if (Game1.currentMinigame != null)
@@ -979,7 +1045,9 @@ namespace StardewModdingAPI.Framework
Game1.spriteBatch.End();
}
this.drawOverlays(Game1.spriteBatch);
+#if !SMAPI_3_0_STRICT
this.RaisePostRender(needsNewBatch: true);
+#endif
if ((double)Game1.options.zoomLevel == 1.0)
return;
this.GraphicsDevice.SetRenderTarget((RenderTarget2D)null);
@@ -997,10 +1065,14 @@ namespace StardewModdingAPI.Framework
try
{
this.Events.RenderingActiveMenu.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPreRenderGuiEvent.Raise();
+#endif
Game1.activeClickableMenu.draw(Game1.spriteBatch);
this.Events.RenderedActiveMenu.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderGuiEvent.Raise();
+#endif
}
catch (Exception ex)
{
@@ -1090,7 +1162,9 @@ namespace StardewModdingAPI.Framework
if (++batchOpens == 1)
this.Events.Rendering.RaiseEmpty();
this.Events.RenderingWorld.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPreRenderEvent.Raise();
+#endif
if (Game1.background != null)
Game1.background.draw(Game1.spriteBatch);
Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch);
@@ -1403,10 +1477,14 @@ namespace StardewModdingAPI.Framework
if ((Game1.displayHUD || Game1.eventUp) && (Game1.currentBillboard == 0 && Game1.gameMode == (byte)3) && (!Game1.freezeControls && !Game1.panMode && !Game1.HostPaused))
{
this.Events.RenderingHud.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPreRenderHudEvent.Raise();
+#endif
this.drawHUD();
this.Events.RenderedHud.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderHudEvent.Raise();
+#endif
}
else if (Game1.activeClickableMenu == null && Game1.farmEvent == null)
Game1.spriteBatch.Draw(Game1.mouseCursors, new Vector2((float)Game1.getOldMouseX(), (float)Game1.getOldMouseY()), new Microsoft.Xna.Framework.Rectangle?(Game1.getSourceRectForStandardTileSheet(Game1.mouseCursors, 0, 16, 16)), Color.White, 0.0f, Vector2.Zero, (float)(4.0 + (double)Game1.dialogueButtonScale / 150.0), SpriteEffects.None, 1f);
@@ -1515,10 +1593,14 @@ namespace StardewModdingAPI.Framework
try
{
this.Events.RenderingActiveMenu.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPreRenderGuiEvent.Raise();
+#endif
Game1.activeClickableMenu.draw(Game1.spriteBatch);
this.Events.RenderedActiveMenu.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderGuiEvent.Raise();
+#endif
}
catch (Exception ex)
{
@@ -1535,7 +1617,9 @@ namespace StardewModdingAPI.Framework
}
this.Events.Rendered.RaiseEmpty();
+#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderEvent.Raise();
+#endif
Game1.spriteBatch.End();
this.drawOverlays(Game1.spriteBatch);
this.renderScreenBuffer();
@@ -1555,6 +1639,7 @@ namespace StardewModdingAPI.Framework
this.RaisedAfterLoadEvent = false;
}
+#if !SMAPI_3_0_STRICT
/// Raise the if there are any listeners.
/// Whether to create a new sprite batch.
private void RaisePostRender(bool needsNewBatch = false)
@@ -1568,5 +1653,6 @@ namespace StardewModdingAPI.Framework
Game1.spriteBatch.End();
}
}
+#endif
}
}
diff --git a/src/SMAPI/Framework/SMultiplayer.cs b/src/SMAPI/Framework/SMultiplayer.cs
index 629fce1d..12cd2d46 100644
--- a/src/SMAPI/Framework/SMultiplayer.cs
+++ b/src/SMAPI/Framework/SMultiplayer.cs
@@ -82,6 +82,7 @@ namespace StardewModdingAPI.Framework
this.OnModMessageReceived = onModMessageReceived;
}
+#if !SMAPI_3_0_STRICT
/// Handle sync messages from other players and perform other initial sync logic.
public override void UpdateEarly()
{
@@ -97,6 +98,7 @@ namespace StardewModdingAPI.Framework
base.UpdateLate(forceSync);
this.EventManager.Legacy_AfterMainBroadcast.Raise();
}
+#endif
/// Initialise a client before the game connects to a remote server.
/// The client to initialise.
diff --git a/src/SMAPI/IModHelper.cs b/src/SMAPI/IModHelper.cs
index 7e82e0d0..678339dc 100644
--- a/src/SMAPI/IModHelper.cs
+++ b/src/SMAPI/IModHelper.cs
@@ -56,6 +56,7 @@ namespace StardewModdingAPI
/// The config settings to save.
void WriteConfig(TConfig config) where TConfig : class, new();
+#if !SMAPI_3_0_STRICT
/****
** Generic JSON files
****/
@@ -88,5 +89,6 @@ namespace StardewModdingAPI
/// Get all content packs loaded for this mod.
IEnumerable GetContentPacks();
+#endif
}
}
diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs
index ff8d54e3..c4ddf807 100644
--- a/src/SMAPI/Metadata/InstructionMetadata.cs
+++ b/src/SMAPI/Metadata/InstructionMetadata.cs
@@ -52,7 +52,10 @@ namespace StardewModdingAPI.Metadata
new FieldFinder(typeof(SaveGame).FullName, nameof(SaveGame.serializer), InstructionHandleResult.DetectedSaveSerialiser),
new FieldFinder(typeof(SaveGame).FullName, nameof(SaveGame.farmerSerializer), InstructionHandleResult.DetectedSaveSerialiser),
new FieldFinder(typeof(SaveGame).FullName, nameof(SaveGame.locationSerializer), InstructionHandleResult.DetectedSaveSerialiser),
+ new TypeFinder(typeof(ISpecialisedEvents).FullName, InstructionHandleResult.DetectedUnvalidatedUpdateTick),
+#if !SMAPI_3_0_STRICT
new EventFinder(typeof(SpecialisedEvents).FullName, nameof(SpecialisedEvents.UnvalidatedUpdateTick), InstructionHandleResult.DetectedUnvalidatedUpdateTick),
+#endif
/****
** detect paranoid issues
diff --git a/src/SMAPI/SemanticVersion.cs b/src/SMAPI/SemanticVersion.cs
index 401f62c2..f75105df 100644
--- a/src/SMAPI/SemanticVersion.cs
+++ b/src/SMAPI/SemanticVersion.cs
@@ -29,6 +29,7 @@ namespace StardewModdingAPI
/// The patch version for backwards-compatible bug fixes.
public int PatchVersion => this.Version.PatchVersion;
+#if !SMAPI_3_0_STRICT
/// An optional build tag.
[Obsolete("Use " + nameof(ISemanticVersion.PrereleaseTag) + " instead")]
public string Build
@@ -39,6 +40,7 @@ namespace StardewModdingAPI
return this.Version.PrereleaseTag;
}
}
+#endif
/// An optional prerelease tag.
public string PrereleaseTag => this.Version.PrereleaseTag;
diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj
index 5a098b8a..3696b54d 100644
--- a/src/SMAPI/StardewModdingAPI.csproj
+++ b/src/SMAPI/StardewModdingAPI.csproj
@@ -32,7 +32,7 @@
x86
false
- DEBUG;TRACE
+ DEBUG;TRACE;SMAPI_3_0_STRICT
true
false
$(SolutionDir)\..\bin\Debug\SMAPI
diff --git a/src/StardewModdingAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs b/src/StardewModdingAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs
index 6631b01d..0a6e5758 100644
--- a/src/StardewModdingAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs
+++ b/src/StardewModdingAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs
@@ -17,9 +17,11 @@ namespace StardewModdingAPI
/// The patch version for backwards-compatible bug fixes.
int PatchVersion { get; }
+#if !SMAPI_3_0_STRICT
/// An optional build tag.
[Obsolete("Use " + nameof(ISemanticVersion.PrereleaseTag) + " instead")]
string Build { get; }
+#endif
/// An optional prerelease tag.
string PrereleaseTag { get; }
diff --git a/src/StardewModdingAPI.Toolkit.CoreInterfaces/StardewModdingAPI.Toolkit.CoreInterfaces.csproj b/src/StardewModdingAPI.Toolkit.CoreInterfaces/StardewModdingAPI.Toolkit.CoreInterfaces.csproj
index 525931e5..539cb5d8 100644
--- a/src/StardewModdingAPI.Toolkit.CoreInterfaces/StardewModdingAPI.Toolkit.CoreInterfaces.csproj
+++ b/src/StardewModdingAPI.Toolkit.CoreInterfaces/StardewModdingAPI.Toolkit.CoreInterfaces.csproj
@@ -8,6 +8,10 @@
..\..\bin\$(Configuration)\SMAPI.Toolkit.CoreInterfaces\$(TargetFramework)\StardewModdingAPI.Toolkit.CoreInterfaces.xml
+
+ $(DefineConstants);SMAPI_3_0_STRICT
+
+
diff --git a/src/StardewModdingAPI.Toolkit/SemanticVersion.cs b/src/StardewModdingAPI.Toolkit/SemanticVersion.cs
index a7990d13..2d0bc033 100644
--- a/src/StardewModdingAPI.Toolkit/SemanticVersion.cs
+++ b/src/StardewModdingAPI.Toolkit/SemanticVersion.cs
@@ -39,9 +39,11 @@ namespace StardewModdingAPI.Toolkit
/// The patch version for backwards-compatible bug fixes.
public int PatchVersion { get; }
+#if !SMAPI_3_0_STRICT
/// An optional prerelease tag.
[Obsolete("Use " + nameof(ISemanticVersion.PrereleaseTag) + " instead")]
public string Build => this.PrereleaseTag;
+#endif
/// An optional prerelease tag.
public string PrereleaseTag { get; }
@@ -114,7 +116,7 @@ namespace StardewModdingAPI.Toolkit
{
if (other == null)
throw new ArgumentNullException(nameof(other));
- return this.CompareTo(other.MajorVersion, other.MinorVersion, other.PatchVersion, other.Build);
+ return this.CompareTo(other.MajorVersion, other.MinorVersion, other.PatchVersion, other.PrereleaseTag);
}
/// Indicates whether the current object is equal to another object of the same type.
@@ -128,7 +130,7 @@ namespace StardewModdingAPI.Toolkit
/// Whether this is a pre-release version.
public bool IsPrerelease()
{
- return !string.IsNullOrWhiteSpace(this.Build);
+ return !string.IsNullOrWhiteSpace(this.PrereleaseTag);
}
/// Get whether this version is older than the specified version.
@@ -187,7 +189,7 @@ namespace StardewModdingAPI.Toolkit
: $"{this.MajorVersion}.{this.MinorVersion}";
// tag
- string tag = this.Build;
+ string tag = this.PrereleaseTag;
if (tag != null)
result += $"-{tag}";
return result;
@@ -241,11 +243,11 @@ namespace StardewModdingAPI.Toolkit
return this.MinorVersion.CompareTo(otherMinor);
if (this.PatchVersion != otherPatch)
return this.PatchVersion.CompareTo(otherPatch);
- if (this.Build == otherTag)
+ if (this.PrereleaseTag == otherTag)
return same;
// stable supercedes pre-release
- bool curIsStable = string.IsNullOrWhiteSpace(this.Build);
+ bool curIsStable = string.IsNullOrWhiteSpace(this.PrereleaseTag);
bool otherIsStable = string.IsNullOrWhiteSpace(otherTag);
if (curIsStable)
return curNewer;
@@ -253,7 +255,7 @@ namespace StardewModdingAPI.Toolkit
return curOlder;
// compare two pre-release tag values
- string[] curParts = this.Build.Split('.', '-');
+ string[] curParts = this.PrereleaseTag.Split('.', '-');
string[] otherParts = otherTag.Split('.', '-');
for (int i = 0; i < curParts.Length; i++)
{
@@ -292,11 +294,11 @@ namespace StardewModdingAPI.Toolkit
throw new FormatException($"{this} isn't a valid semantic version. The major, minor, and patch numbers can't be negative.");
if (this.MajorVersion == 0 && this.MinorVersion == 0 && this.PatchVersion == 0)
throw new FormatException($"{this} isn't a valid semantic version. At least one of the major, minor, and patch numbers must be more than zero.");
- if (this.Build != null)
+ if (this.PrereleaseTag != null)
{
- if (this.Build.Trim() == "")
+ if (this.PrereleaseTag.Trim() == "")
throw new FormatException($"{this} isn't a valid semantic version. The tag cannot be a blank string (but may be omitted).");
- if (!Regex.IsMatch(this.Build, $"^{SemanticVersion.TagPattern}$", RegexOptions.IgnoreCase))
+ if (!Regex.IsMatch(this.PrereleaseTag, $"^{SemanticVersion.TagPattern}$", RegexOptions.IgnoreCase))
throw new FormatException($"{this} isn't a valid semantic version. The tag is invalid.");
}
}
diff --git a/src/StardewModdingAPI.Toolkit/StardewModdingAPI.Toolkit.csproj b/src/StardewModdingAPI.Toolkit/StardewModdingAPI.Toolkit.csproj
index 3fa28d19..29667b1e 100644
--- a/src/StardewModdingAPI.Toolkit/StardewModdingAPI.Toolkit.csproj
+++ b/src/StardewModdingAPI.Toolkit/StardewModdingAPI.Toolkit.csproj
@@ -7,6 +7,10 @@
..\..\bin\$(Configuration)\SMAPI.Toolkit\$(TargetFramework)\StardewModdingAPI.Toolkit.xml
+
+ $(DefineConstants);SMAPI_3_0_STRICT
+
+