From 9240bdbf9b6b54d820cb01953ceea31f5e06598e Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 7 Feb 2019 22:28:55 -0500 Subject: [PATCH] fix save folder constants not available during early load stages --- docs/release-notes.md | 5 +++ src/SMAPI/Constants.cs | 86 +++++++++++++++++++++++++++--------- src/SMAPI/Context.cs | 4 ++ src/SMAPI/Framework/SGame.cs | 13 +++--- 4 files changed, 79 insertions(+), 29 deletions(-) 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