diff --git a/docs/release-notes.md b/docs/release-notes.md
index e08e7af4..cde04226 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -1,4 +1,9 @@
# Release notes
+## Upcoming
+
+* For modders:
+ * Fixed `Constants.SaveFolderName` and `CurrentSavePath` not available during early load stages when using `Specialised.LoadStageChanged` event.
+
## 2.10.1
Released 30 December 2018 for Stardew Valley 1.3.32.
diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs
index a0ba67ab..dde8f2a0 100644
--- a/src/SMAPI/Constants.cs
+++ b/src/SMAPI/Constants.cs
@@ -2,6 +2,7 @@ using System;
using System.IO;
using System.Linq;
using System.Reflection;
+using StardewModdingAPI.Enums;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.ModLoading;
using StardewModdingAPI.Internal;
@@ -12,16 +13,6 @@ namespace StardewModdingAPI
/// Contains SMAPI's constants and assumptions.
public static class Constants
{
- /*********
- ** Fields
- *********/
- /// The directory path containing the current save's data (if a save is loaded).
- private static string RawSavePath => Context.IsSaveLoaded ? Path.Combine(Constants.SavesPath, Constants.GetSaveFolderName()) : null;
-
- /// Whether the directory containing the current save's data exists on disk.
- private static bool SavePathReady => Context.IsSaveLoaded && Directory.Exists(Constants.RawSavePath);
-
-
/*********
** Accessors
*********/
@@ -52,11 +43,33 @@ namespace StardewModdingAPI
/// The directory path where all saves are stored.
public static string SavesPath { get; } = Path.Combine(Constants.DataPath, "Saves");
- /// The directory name containing the current save's data (if a save is loaded and the directory exists).
- public static string SaveFolderName => Context.IsSaveLoaded ? Constants.GetSaveFolderName() : "";
+ /// The name of the current save folder (if save info is available, regardless of whether the save file exists yet).
+ public static string SaveFolderName
+ {
+ get
+ {
+ return Constants.GetSaveFolderName()
+#if SMAPI_3_0_STRICT
+ ;
+#else
+ ?? "";
+#endif
+ }
+ }
- /// The directory path containing the current save's data (if a save is loaded and the directory exists).
- public static string CurrentSavePath => Constants.SavePathReady ? Path.Combine(Constants.SavesPath, Constants.GetSaveFolderName()) : "";
+ /// The absolute path to the current save folder (if save info is available and the save file exists).
+ public static string CurrentSavePath
+ {
+ get
+ {
+ return Constants.GetSaveFolderPathIfExists()
+#if SMAPI_3_0_STRICT
+ ;
+#else
+ ?? "";
+#endif
+ }
+ }
/****
** Internal
@@ -184,13 +197,6 @@ namespace StardewModdingAPI
/*********
** Private methods
*********/
- /// Get the name of a save directory for the current player.
- private static string GetSaveFolderName()
- {
- string prefix = new string(Game1.player.Name.Where(char.IsLetterOrDigit).ToArray());
- return $"{prefix}_{Game1.uniqueIDForThisGame}";
- }
-
/// Get the game's current version string.
private static string GetGameVersion()
{
@@ -200,5 +206,43 @@ namespace StardewModdingAPI
throw new InvalidOperationException($"The {nameof(Game1)}.{nameof(Game1.version)} field could not be found.");
return (string)field.GetValue(null);
}
+
+ /// Get the name of the save folder, if any.
+ internal static string GetSaveFolderName()
+ {
+ // save not available
+ if (Context.LoadStage == LoadStage.None)
+ return null;
+
+ // get basic info
+ string playerName;
+ ulong saveID;
+ if (Context.LoadStage == LoadStage.SaveParsed)
+ {
+ playerName = SaveGame.loaded.player.Name;
+ saveID = SaveGame.loaded.uniqueIDForThisGame;
+ }
+ else
+ {
+ playerName = Game1.player.Name;
+ saveID = Game1.uniqueIDForThisGame;
+ }
+
+ // build folder name
+ return $"{new string(playerName.Where(char.IsLetterOrDigit).ToArray())}_{saveID}";
+ }
+
+ /// Get the path to the current save folder, if any.
+ internal static string GetSaveFolderPathIfExists()
+ {
+ string folderName = Constants.GetSaveFolderName();
+ if (folderName == null)
+ return null;
+
+ string path = Path.Combine(Constants.SavesPath, folderName);
+ return Directory.Exists(path)
+ ? path
+ : null;
+ }
}
}
diff --git a/src/SMAPI/Context.cs b/src/SMAPI/Context.cs
index cd1cf1c2..1cdef7f1 100644
--- a/src/SMAPI/Context.cs
+++ b/src/SMAPI/Context.cs
@@ -1,3 +1,4 @@
+using StardewModdingAPI.Enums;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;
@@ -39,5 +40,8 @@ namespace StardewModdingAPI
/// Whether the game is currently writing to the save file.
internal static bool IsSaving => Game1.activeClickableMenu is SaveGameMenu || Game1.activeClickableMenu is ShippingMenu; // saving is performed by SaveGameMenu, but it's wrapped by ShippingMenu on days when the player shipping something
+
+ /// The current stage in the game's loading process.
+ internal static LoadStage LoadStage { get; set; }
}
}
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index 25ffcabd..6aff6c01 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -69,9 +69,6 @@ namespace StardewModdingAPI.Framework
/// Skipping a few frames ensures the game finishes initialising the world before mods try to change it.
private readonly Countdown AfterLoadTimer = new Countdown(5);
- /// The current stage in the game's loading process.
- private LoadStage LoadStage = LoadStage.None;
-
/// Whether the game is saving and SMAPI has already raised .
private bool IsBetweenSaveEvents;
@@ -215,12 +212,12 @@ namespace StardewModdingAPI.Framework
internal void OnLoadStageChanged(LoadStage newStage)
{
// nothing to do
- if (newStage == this.LoadStage)
+ if (newStage == Context.LoadStage)
return;
// update data
- LoadStage oldStage = this.LoadStage;
- this.LoadStage = newStage;
+ LoadStage oldStage = Context.LoadStage;
+ Context.LoadStage = newStage;
if (newStage == LoadStage.None)
{
this.Monitor.Log("Context: returned to title", LogLevel.Trace);
@@ -511,7 +508,7 @@ namespace StardewModdingAPI.Framework
*********/
if (wasWorldReady && !Context.IsWorldReady)
this.OnLoadStageChanged(LoadStage.None);
- else if (Context.IsWorldReady && this.LoadStage != LoadStage.Ready)
+ else if (Context.IsWorldReady && Context.LoadStage != LoadStage.Ready)
{
// print context
string context = $"Context: loaded saved game '{Constants.SaveFolderName}', starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.";
@@ -884,7 +881,7 @@ namespace StardewModdingAPI.Framework
events.GameLaunched.Raise(new GameLaunchedEventArgs());
// preloaded
- if (Context.IsSaveLoaded && this.LoadStage != LoadStage.Loaded && this.LoadStage != LoadStage.Ready)
+ if (Context.IsSaveLoaded && Context.LoadStage != LoadStage.Loaded && Context.LoadStage != LoadStage.Ready)
this.OnLoadStageChanged(LoadStage.Loaded);
// update tick