From b3ff0045722acec9b8c12f5f13dec4f21008d6b8 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 18 Jan 2017 21:32:38 -0500 Subject: fix PlayerEvents.LoadedGame and SaveEvents.AfterLoad being fired before the world finishes loading (#216) --- src/StardewModdingAPI/Inheritance/SGame.cs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/StardewModdingAPI/Inheritance/SGame.cs b/src/StardewModdingAPI/Inheritance/SGame.cs index 8e87bac6..28c2aa42 100644 --- a/src/StardewModdingAPI/Inheritance/SGame.cs +++ b/src/StardewModdingAPI/Inheritance/SGame.cs @@ -24,8 +24,9 @@ namespace StardewModdingAPI.Inheritance /********* ** Properties *********/ - /// Whether to raise on the next tick. - private bool FireLoadedGameEvent; + /// The number of ticks until SMAPI should notify mods when is set. + /// Skipping a few frames ensures the game finishes initialising the world before mods try to change it. + private int AfterLoadTimer = 5; /// The debug messages to add to the next debug output. internal static Queue DebugMessageQueue { get; private set; } @@ -1028,17 +1029,15 @@ namespace StardewModdingAPI.Inheritance this.PreviousYearOfGame = Game1.year; } - // raise player loaded save (in the following tick to let the game finish updating first) - if (this.FireLoadedGameEvent) + // raise save loaded + if (Game1.hasLoadedGame && this.AfterLoadTimer >= 0) { - SaveEvents.InvokeAfterLoad(this.Monitor); - PlayerEvents.InvokeLoadedGame(this.Monitor, new EventArgsLoadedGameChanged(Game1.hasLoadedGame)); - this.FireLoadedGameEvent = false; - } - if (Game1.hasLoadedGame != this.PreviouslyLoadedGame) - { - this.FireLoadedGameEvent = true; - this.PreviouslyLoadedGame = Game1.hasLoadedGame; + if (this.AfterLoadTimer == 0) + { + SaveEvents.InvokeAfterLoad(this.Monitor); + PlayerEvents.InvokeLoadedGame(this.Monitor, new EventArgsLoadedGameChanged(Game1.hasLoadedGame)); + } + this.AfterLoadTimer--; } // raise mine level changed -- cgit From 602227eae8da421db759e45595812d9978ca8238 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 18 Jan 2017 22:14:50 -0500 Subject: fix some world events being fired during game startup (#217) --- release-notes.md | 1 + src/StardewModdingAPI/Inheritance/SGame.cs | 202 +++++++++++++++-------------- 2 files changed, 106 insertions(+), 97 deletions(-) (limited to 'src') diff --git a/release-notes.md b/release-notes.md index 4e91ee2e..c870bbb5 100644 --- a/release-notes.md +++ b/release-notes.md @@ -5,6 +5,7 @@ See [log](https://github.com/Pathoschild/SMAPI/compare/1.6...1.7). For mod developers: * Fixed `PlayerEvents.LoadedGame` and `SaveEvents.AfterLoad` being fired before the world finishes initialising. +* Fixed some `LocationEvents`, `PlayerEvents`, and `TimeEvents` being fired during game startup. ## 1.6 See [log](https://github.com/Pathoschild/SMAPI/compare/1.5...1.6). diff --git a/src/StardewModdingAPI/Inheritance/SGame.cs b/src/StardewModdingAPI/Inheritance/SGame.cs index 28c2aa42..f23f3212 100644 --- a/src/StardewModdingAPI/Inheritance/SGame.cs +++ b/src/StardewModdingAPI/Inheritance/SGame.cs @@ -28,6 +28,9 @@ namespace StardewModdingAPI.Inheritance /// Skipping a few frames ensures the game finishes initialising the world before mods try to change it. private int AfterLoadTimer = 5; + /// Whether the player has loaded a save and the world has finished initialising. + private bool IsWorldReady => this.AfterLoadTimer < 0; + /// The debug messages to add to the next debug output. internal static Queue DebugMessageQueue { get; private set; } @@ -861,57 +864,71 @@ namespace StardewModdingAPI.Inheritance /// Detect changes since the last update ticket and trigger mod events. private void UpdateEventCalls() { - // get latest state - this.KStateNow = Keyboard.GetState(); - this.MStateNow = Mouse.GetState(); - this.MPositionNow = new Point(Game1.getMouseX(), Game1.getMouseY()); - - // raise key pressed - foreach (var key in this.FramePressedKeys) - ControlEvents.InvokeKeyPressed(this.Monitor, key); - - // raise key released - foreach (var key in this.FrameReleasedKeys) - ControlEvents.InvokeKeyReleased(this.Monitor, key); - - // raise controller button pressed - for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) + // save loaded event + if (Game1.hasLoadedGame && this.AfterLoadTimer >= 0) { - var buttons = this.GetFramePressedButtons(i); - foreach (var button in buttons) + if (this.AfterLoadTimer == 0) { - if (button == Buttons.LeftTrigger || button == Buttons.RightTrigger) - ControlEvents.InvokeTriggerPressed(this.Monitor, i, button, button == Buttons.LeftTrigger ? GamePad.GetState(i).Triggers.Left : GamePad.GetState(i).Triggers.Right); - else - ControlEvents.InvokeButtonPressed(this.Monitor, i, button); + SaveEvents.InvokeAfterLoad(this.Monitor); + PlayerEvents.InvokeLoadedGame(this.Monitor, new EventArgsLoadedGameChanged(Game1.hasLoadedGame)); } + this.AfterLoadTimer--; } - // raise controller button released - for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) + // input events { - foreach (var button in this.GetFrameReleasedButtons(i)) + // get latest state + this.KStateNow = Keyboard.GetState(); + this.MStateNow = Mouse.GetState(); + this.MPositionNow = new Point(Game1.getMouseX(), Game1.getMouseY()); + + // raise key pressed + foreach (var key in this.FramePressedKeys) + ControlEvents.InvokeKeyPressed(this.Monitor, key); + + // raise key released + foreach (var key in this.FrameReleasedKeys) + ControlEvents.InvokeKeyReleased(this.Monitor, key); + + // raise controller button pressed + for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) { - if (button == Buttons.LeftTrigger || button == Buttons.RightTrigger) - ControlEvents.InvokeTriggerReleased(this.Monitor, i, button, button == Buttons.LeftTrigger ? GamePad.GetState(i).Triggers.Left : GamePad.GetState(i).Triggers.Right); - else - ControlEvents.InvokeButtonReleased(this.Monitor, i, button); + var buttons = this.GetFramePressedButtons(i); + foreach (var button in buttons) + { + if (button == Buttons.LeftTrigger || button == Buttons.RightTrigger) + ControlEvents.InvokeTriggerPressed(this.Monitor, i, button, button == Buttons.LeftTrigger ? GamePad.GetState(i).Triggers.Left : GamePad.GetState(i).Triggers.Right); + else + ControlEvents.InvokeButtonPressed(this.Monitor, i, button); + } } - } - // raise keyboard state changed - if (this.KStateNow != this.KStatePrior) - ControlEvents.InvokeKeyboardChanged(this.Monitor, this.KStatePrior, this.KStateNow); + // raise controller button released + for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) + { + foreach (var button in this.GetFrameReleasedButtons(i)) + { + if (button == Buttons.LeftTrigger || button == Buttons.RightTrigger) + ControlEvents.InvokeTriggerReleased(this.Monitor, i, button, button == Buttons.LeftTrigger ? GamePad.GetState(i).Triggers.Left : GamePad.GetState(i).Triggers.Right); + else + ControlEvents.InvokeButtonReleased(this.Monitor, i, button); + } + } - // raise mouse state changed - if (this.MStateNow != this.MStatePrior) - { - ControlEvents.InvokeMouseChanged(this.Monitor, this.MStatePrior, this.MStateNow, this.MPositionPrior, this.MPositionNow); - this.MStatePrior = this.MStateNow; - this.MPositionPrior = this.MPositionPrior; + // raise keyboard state changed + if (this.KStateNow != this.KStatePrior) + ControlEvents.InvokeKeyboardChanged(this.Monitor, this.KStatePrior, this.KStateNow); + + // raise mouse state changed + if (this.MStateNow != this.MStatePrior) + { + ControlEvents.InvokeMouseChanged(this.Monitor, this.MStatePrior, this.MStateNow, this.MPositionPrior, this.MPositionNow); + this.MStatePrior = this.MStateNow; + this.MPositionPrior = this.MPositionPrior; + } } - // raise menu changed + // menu events if (Game1.activeClickableMenu != this.PreviousActiveMenu) { IClickableMenu previousMenu = this.PreviousActiveMenu; @@ -934,23 +951,23 @@ namespace StardewModdingAPI.Inheritance this.PreviousActiveMenu = newMenu; } - // raise location list changed - if (this.GetHash(Game1.locations) != this.PreviousGameLocations) + // world & player events + if (this.IsWorldReady) { - LocationEvents.InvokeLocationsChanged(this.Monitor, Game1.locations); - this.PreviousGameLocations = this.GetHash(Game1.locations); - } + // raise location list changed + if (this.GetHash(Game1.locations) != this.PreviousGameLocations) + { + LocationEvents.InvokeLocationsChanged(this.Monitor, Game1.locations); + this.PreviousGameLocations = this.GetHash(Game1.locations); + } - // raise current location changed - if (Game1.currentLocation != this.PreviousGameLocation) - { - LocationEvents.InvokeCurrentLocationChanged(this.Monitor, this.PreviousGameLocation, Game1.currentLocation); - this.PreviousGameLocation = Game1.currentLocation; - } + // raise current location changed + if (Game1.currentLocation != this.PreviousGameLocation) + { + LocationEvents.InvokeCurrentLocationChanged(this.Monitor, this.PreviousGameLocation, Game1.currentLocation); + this.PreviousGameLocation = Game1.currentLocation; + } - // player events - if (Game1.player != null) - { // raise player changed if (Game1.player != this.PreviousFarmer) { @@ -997,57 +1014,48 @@ namespace StardewModdingAPI.Inheritance PlayerEvents.InvokeInventoryChanged(this.Monitor, Game1.player.items, changedItems); this.PreviousItems = Game1.player.items.Where(n => n != null).ToDictionary(n => n, n => n.Stack); } - } - - // raise current location's object list changed - int? objectHash = Game1.currentLocation?.objects != null ? this.GetHash(Game1.currentLocation.objects) : (int?)null; - if (objectHash != null && this.PreviousLocationObjects != objectHash) - { - LocationEvents.InvokeOnNewLocationObject(this.Monitor, Game1.currentLocation.objects); - this.PreviousLocationObjects = objectHash.Value; - } - // raise time changed - if (Game1.timeOfDay != this.PreviousTimeOfDay) - { - TimeEvents.InvokeTimeOfDayChanged(this.Monitor, this.PreviousTimeOfDay, Game1.timeOfDay); - this.PreviousTimeOfDay = Game1.timeOfDay; - } - if (Game1.dayOfMonth != this.PreviousDayOfMonth) - { - TimeEvents.InvokeDayOfMonthChanged(this.Monitor, this.PreviousDayOfMonth, Game1.dayOfMonth); - this.PreviousDayOfMonth = Game1.dayOfMonth; - } - if (Game1.currentSeason != this.PreviousSeasonOfYear) - { - TimeEvents.InvokeSeasonOfYearChanged(this.Monitor, this.PreviousSeasonOfYear, Game1.currentSeason); - this.PreviousSeasonOfYear = Game1.currentSeason; - } - if (Game1.year != this.PreviousYearOfGame) - { - TimeEvents.InvokeYearOfGameChanged(this.Monitor, this.PreviousYearOfGame, Game1.year); - this.PreviousYearOfGame = Game1.year; - } + // raise current location's object list changed + { + int? objectHash = Game1.currentLocation?.objects != null ? this.GetHash(Game1.currentLocation.objects) : (int?)null; + if (objectHash != null && this.PreviousLocationObjects != objectHash) + { + LocationEvents.InvokeOnNewLocationObject(this.Monitor, Game1.currentLocation.objects); + this.PreviousLocationObjects = objectHash.Value; + } + } - // raise save loaded - if (Game1.hasLoadedGame && this.AfterLoadTimer >= 0) - { - if (this.AfterLoadTimer == 0) + // raise time changed + if (Game1.timeOfDay != this.PreviousTimeOfDay) { - SaveEvents.InvokeAfterLoad(this.Monitor); - PlayerEvents.InvokeLoadedGame(this.Monitor, new EventArgsLoadedGameChanged(Game1.hasLoadedGame)); + TimeEvents.InvokeTimeOfDayChanged(this.Monitor, this.PreviousTimeOfDay, Game1.timeOfDay); + this.PreviousTimeOfDay = Game1.timeOfDay; + } + if (Game1.dayOfMonth != this.PreviousDayOfMonth) + { + TimeEvents.InvokeDayOfMonthChanged(this.Monitor, this.PreviousDayOfMonth, Game1.dayOfMonth); + this.PreviousDayOfMonth = Game1.dayOfMonth; + } + if (Game1.currentSeason != this.PreviousSeasonOfYear) + { + TimeEvents.InvokeSeasonOfYearChanged(this.Monitor, this.PreviousSeasonOfYear, Game1.currentSeason); + this.PreviousSeasonOfYear = Game1.currentSeason; + } + if (Game1.year != this.PreviousYearOfGame) + { + TimeEvents.InvokeYearOfGameChanged(this.Monitor, this.PreviousYearOfGame, Game1.year); + this.PreviousYearOfGame = Game1.year; } - this.AfterLoadTimer--; - } - // raise mine level changed - if (Game1.mine != null && Game1.mine.mineLevel != this.PreviousMineLevel) - { - MineEvents.InvokeMineLevelChanged(this.Monitor, this.PreviousMineLevel, Game1.mine.mineLevel); - this.PreviousMineLevel = Game1.mine.mineLevel; + // raise mine level changed + if (Game1.mine != null && Game1.mine.mineLevel != this.PreviousMineLevel) + { + MineEvents.InvokeMineLevelChanged(this.Monitor, this.PreviousMineLevel, Game1.mine.mineLevel); + this.PreviousMineLevel = Game1.mine.mineLevel; + } } - // raise game transitioning to new day + // raise game day transition event (obsolete) if (Game1.newDay != this.PreviousIsNewDay) { TimeEvents.InvokeOnNewDay(this.Monitor, this.PreviousDayOfMonth, Game1.dayOfMonth, Game1.newDay); -- cgit From f4846173a963a74017cbc5ef5aa040553e328e36 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 19 Jan 2017 00:54:59 -0500 Subject: fix before/after save events not triggering on days when the player shipped an item (#218) --- src/StardewModdingAPI/Inheritance/SGame.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/StardewModdingAPI/Inheritance/SGame.cs b/src/StardewModdingAPI/Inheritance/SGame.cs index f23f3212..69c20244 100644 --- a/src/StardewModdingAPI/Inheritance/SGame.cs +++ b/src/StardewModdingAPI/Inheritance/SGame.cs @@ -935,9 +935,10 @@ namespace StardewModdingAPI.Inheritance IClickableMenu newMenu = Game1.activeClickableMenu; // raise save events - if (newMenu is SaveGameMenu) + // (saving is performed by SaveGameMenu; on days when the player shipping something, ShippingMenu wraps SaveGameMenu) + if (newMenu is SaveGameMenu || newMenu is ShippingMenu) SaveEvents.InvokeBeforeSave(this.Monitor); - else if (previousMenu is SaveGameMenu) + else if (previousMenu is SaveGameMenu || previousMenu is ShippingMenu) SaveEvents.InvokeAfterSave(this.Monitor); // raise menu events -- cgit From f681f618c1423bab6256aaa1971d35cd25d643a5 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 19 Jan 2017 01:10:13 -0500 Subject: add mod folder path to console --- release-notes.md | 4 ++++ src/StardewModdingAPI/Program.cs | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/release-notes.md b/release-notes.md index c870bbb5..b1b33f2d 100644 --- a/release-notes.md +++ b/release-notes.md @@ -3,7 +3,11 @@ ## 1.7 See [log](https://github.com/Pathoschild/SMAPI/compare/1.6...1.7). +For players: +* Added mod folder path to the console output. + For mod developers: +* Fixed `SaveEvents.BeforeSave` and `SaveEvents.AfterSave` not triggering on days when the player shipped something. * Fixed `PlayerEvents.LoadedGame` and `SaveEvents.AfterLoad` being fired before the world finishes initialising. * Fixed some `LocationEvents`, `PlayerEvents`, and `TimeEvents` being fired during game startup. diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index e5c27e71..40645561 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -100,7 +100,7 @@ namespace StardewModdingAPI Program.Monitor.WriteToConsole = !args.Contains("--no-terminal"); Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); // for consistent log formatting - // add info headers + // add info header Program.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Game1.version} on {Environment.OSVersion}", LogLevel.Info); // initialise user settings @@ -126,8 +126,11 @@ namespace StardewModdingAPI if (!Program.Settings.CheckForUpdates) Program.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 editing or deleting {Constants.ApiConfigPath}.", LogLevel.Warn); if (!Program.Monitor.WriteToConsole) - Program.Monitor.Log($"Writing to the terminal is disabled because the --no-terminal argument was received. This usually means launching the terminal failed.", LogLevel.Warn); + Program.Monitor.Log("Writing to the terminal is disabled because the --no-terminal argument was received. This usually means launching the terminal failed.", LogLevel.Warn); + // print file paths + Program.Monitor.Log($"Mods go here: {Program.ModPath}"); + // initialise legacy log Log.Monitor = Program.GetSecondaryMonitor("legacy mod"); Log.ModRegistry = Program.ModRegistry; -- cgit From 698e012cd23e9ecb1ea2cdc0bccf96bffae0fca0 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 19 Jan 2017 01:18:37 -0500 Subject: simplify overridden game version --- src/StardewModdingAPI/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 40645561..8028df78 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -226,7 +226,7 @@ namespace StardewModdingAPI Program.StardewAssembly = Assembly.UnsafeLoadFrom(Program.GameExecutablePath); Program.StardewProgramType = Program.StardewAssembly.GetType("StardewValley.Program", true); Program.StardewGameInfo = Program.StardewProgramType.GetField("gamePtr"); - Game1.version += $"-Z_MODDED | SMAPI {Constants.ApiVersion}"; + Game1.version += $" | SMAPI {Constants.ApiVersion}"; // add error interceptors #if SMAPI_FOR_WINDOWS -- cgit From 6c07b5f395412dea6cc1b31c88cd1168e850a02c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 19 Jan 2017 01:18:53 -0500 Subject: tweak error text when starting game throws an exception --- src/StardewModdingAPI/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 8028df78..adfd9481 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -294,7 +294,7 @@ namespace StardewModdingAPI } catch (Exception ex) { - Program.Monitor.Log($"SMAPI encountered a fatal error:\n{ex.GetLogSummary()}", LogLevel.Error); + Program.Monitor.Log($"The game encountered a fatal error:\n{ex.GetLogSummary()}", LogLevel.Error); } } -- cgit From 4fe123350c8946ea9da4269063c30a41aaca87e3 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 19 Jan 2017 01:41:55 -0500 Subject: bump several deprecations --- release-notes.md | 2 ++ src/StardewModdingAPI/Inheritance/SObject.cs | 2 +- src/StardewModdingAPI/LogWriter.cs | 2 +- src/StardewModdingAPI/Mod.cs | 2 +- src/StardewModdingAPI/Program.cs | 4 ++-- src/StardewModdingAPI/Version.cs | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/release-notes.md b/release-notes.md index b1b33f2d..1aa0a0b0 100644 --- a/release-notes.md +++ b/release-notes.md @@ -10,6 +10,8 @@ For mod developers: * Fixed `SaveEvents.BeforeSave` and `SaveEvents.AfterSave` not triggering on days when the player shipped something. * Fixed `PlayerEvents.LoadedGame` and `SaveEvents.AfterLoad` being fired before the world finishes initialising. * Fixed some `LocationEvents`, `PlayerEvents`, and `TimeEvents` being fired during game startup. +* Increased deprecation levels for `SObject`, `LogWriter` (not `Log`), and `Mod.Entry(ModHelper)` (not `Mod.Entry(IModHelper)`) to _pending removal_. +* Increased deprecation levels for `Mod.PerSaveConfigFolder`, `Mod.PerSaveConfigPath`, and `Version.VersionString` to _info_. ## 1.6 See [log](https://github.com/Pathoschild/SMAPI/compare/1.5...1.6). diff --git a/src/StardewModdingAPI/Inheritance/SObject.cs b/src/StardewModdingAPI/Inheritance/SObject.cs index eae5424d..0b0a7ec9 100644 --- a/src/StardewModdingAPI/Inheritance/SObject.cs +++ b/src/StardewModdingAPI/Inheritance/SObject.cs @@ -49,7 +49,7 @@ namespace StardewModdingAPI.Inheritance *********/ public SObject() { - Program.DeprecationManager.Warn(nameof(SObject), "0.39.3", DeprecationLevel.Info); + Program.DeprecationManager.Warn(nameof(SObject), "0.39.3", DeprecationLevel.PendingRemoval); this.name = "Modded Item Name"; this.Description = "Modded Item Description"; diff --git a/src/StardewModdingAPI/LogWriter.cs b/src/StardewModdingAPI/LogWriter.cs index 9c2ef515..e22759a7 100644 --- a/src/StardewModdingAPI/LogWriter.cs +++ b/src/StardewModdingAPI/LogWriter.cs @@ -60,7 +60,7 @@ namespace StardewModdingAPI /// Raise a deprecation warning. private void WarnDeprecated() { - Program.DeprecationManager.Warn($"the {nameof(LogWriter)} class", "1.0", DeprecationLevel.Info); + Program.DeprecationManager.Warn($"the {nameof(LogWriter)} class", "1.0", DeprecationLevel.PendingRemoval); } } } \ No newline at end of file diff --git a/src/StardewModdingAPI/Mod.cs b/src/StardewModdingAPI/Mod.cs index d12a7e05..0d35939d 100644 --- a/src/StardewModdingAPI/Mod.cs +++ b/src/StardewModdingAPI/Mod.cs @@ -71,7 +71,7 @@ namespace StardewModdingAPI { get { - Program.DeprecationManager.Warn($"{nameof(Mod)}.{nameof(Mod.PerSaveConfigPath)}", "1.0", DeprecationLevel.Notice); + Program.DeprecationManager.Warn($"{nameof(Mod)}.{nameof(Mod.PerSaveConfigPath)}", "1.0", DeprecationLevel.Info); Program.DeprecationManager.MarkWarned($"{nameof(Mod)}.{nameof(Mod.PerSaveConfigFolder)}", "1.0"); // avoid redundant warnings return Constants.CurrentSavePathExists ? Path.Combine(this.PerSaveConfigFolder, Constants.SaveFolderName + ".json") : ""; } diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index adfd9481..11578fc4 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -439,7 +439,7 @@ namespace StardewModdingAPI // create per-save directory if (manifest.PerSaveConfigs) { - Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.PerSaveConfigs)}", "1.0", DeprecationLevel.Notice); + Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.PerSaveConfigs)}", "1.0", DeprecationLevel.Info); try { string psDir = Path.Combine(directory, "psconfigs"); @@ -552,7 +552,7 @@ namespace StardewModdingAPI if (Program.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] { typeof(object[]) })) Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Mod)}.{nameof(Mod.Entry)}(object[]) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.0", DeprecationLevel.Notice); if (Program.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] { typeof(ModHelper) })) - Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Mod)}.{nameof(Mod.Entry)}({nameof(ModHelper)}) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.1", DeprecationLevel.Info); + Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Mod)}.{nameof(Mod.Entry)}({nameof(ModHelper)}) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.1", DeprecationLevel.PendingRemoval); } catch (Exception ex) { diff --git a/src/StardewModdingAPI/Version.cs b/src/StardewModdingAPI/Version.cs index 87cd4f3d..e66d7be5 100644 --- a/src/StardewModdingAPI/Version.cs +++ b/src/StardewModdingAPI/Version.cs @@ -30,7 +30,7 @@ namespace StardewModdingAPI { get { - Program.DeprecationManager.Warn($"{nameof(Version)}.{nameof(Version.VersionString)}", "1.0", DeprecationLevel.Notice); + Program.DeprecationManager.Warn($"{nameof(Version)}.{nameof(Version.VersionString)}", "1.0", DeprecationLevel.Info); return this.GetSemanticVersion().ToString(); } } -- cgit From ba590b20a6581323ac13fc63d380524789d49c97 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 19 Jan 2017 11:07:48 -0500 Subject: add public mod registry (#220) --- release-notes.md | 1 + src/StardewModdingAPI/Framework/ModRegistry.cs | 29 +++++++++++++++++++++++++- src/StardewModdingAPI/IModHelper.cs | 3 +++ src/StardewModdingAPI/IModRegistry.cs | 20 ++++++++++++++++++ src/StardewModdingAPI/ModHelper.cs | 13 ++++++++++-- src/StardewModdingAPI/Program.cs | 2 +- src/StardewModdingAPI/StardewModdingAPI.csproj | 1 + 7 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 src/StardewModdingAPI/IModRegistry.cs (limited to 'src') diff --git a/release-notes.md b/release-notes.md index 1aa0a0b0..58394c94 100644 --- a/release-notes.md +++ b/release-notes.md @@ -7,6 +7,7 @@ For players: * Added mod folder path to the console output. For mod developers: +* Added a mod registry which provides metadata about loaded mods (see `helper.ModRegistry`). * Fixed `SaveEvents.BeforeSave` and `SaveEvents.AfterSave` not triggering on days when the player shipped something. * Fixed `PlayerEvents.LoadedGame` and `SaveEvents.AfterLoad` being fired before the world finishes initialising. * Fixed some `LocationEvents`, `PlayerEvents`, and `TimeEvents` being fired during game startup. diff --git a/src/StardewModdingAPI/Framework/ModRegistry.cs b/src/StardewModdingAPI/Framework/ModRegistry.cs index 51ec7123..209f1928 100644 --- a/src/StardewModdingAPI/Framework/ModRegistry.cs +++ b/src/StardewModdingAPI/Framework/ModRegistry.cs @@ -7,7 +7,7 @@ using System.Reflection; namespace StardewModdingAPI.Framework { /// Tracks the installed mods. - internal class ModRegistry + internal class ModRegistry : IModRegistry { /********* ** Properties @@ -22,6 +22,33 @@ namespace StardewModdingAPI.Framework /********* ** Public methods *********/ + /**** + ** IModRegistry + ****/ + /// Get metadata for all loaded mods. + public IEnumerable GetAll() + { + return this.Mods.Select(p => p.ModManifest); + } + + /// Get metadata for a loaded mod. + /// The mod's unique ID. + /// Returns the matching mod's metadata, or null if not found. + public IManifest Get(string uniqueID) + { + return this.GetAll().FirstOrDefault(p => p.UniqueID == uniqueID); + } + + /// Get whether a mod has been loaded. + /// The mod's unique ID. + public bool IsLoaded(string uniqueID) + { + return this.GetAll().Any(p => p.UniqueID == uniqueID); + } + + /**** + ** Internal methods + ****/ /// Register a mod as a possible source of deprecation warnings. /// The mod instance. public void Add(IMod mod) diff --git a/src/StardewModdingAPI/IModHelper.cs b/src/StardewModdingAPI/IModHelper.cs index 183b3b2b..02f9c038 100644 --- a/src/StardewModdingAPI/IModHelper.cs +++ b/src/StardewModdingAPI/IModHelper.cs @@ -12,6 +12,9 @@ /// Simplifies access to private game code. IReflectionHelper Reflection { get; } + /// Metadata about loaded mods. + IModRegistry ModRegistry { get; } + /********* ** Public methods diff --git a/src/StardewModdingAPI/IModRegistry.cs b/src/StardewModdingAPI/IModRegistry.cs new file mode 100644 index 00000000..676c9734 --- /dev/null +++ b/src/StardewModdingAPI/IModRegistry.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace StardewModdingAPI +{ + /// Provides metadata about loaded mods. + public interface IModRegistry + { + /// Get metadata for all loaded mods. + IEnumerable GetAll(); + + /// Get metadata for a loaded mod. + /// The mod's unique ID. + /// Returns the matching mod's metadata, or null if not found. + IManifest Get(string uniqueID); + + /// Get whether a mod has been loaded. + /// The mod's unique ID. + bool IsLoaded(string uniqueID); + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/ModHelper.cs b/src/StardewModdingAPI/ModHelper.cs index 78b3eefa..c20130cf 100644 --- a/src/StardewModdingAPI/ModHelper.cs +++ b/src/StardewModdingAPI/ModHelper.cs @@ -30,22 +30,31 @@ namespace StardewModdingAPI /// Simplifies access to private game code. public IReflectionHelper Reflection { get; } = new ReflectionHelper(); + /// Metadata about loaded mods. + public IModRegistry ModRegistry { get; } + /********* ** Public methods *********/ /// Construct an instance. /// The mod directory path. - public ModHelper(string modDirectory) + /// Metadata about loaded mods. + /// An argument is null or invalid. + /// The path does not exist on disk. + public ModHelper(string modDirectory, IModRegistry modRegistry) { // validate + if (modRegistry == null) + throw new ArgumentException("The mod registry cannot be null."); if (string.IsNullOrWhiteSpace(modDirectory)) - throw new InvalidOperationException("The mod directory cannot be empty."); + throw new ArgumentException("The mod directory cannot be empty."); if (!Directory.Exists(modDirectory)) throw new InvalidOperationException("The specified mod directory does not exist."); // initialise this.DirectoryPath = modDirectory; + this.ModRegistry = modRegistry; } /**** diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 11578fc4..29f71cb7 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -353,7 +353,7 @@ namespace StardewModdingAPI } // get helper - IModHelper helper = new ModHelper(directory); + IModHelper helper = new ModHelper(directory, Program.ModRegistry); // get manifest path string manifestPath = Path.Combine(directory, "manifest.json"); diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 125f287e..d56b6866 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -148,6 +148,7 @@ + -- cgit From 1cf8a628dc730e656a344facb731b5bafa36d046 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 19 Jan 2017 11:34:24 -0500 Subject: only call mod.Entry() once all mods have been loaded (#220) --- release-notes.md | 1 + src/StardewModdingAPI/Program.cs | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/release-notes.md b/release-notes.md index 58394c94..74cd8c9d 100644 --- a/release-notes.md +++ b/release-notes.md @@ -8,6 +8,7 @@ For players: For mod developers: * Added a mod registry which provides metadata about loaded mods (see `helper.ModRegistry`). +* `Mod.Entry()` is now only called once all mods have loaded. * Fixed `SaveEvents.BeforeSave` and `SaveEvents.AfterSave` not triggering on days when the player shipped something. * Fixed `PlayerEvents.LoadedGame` and `SaveEvents.AfterLoad` being fired before the world finishes initialising. * Fixed some `LocationEvents`, `PlayerEvents`, and `TimeEvents` being fired during game startup. diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 29f71cb7..bf0805be 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -130,7 +130,7 @@ namespace StardewModdingAPI // print file paths Program.Monitor.Log($"Mods go here: {Program.ModPath}"); - + // initialise legacy log Log.Monitor = Program.GetSecondaryMonitor("legacy mod"); Log.ModRegistry = Program.ModRegistry; @@ -336,7 +336,7 @@ namespace StardewModdingAPI Program.Monitor.Log($"Couldn't read metadata file at {Constants.ApiModMetadataPath}. SMAPI will still run, but some features may be disabled.\n{ex}", LogLevel.Warn); } - // load mods + // load mod assemblies foreach (string directory in Directory.GetDirectories(Program.ModPath)) { string directoryName = new DirectoryInfo(directory).Name; @@ -539,8 +539,11 @@ namespace StardewModdingAPI Program.Monitor.Log($"{errorPrefix}: an error occurred while loading the target DLL.\n{ex.GetLogSummary()}", LogLevel.Error); continue; } + } - // call mod entry + // initialise mods + foreach (Mod mod in Program.ModRegistry.GetMods()) + { try { // call entry methods @@ -550,13 +553,13 @@ namespace StardewModdingAPI // raise deprecation warning for old Entry() methods if (Program.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] { typeof(object[]) })) - Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Mod)}.{nameof(Mod.Entry)}(object[]) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.0", DeprecationLevel.Notice); + Program.DeprecationManager.Warn(mod.ModManifest.Name, $"{nameof(Mod)}.{nameof(Mod.Entry)}(object[]) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.0", DeprecationLevel.Notice); if (Program.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] { typeof(ModHelper) })) - Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Mod)}.{nameof(Mod.Entry)}({nameof(ModHelper)}) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.1", DeprecationLevel.PendingRemoval); + Program.DeprecationManager.Warn(mod.ModManifest.Name, $"{nameof(Mod)}.{nameof(Mod.Entry)}({nameof(ModHelper)}) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.1", DeprecationLevel.PendingRemoval); } catch (Exception ex) { - Program.Monitor.Log($"The {manifest.Name} mod failed on entry initialisation. It will still be loaded, but may not function correctly.\n{ex.GetLogSummary()}", LogLevel.Warn); + Program.Monitor.Log($"The {mod.ModManifest.Name} mod failed on entry initialisation. It will still be loaded, but may not function correctly.\n{ex.GetLogSummary()}", LogLevel.Warn); } } -- cgit From 5ccd5b5df7d7fcbbf176bf2d99a274ee39681ea9 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 19 Jan 2017 11:43:32 -0500 Subject: log deprecation warnings after list of loaded mods (#220) --- release-notes.md | 1 + src/StardewModdingAPI/Program.cs | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/release-notes.md b/release-notes.md index 74cd8c9d..635abccb 100644 --- a/release-notes.md +++ b/release-notes.md @@ -9,6 +9,7 @@ For players: For mod developers: * Added a mod registry which provides metadata about loaded mods (see `helper.ModRegistry`). * `Mod.Entry()` is now only called once all mods have loaded. +* Deprecation warnings are now logged after the list of loaded mods. * Fixed `SaveEvents.BeforeSave` and `SaveEvents.AfterSave` not triggering on days when the player shipped something. * Fixed `PlayerEvents.LoadedGame` and `SaveEvents.AfterLoad` being fired before the world finishes initialising. * Fixed some `LocationEvents`, `PlayerEvents`, and `TimeEvents` being fired during game startup. diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index bf0805be..ec3ccce7 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -337,6 +337,7 @@ namespace StardewModdingAPI } // load mod assemblies + List deprecationWarnings = new List(); // queue up deprecation warnings to show after mod list foreach (string directory in Directory.GetDirectories(Program.ModPath)) { string directoryName = new DirectoryInfo(directory).Name; @@ -391,7 +392,7 @@ namespace StardewModdingAPI // log deprecated fields if (manifest.UsedAuthourField) - Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.Authour)}", "1.0", DeprecationLevel.Notice); + deprecationWarnings.Add(() => Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.Authour)}", "1.0", DeprecationLevel.Notice)); } catch (Exception ex) { @@ -439,7 +440,7 @@ namespace StardewModdingAPI // create per-save directory if (manifest.PerSaveConfigs) { - Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.PerSaveConfigs)}", "1.0", DeprecationLevel.Info); + deprecationWarnings.Add(() => Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.PerSaveConfigs)}", "1.0", DeprecationLevel.Info)); try { string psDir = Path.Combine(directory, "psconfigs"); @@ -541,6 +542,11 @@ namespace StardewModdingAPI } } + // log deprecation warnings + foreach (Action warning in deprecationWarnings) + warning(); + deprecationWarnings = null; + // initialise mods foreach (Mod mod in Program.ModRegistry.GetMods()) { -- cgit From b90387668d5fee2ebf67d730dbb3aa8cec6a5b67 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 19 Jan 2017 12:22:32 -0500 Subject: update for 1.7 release --- release-notes.md | 15 +++++++-------- src/GlobalAssemblyInfo.cs | 4 ++-- src/StardewModdingAPI/Constants.cs | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/release-notes.md b/release-notes.md index 635abccb..4e7b3a30 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,20 +1,19 @@ -# Release notes +# Release notes ## 1.7 See [log](https://github.com/Pathoschild/SMAPI/compare/1.6...1.7). For players: -* Added mod folder path to the console output. +* The console now shows the folder path where mods should be added. +* The console now shows deprecation warnings after the list of loaded mods (instead of intermingled). For mod developers: -* Added a mod registry which provides metadata about loaded mods (see `helper.ModRegistry`). -* `Mod.Entry()` is now only called once all mods have loaded. -* Deprecation warnings are now logged after the list of loaded mods. -* Fixed `SaveEvents.BeforeSave` and `SaveEvents.AfterSave` not triggering on days when the player shipped something. +* Added a mod registry which provides metadata about loaded mods. +* The `Entry(…)` method is now deferred until all mods are loaded. +* Fixed `SaveEvents.BeforeSave` and `.AfterSave` not triggering on days when the player shipped something. * Fixed `PlayerEvents.LoadedGame` and `SaveEvents.AfterLoad` being fired before the world finishes initialising. * Fixed some `LocationEvents`, `PlayerEvents`, and `TimeEvents` being fired during game startup. -* Increased deprecation levels for `SObject`, `LogWriter` (not `Log`), and `Mod.Entry(ModHelper)` (not `Mod.Entry(IModHelper)`) to _pending removal_. -* Increased deprecation levels for `Mod.PerSaveConfigFolder`, `Mod.PerSaveConfigPath`, and `Version.VersionString` to _info_. +* Increased deprecation levels for `SObject`, `LogWriter` (not `Log`), and `Mod.Entry(ModHelper)` (not `Mod.Entry(IModHelper)`) to _pending removal_. Increased deprecation levels for `Mod.PerSaveConfigFolder`, `Mod.PerSaveConfigPath`, and `Version.VersionString` to _info_. ## 1.6 See [log](https://github.com/Pathoschild/SMAPI/compare/1.5...1.6). diff --git a/src/GlobalAssemblyInfo.cs b/src/GlobalAssemblyInfo.cs index 3df34a96..29e5dae7 100644 --- a/src/GlobalAssemblyInfo.cs +++ b/src/GlobalAssemblyInfo.cs @@ -2,5 +2,5 @@ using System.Runtime.InteropServices; [assembly: ComVisible(false)] -[assembly: AssemblyVersion("1.6.0.0")] -[assembly: AssemblyFileVersion("1.6.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.7.0.0")] +[assembly: AssemblyFileVersion("1.7.0.0")] \ No newline at end of file diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 2211e167..df527dfe 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -30,7 +30,7 @@ namespace StardewModdingAPI public static readonly Version Version = (Version)Constants.ApiVersion; /// SMAPI's current semantic version. - public static ISemanticVersion ApiVersion => new Version(1, 6, 0, null, suppressDeprecationWarning: true); + public static ISemanticVersion ApiVersion => new Version(1, 7, 0, null, suppressDeprecationWarning: true); /// The minimum supported version of Stardew Valley. public const string MinimumGameVersion = "1.1"; -- cgit