From 4b325f61b370b24403fa10616178dceefa773420 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 23 Dec 2018 16:51:38 -0500 Subject: allow Read/WriteSaveFile as soon as the save is loaded --- src/SMAPI/Framework/ModHelpers/DataHelper.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/SMAPI/Framework') diff --git a/src/SMAPI/Framework/ModHelpers/DataHelper.cs b/src/SMAPI/Framework/ModHelpers/DataHelper.cs index e5100aed..242b8ab1 100644 --- a/src/SMAPI/Framework/ModHelpers/DataHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/DataHelper.cs @@ -77,9 +77,9 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The player hasn't loaded a save file yet or isn't the main player. public TModel ReadSaveData(string key) where TModel : class { - if (!Context.IsSaveLoaded) + if (!Game1.hasLoadedGame) throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.ReadSaveData)} when a save file isn't loaded."); - if (!Context.IsMainPlayer) + if (!Game1.IsMasterGame) throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.ReadSaveData)} because this isn't the main player. (Save files are stored on the main player's computer.)"); return Game1.CustomData.TryGetValue(this.GetSaveFileKey(key), out string value) @@ -94,9 +94,9 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The player hasn't loaded a save file yet or isn't the main player. public void WriteSaveData(string key, TModel data) where TModel : class { - if (!Context.IsSaveLoaded) + if (!Game1.hasLoadedGame) throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteSaveData)} when a save file isn't loaded."); - if (!Context.IsMainPlayer) + if (!Game1.IsMasterGame) throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.ReadSaveData)} because this isn't the main player. (Save files are stored on the main player's computer.)"); string internalKey = this.GetSaveFileKey(key); -- cgit From 041bd2d6ba726eeea88afed3be307343a6f9286b Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 23 Dec 2018 19:26:02 -0500 Subject: add Specialised.SavePreloaded event --- src/SMAPI/Framework/Events/EventManager.cs | 6 +++++- src/SMAPI/Framework/Events/ModGameLoopEvents.cs | 2 +- src/SMAPI/Framework/Events/ModSpecialisedEvents.cs | 7 +++++++ src/SMAPI/Framework/SGame.cs | 23 ++++++++++++++++++---- 4 files changed, 32 insertions(+), 6 deletions(-) (limited to 'src/SMAPI/Framework') diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index 0ad85adf..bd862046 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -70,7 +70,7 @@ namespace StardewModdingAPI.Framework.Events /// Raised after the game finishes writing data to the save file (except the initial save creation). public readonly ManagedEvent Saved; - /// Raised after the player loads a save slot. + /// Raised after the player loads a save slot and the world is initialised. public readonly ManagedEvent SaveLoaded; /// Raised after the game begins a new day, including when loading a save. @@ -151,6 +151,9 @@ namespace StardewModdingAPI.Framework.Events /**** ** Specialised ****/ + /// Raised immediately after the player loads a save slot, but before the world is fully initialised. + public readonly ManagedEvent SavePreloaded; + /// Raised before the game performs its overall update tick (≈60 times per second). See notes on . public readonly ManagedEvent UnvalidatedUpdateTicking; @@ -408,6 +411,7 @@ namespace StardewModdingAPI.Framework.Events this.ObjectListChanged = ManageEventOf(nameof(IModEvents.World), nameof(IWorldEvents.ObjectListChanged)); this.TerrainFeatureListChanged = ManageEventOf(nameof(IModEvents.World), nameof(IWorldEvents.TerrainFeatureListChanged)); + this.SavePreloaded = ManageEventOf(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.SavePreloaded)); this.UnvalidatedUpdateTicking = ManageEventOf(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicking)); this.UnvalidatedUpdateTicked = ManageEventOf(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicked)); diff --git a/src/SMAPI/Framework/Events/ModGameLoopEvents.cs b/src/SMAPI/Framework/Events/ModGameLoopEvents.cs index a5beac99..3a764ab0 100644 --- a/src/SMAPI/Framework/Events/ModGameLoopEvents.cs +++ b/src/SMAPI/Framework/Events/ModGameLoopEvents.cs @@ -58,7 +58,7 @@ namespace StardewModdingAPI.Framework.Events remove => this.EventManager.Saved.Remove(value); } - /// Raised after the player loads a save slot. + /// Raised after the player loads a save slot and the world is initialised. public event EventHandler SaveLoaded { add => this.EventManager.SaveLoaded.Add(value); diff --git a/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs b/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs index 17c32bb8..83e349cf 100644 --- a/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs +++ b/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs @@ -9,6 +9,13 @@ namespace StardewModdingAPI.Framework.Events /********* ** Accessors *********/ + /// Raised immediately after the player loads a save slot, but before the world is fully initialised. The save and game data are available at this point, but some in-game content (like location maps) haven't been initialised yet. + public event EventHandler SavePreloaded + { + add => this.EventManager.SavePreloaded.Add(value); + remove => this.EventManager.SavePreloaded.Remove(value); + } + /// Raised before the game state is updated (≈60 times per second), regardless of normal SMAPI validation. This event is not thread-safe and may be invoked while game logic is running asynchronously. Changes to game state in this method may crash the game or corrupt an in-progress save. Do not use this event unless you're fully aware of the context in which your code will be run. Mods using this event will trigger a stability warning in the SMAPI console. public event EventHandler UnvalidatedUpdateTicking { diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index d515d3ad..befd9cef 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -69,8 +69,11 @@ 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); + /// Whether was raised for this session. + private bool RaisedPreloadedEvent; + /// Whether the after-load events were raised for this session. - private bool RaisedAfterLoadEvent; + private bool RaisedLoadedEvent; /// Whether the game is saving and SMAPI has already raised . private bool IsBetweenSaveEvents; @@ -217,6 +220,7 @@ namespace StardewModdingAPI.Framework private void OnReturnedToTitle() { this.Monitor.Log("Context: returned to title", LogLevel.Trace); + this.RaisedPreloadedEvent = false; this.Multiplayer.CleanupOnMultiplayerExit(); this.Events.ReturnedToTitle.RaiseEmpty(); #if !SMAPI_3_0_STRICT @@ -466,7 +470,7 @@ namespace StardewModdingAPI.Framework *********/ if (wasWorldReady && !Context.IsWorldReady) this.OnReturnedToTitle(); - else if (!this.RaisedAfterLoadEvent && Context.IsWorldReady) + else if (!this.RaisedLoadedEvent && Context.IsWorldReady) { // print context string context = $"Context: loaded saved game '{Constants.SaveFolderName}', starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}."; @@ -480,7 +484,7 @@ namespace StardewModdingAPI.Framework this.Monitor.Log(context, LogLevel.Trace); // raise events - this.RaisedAfterLoadEvent = true; + this.RaisedLoadedEvent = true; this.Events.SaveLoaded.RaiseEmpty(); this.Events.DayStarted.RaiseEmpty(); #if !SMAPI_3_0_STRICT @@ -824,8 +828,19 @@ namespace StardewModdingAPI.Framework ** Game update *********/ this.TicksElapsed++; + + // game launched if (this.TicksElapsed == 1) this.Events.GameLaunched.Raise(new GameLaunchedEventArgs()); + + // preloaded + if (Context.IsSaveLoaded && !this.RaisedPreloadedEvent) + { + this.RaisedPreloadedEvent = true; + this.Events.SavePreloaded.RaiseEmpty(); + } + + // update tick this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed)); this.Events.UpdateTicking.Raise(new UpdateTickingEventArgs(this.TicksElapsed)); try @@ -1639,7 +1654,7 @@ namespace StardewModdingAPI.Framework { Context.IsWorldReady = false; this.AfterLoadTimer.Reset(); - this.RaisedAfterLoadEvent = false; + this.RaisedLoadedEvent = false; } #if !SMAPI_3_0_STRICT -- cgit From 6ad52d607c49b16c6933060375086830edd9a1f9 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 24 Dec 2018 17:28:58 -0500 Subject: add Specialised.LoadStageChanged event --- src/SMAPI/Framework/Events/EventManager.cs | 6 +- src/SMAPI/Framework/Events/ModSpecialisedEvents.cs | 8 +- src/SMAPI/Framework/SCore.cs | 13 ++-- src/SMAPI/Framework/SGame.cs | 89 ++++++++++++++-------- 4 files changed, 73 insertions(+), 43 deletions(-) (limited to 'src/SMAPI/Framework') diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index bd862046..b7f00f52 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -151,8 +151,8 @@ namespace StardewModdingAPI.Framework.Events /**** ** Specialised ****/ - /// Raised immediately after the player loads a save slot, but before the world is fully initialised. - public readonly ManagedEvent SavePreloaded; + /// Raised when the low-level stage in the game's loading process has changed. See notes on . + public readonly ManagedEvent LoadStageChanged; /// Raised before the game performs its overall update tick (≈60 times per second). See notes on . public readonly ManagedEvent UnvalidatedUpdateTicking; @@ -411,7 +411,7 @@ namespace StardewModdingAPI.Framework.Events this.ObjectListChanged = ManageEventOf(nameof(IModEvents.World), nameof(IWorldEvents.ObjectListChanged)); this.TerrainFeatureListChanged = ManageEventOf(nameof(IModEvents.World), nameof(IWorldEvents.TerrainFeatureListChanged)); - this.SavePreloaded = ManageEventOf(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.SavePreloaded)); + this.LoadStageChanged = ManageEventOf(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.LoadStageChanged)); this.UnvalidatedUpdateTicking = ManageEventOf(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicking)); this.UnvalidatedUpdateTicked = ManageEventOf(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicked)); diff --git a/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs b/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs index 83e349cf..7c3e9dee 100644 --- a/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs +++ b/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs @@ -9,11 +9,11 @@ namespace StardewModdingAPI.Framework.Events /********* ** Accessors *********/ - /// Raised immediately after the player loads a save slot, but before the world is fully initialised. The save and game data are available at this point, but some in-game content (like location maps) haven't been initialised yet. - public event EventHandler SavePreloaded + /// Raised when the low-level stage in the game's loading process has changed. This is an advanced event for mods which need to run code at specific points in the loading process. The available stages or when they happen might change without warning in future versions (e.g. due to changes in the game's load process), so mods using this event are more likely to break or have bugs. Most mods should use instead. + public event EventHandler LoadStageChanged { - add => this.EventManager.SavePreloaded.Add(value); - remove => this.EventManager.SavePreloaded.Remove(value); + add => this.EventManager.LoadStageChanged.Add(value); + remove => this.EventManager.LoadStageChanged.Remove(value); } /// Raised before the game state is updated (≈60 times per second), regardless of normal SMAPI validation. This event is not thread-safe and may be invoked while game logic is running asynchronously. Changes to game state in this method may crash the game or corrupt an in-progress save. Do not use this event unless you're fully aware of the context in which your code will be run. Mods using this event will trigger a stability warning in the SMAPI console. diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 679838ba..00801b72 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -181,12 +181,6 @@ namespace StardewModdingAPI.Framework return; } #endif - - // apply game patches - new GamePatcher(this.Monitor).Apply( - new DialogueErrorPatch(this.MonitorForGame, this.Reflection), - new ObjectErrorPatch() - ); } /// Launch SMAPI. @@ -237,6 +231,13 @@ namespace StardewModdingAPI.Framework this.GameInstance = new SGame(this.Monitor, this.MonitorForGame, this.Reflection, this.EventManager, this.Toolkit.JsonHelper, this.ModRegistry, SCore.DeprecationManager, this.OnLocaleChanged, this.InitialiseAfterGameStart, this.Dispose); StardewValley.Program.gamePtr = this.GameInstance; + // apply game patches + new GamePatcher(this.Monitor).Apply( + new DialogueErrorPatch(this.MonitorForGame, this.Reflection), + new ObjectErrorPatch(), + new LoadForNewGamePatch(this.Reflection, this.GameInstance.OnLoadStageChanged) + ); + // add exit handler new Thread(() => { diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index befd9cef..cb62de2a 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -69,11 +69,8 @@ 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); - /// Whether was raised for this session. - private bool RaisedPreloadedEvent; - - /// Whether the after-load events were raised for this session. - private bool RaisedLoadedEvent; + /// 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; @@ -216,16 +213,33 @@ namespace StardewModdingAPI.Framework this.Events.ModMessageReceived.RaiseForMods(new ModMessageReceivedEventArgs(message), mod => mod != null && modIDs.Contains(mod.Manifest.UniqueID)); } - /// A callback raised when the player quits a save and returns to the title screen. - private void OnReturnedToTitle() + /// A callback invoked when the game's low-level load stage changes. + /// The new load stage. + internal void OnLoadStageChanged(LoadStage newStage) { - this.Monitor.Log("Context: returned to title", LogLevel.Trace); - this.RaisedPreloadedEvent = false; - this.Multiplayer.CleanupOnMultiplayerExit(); - this.Events.ReturnedToTitle.RaiseEmpty(); + // nothing to do + if (newStage == this.LoadStage) + return; + + // update data + LoadStage oldStage = this.LoadStage; + this.LoadStage = newStage; + if (newStage == LoadStage.None) + { + this.Monitor.Log("Context: returned to title", LogLevel.Trace); + this.Multiplayer.CleanupOnMultiplayerExit(); + } + this.Monitor.VerboseLog($"Context: load stage changed to {newStage}"); + + // raise events + this.Events.LoadStageChanged.Raise(new LoadStageChangedEventArgs(oldStage, newStage)); + if (newStage == LoadStage.None) + { + this.Events.ReturnedToTitle.RaiseEmpty(); #if !SMAPI_3_0_STRICT - this.Events.Legacy_AfterReturnToTitle.Raise(); + this.Events.Legacy_AfterReturnToTitle.Raise(); #endif + } } /// Constructor a content manager to read XNB files. @@ -284,7 +298,29 @@ namespace StardewModdingAPI.Framework { this.Monitor.Log("Game loader synchronising...", LogLevel.Trace); while (Game1.currentLoader?.MoveNext() == true) - ; + { + // raise load stage changed + switch (Game1.currentLoader.Current) + { + case 20: + this.OnLoadStageChanged(LoadStage.SaveParsed); + break; + + case 36: + this.OnLoadStageChanged(LoadStage.SaveLoadedBasicInfo); + break; + + case 50: + this.OnLoadStageChanged(LoadStage.SaveLoadedLocations); + break; + + default: + if (Game1.gameMode == Game1.playingGameMode) + this.OnLoadStageChanged(LoadStage.Preloaded); + break; + } + } + Game1.currentLoader = null; this.Monitor.Log("Game loader done.", LogLevel.Trace); } @@ -411,6 +447,7 @@ namespace StardewModdingAPI.Framework // raise after-create this.IsBetweenCreateEvents = false; this.Monitor.Log($"Context: after save creation, starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace); + this.OnLoadStageChanged(LoadStage.CreatedSaveFile); this.Events.SaveCreated.RaiseEmpty(); #if !SMAPI_3_0_STRICT this.Events.Legacy_AfterCreateSave.Raise(); @@ -434,7 +471,10 @@ namespace StardewModdingAPI.Framework *********/ bool wasWorldReady = Context.IsWorldReady; if ((Context.IsWorldReady && !Context.IsSaveLoaded) || Game1.exitToTitle) - this.MarkWorldNotReady(); + { + Context.IsWorldReady = false; + this.AfterLoadTimer.Reset(); + } else if (Context.IsSaveLoaded && this.AfterLoadTimer.Current > 0 && Game1.currentLocation != null) { if (Game1.dayOfMonth != 0) // wait until new-game intro finishes (world not fully initialised yet) @@ -469,8 +509,8 @@ namespace StardewModdingAPI.Framework ** Load / return-to-title events *********/ if (wasWorldReady && !Context.IsWorldReady) - this.OnReturnedToTitle(); - else if (!this.RaisedLoadedEvent && Context.IsWorldReady) + this.OnLoadStageChanged(LoadStage.None); + else if (Context.IsWorldReady && this.LoadStage != LoadStage.Ready) { // print context string context = $"Context: loaded saved game '{Constants.SaveFolderName}', starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}."; @@ -484,7 +524,7 @@ namespace StardewModdingAPI.Framework this.Monitor.Log(context, LogLevel.Trace); // raise events - this.RaisedLoadedEvent = true; + this.OnLoadStageChanged(LoadStage.Ready); this.Events.SaveLoaded.RaiseEmpty(); this.Events.DayStarted.RaiseEmpty(); #if !SMAPI_3_0_STRICT @@ -834,11 +874,8 @@ namespace StardewModdingAPI.Framework this.Events.GameLaunched.Raise(new GameLaunchedEventArgs()); // preloaded - if (Context.IsSaveLoaded && !this.RaisedPreloadedEvent) - { - this.RaisedPreloadedEvent = true; - this.Events.SavePreloaded.RaiseEmpty(); - } + if (Context.IsSaveLoaded && this.LoadStage != LoadStage.Loaded && this.LoadStage != LoadStage.Ready) + this.OnLoadStageChanged(LoadStage.Loaded); // update tick this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed)); @@ -1649,14 +1686,6 @@ namespace StardewModdingAPI.Framework /**** ** Methods ****/ - /// Perform any cleanup needed when a save is unloaded. - private void MarkWorldNotReady() - { - Context.IsWorldReady = false; - this.AfterLoadTimer.Reset(); - this.RaisedLoadedEvent = false; - } - #if !SMAPI_3_0_STRICT /// Raise the if there are any listeners. /// Whether to create a new sprite batch. -- cgit From 8e0573d7d9f18792a19e741660b6a090cca1fb38 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 25 Dec 2018 15:10:22 -0500 Subject: add GameLoop.OneSecondUpdateTicking/Ticked --- src/SMAPI/Framework/Events/EventManager.cs | 8 ++++++++ src/SMAPI/Framework/Events/ModGameLoopEvents.cs | 14 ++++++++++++++ src/SMAPI/Framework/SGame.cs | 5 +++++ 3 files changed, 27 insertions(+) (limited to 'src/SMAPI/Framework') diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index b7f00f52..13244601 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -58,6 +58,12 @@ namespace StardewModdingAPI.Framework.Events /// Raised after the game performs its overall update tick (≈60 times per second). public readonly ManagedEvent UpdateTicked; + /// Raised once per second before the game performs its overall update tick. + public readonly ManagedEvent OneSecondUpdateTicking; + + /// Raised once per second after the game performs its overall update tick. + public readonly ManagedEvent OneSecondUpdateTicked; + /// Raised before the game creates the save file. public readonly ManagedEvent SaveCreating; @@ -380,6 +386,8 @@ namespace StardewModdingAPI.Framework.Events this.GameLaunched = ManageEventOf(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.GameLaunched)); this.UpdateTicking = ManageEventOf(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.UpdateTicking)); this.UpdateTicked = ManageEventOf(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.UpdateTicked)); + this.OneSecondUpdateTicking = ManageEventOf(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.OneSecondUpdateTicking)); + this.OneSecondUpdateTicked = ManageEventOf(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.OneSecondUpdateTicked)); this.SaveCreating = ManageEventOf(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.SaveCreating)); this.SaveCreated = ManageEventOf(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.SaveCreated)); this.Saving = ManageEventOf(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.Saving)); diff --git a/src/SMAPI/Framework/Events/ModGameLoopEvents.cs b/src/SMAPI/Framework/Events/ModGameLoopEvents.cs index 3a764ab0..0177c22e 100644 --- a/src/SMAPI/Framework/Events/ModGameLoopEvents.cs +++ b/src/SMAPI/Framework/Events/ModGameLoopEvents.cs @@ -30,6 +30,20 @@ namespace StardewModdingAPI.Framework.Events remove => this.EventManager.UpdateTicked.Remove(value); } + /// Raised once per second before the game state is updated. + public event EventHandler OneSecondUpdateTicking + { + add => this.EventManager.OneSecondUpdateTicking.Add(value); + remove => this.EventManager.OneSecondUpdateTicking.Remove(value); + } + + /// Raised once per second after the game state is updated. + public event EventHandler OneSecondUpdateTicked + { + add => this.EventManager.OneSecondUpdateTicked.Add(value); + remove => this.EventManager.OneSecondUpdateTicked.Remove(value); + } + /// Raised before the game creates a new save file. public event EventHandler SaveCreating { diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index cb62de2a..8abe4d16 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -878,8 +878,11 @@ namespace StardewModdingAPI.Framework this.OnLoadStageChanged(LoadStage.Loaded); // update tick + bool isOneSecond = this.TicksElapsed % 60 == 0; this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed)); this.Events.UpdateTicking.Raise(new UpdateTickingEventArgs(this.TicksElapsed)); + if (isOneSecond) + this.Events.OneSecondUpdateTicking.Raise(new OneSecondUpdateTickingEventArgs(this.TicksElapsed)); try { this.Input.UpdateSuppression(); @@ -891,6 +894,8 @@ namespace StardewModdingAPI.Framework } this.Events.UnvalidatedUpdateTicked.Raise(new UnvalidatedUpdateTickedEventArgs(this.TicksElapsed)); this.Events.UpdateTicked.Raise(new UpdateTickedEventArgs(this.TicksElapsed)); + if (isOneSecond) + this.Events.OneSecondUpdateTicked.Raise(new OneSecondUpdateTickedEventArgs(this.TicksElapsed)); /********* ** Update events -- cgit From 382b5fe914894b87e44462060ca7ae8415c9533e Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 25 Dec 2018 15:12:58 -0500 Subject: minor performance optimisation --- src/SMAPI/Framework/Events/ManagedEvent.cs | 2 + src/SMAPI/Framework/SGame.cs | 294 +++++++++++++++-------------- 2 files changed, 150 insertions(+), 146 deletions(-) (limited to 'src/SMAPI/Framework') diff --git a/src/SMAPI/Framework/Events/ManagedEvent.cs b/src/SMAPI/Framework/Events/ManagedEvent.cs index 65f6e38e..ebd79e6c 100644 --- a/src/SMAPI/Framework/Events/ManagedEvent.cs +++ b/src/SMAPI/Framework/Events/ManagedEvent.cs @@ -93,6 +93,7 @@ namespace StardewModdingAPI.Framework.Events } } +#if !SMAPI_3_0_STRICT /// An event wrapper which intercepts and logs errors in handler code. internal class ManagedEvent : ManagedEventBase { @@ -156,4 +157,5 @@ namespace StardewModdingAPI.Framework.Events } } } +#endif } diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index 8abe4d16..957fd1d6 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -96,15 +96,9 @@ namespace StardewModdingAPI.Framework /// Monitors the entire game state for changes. private WatcherCore Watchers; - /// An index incremented on every tick and reset every 60th tick (0–59). - private int CurrentUpdateTick; - /// Whether post-game-startup initialisation has been performed. private bool IsInitialised; - /// The number of update ticks which have already executed. - private uint TicksElapsed; - /// Whether the next content manager requested by the game will be for . private bool NextContentManagerIsMain; @@ -271,6 +265,8 @@ namespace StardewModdingAPI.Framework /// A snapshot of the game timing state. protected override void Update(GameTime gameTime) { + var events = this.Events; + try { this.DeprecationManager.PrintQueued(); @@ -342,11 +338,11 @@ namespace StardewModdingAPI.Framework // update tick are neglible and not worth the complications of bypassing Game1.Update. if (Game1._newDayTask != null || Game1.gameMode == Game1.loadingMode) { - this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed)); + events.UnvalidatedUpdateTicking.RaiseEmpty(); base.Update(gameTime); - this.Events.UnvalidatedUpdateTicked.Raise(new UnvalidatedUpdateTickedEventArgs(this.TicksElapsed)); + events.UnvalidatedUpdateTicked.RaiseEmpty(); #if !SMAPI_3_0_STRICT - this.Events.Legacy_UnvalidatedUpdateTick.Raise(); + events.Legacy_UnvalidatedUpdateTick.Raise(); #endif return; } @@ -416,9 +412,9 @@ namespace StardewModdingAPI.Framework { this.IsBetweenCreateEvents = true; this.Monitor.Log("Context: before save creation.", LogLevel.Trace); - this.Events.SaveCreating.RaiseEmpty(); + events.SaveCreating.RaiseEmpty(); #if !SMAPI_3_0_STRICT - this.Events.Legacy_BeforeCreateSave.Raise(); + events.Legacy_BeforeCreateSave.Raise(); #endif } @@ -427,18 +423,18 @@ namespace StardewModdingAPI.Framework { this.IsBetweenSaveEvents = true; this.Monitor.Log("Context: before save.", LogLevel.Trace); - this.Events.Saving.RaiseEmpty(); + events.Saving.RaiseEmpty(); #if !SMAPI_3_0_STRICT - this.Events.Legacy_BeforeSave.Raise(); + events.Legacy_BeforeSave.Raise(); #endif } // suppress non-save events - this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed)); + events.UnvalidatedUpdateTicking.RaiseEmpty(); base.Update(gameTime); - this.Events.UnvalidatedUpdateTicked.Raise(new UnvalidatedUpdateTickedEventArgs(this.TicksElapsed)); + events.UnvalidatedUpdateTicked.RaiseEmpty(); #if !SMAPI_3_0_STRICT - this.Events.Legacy_UnvalidatedUpdateTick.Raise(); + events.Legacy_UnvalidatedUpdateTick.Raise(); #endif return; } @@ -448,9 +444,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.OnLoadStageChanged(LoadStage.CreatedSaveFile); - this.Events.SaveCreated.RaiseEmpty(); + events.SaveCreated.RaiseEmpty(); #if !SMAPI_3_0_STRICT - this.Events.Legacy_AfterCreateSave.Raise(); + events.Legacy_AfterCreateSave.Raise(); #endif } if (this.IsBetweenSaveEvents) @@ -458,11 +454,11 @@ namespace StardewModdingAPI.Framework // raise after-save this.IsBetweenSaveEvents = false; this.Monitor.Log($"Context: after save, starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace); - this.Events.Saved.RaiseEmpty(); - this.Events.DayStarted.RaiseEmpty(); + events.Saved.RaiseEmpty(); + events.DayStarted.RaiseEmpty(); #if !SMAPI_3_0_STRICT - this.Events.Legacy_AfterSave.Raise(); - this.Events.Legacy_AfterDayStarted.Raise(); + events.Legacy_AfterSave.Raise(); + events.Legacy_AfterDayStarted.Raise(); #endif } @@ -499,7 +495,7 @@ namespace StardewModdingAPI.Framework this.OnLocaleChanged(); #if !SMAPI_3_0_STRICT - this.Events.Legacy_LocaleChanged.Raise(new EventArgsValueChanged(was.ToString(), now.ToString())); + events.Legacy_LocaleChanged.Raise(new EventArgsValueChanged(was.ToString(), now.ToString())); #endif this.Watchers.LocaleWatcher.Reset(); @@ -525,11 +521,11 @@ namespace StardewModdingAPI.Framework // raise events this.OnLoadStageChanged(LoadStage.Ready); - this.Events.SaveLoaded.RaiseEmpty(); - this.Events.DayStarted.RaiseEmpty(); + events.SaveLoaded.RaiseEmpty(); + events.DayStarted.RaiseEmpty(); #if !SMAPI_3_0_STRICT - this.Events.Legacy_AfterLoad.Raise(); - this.Events.Legacy_AfterDayStarted.Raise(); + events.Legacy_AfterLoad.Raise(); + events.Legacy_AfterDayStarted.Raise(); #endif } @@ -548,9 +544,9 @@ namespace StardewModdingAPI.Framework Point oldSize = this.Watchers.WindowSizeWatcher.PreviousValue; Point newSize = this.Watchers.WindowSizeWatcher.CurrentValue; - this.Events.WindowResized.Raise(new WindowResizedEventArgs(oldSize, newSize)); + events.WindowResized.Raise(new WindowResizedEventArgs(oldSize, newSize)); #if !SMAPI_3_0_STRICT - this.Events.Legacy_Resize.Raise(); + events.Legacy_Resize.Raise(); #endif this.Watchers.WindowSizeWatcher.Reset(); } @@ -569,23 +565,33 @@ namespace StardewModdingAPI.Framework // raise cursor moved event if (this.Watchers.CursorWatcher.IsChanged) { - ICursorPosition was = this.Watchers.CursorWatcher.PreviousValue; - ICursorPosition now = this.Watchers.CursorWatcher.CurrentValue; - this.Watchers.CursorWatcher.Reset(); + if (events.CursorMoved.HasListeners()) + { + ICursorPosition was = this.Watchers.CursorWatcher.PreviousValue; + ICursorPosition now = this.Watchers.CursorWatcher.CurrentValue; + this.Watchers.CursorWatcher.Reset(); - this.Events.CursorMoved.Raise(new CursorMovedEventArgs(was, now)); + events.CursorMoved.Raise(new CursorMovedEventArgs(was, now)); + } + else + this.Watchers.CursorWatcher.Reset(); } // raise mouse wheel scrolled if (this.Watchers.MouseWheelScrollWatcher.IsChanged) { - int was = this.Watchers.MouseWheelScrollWatcher.PreviousValue; - int now = this.Watchers.MouseWheelScrollWatcher.CurrentValue; - this.Watchers.MouseWheelScrollWatcher.Reset(); + if (events.MouseWheelScrolled.HasListeners() || this.Monitor.IsVerbose) + { + int was = this.Watchers.MouseWheelScrollWatcher.PreviousValue; + int now = this.Watchers.MouseWheelScrollWatcher.CurrentValue; + this.Watchers.MouseWheelScrollWatcher.Reset(); - if (this.Monitor.IsVerbose) - this.Monitor.Log($"Events: mouse wheel scrolled to {now}.", LogLevel.Trace); - this.Events.MouseWheelScrolled.Raise(new MouseWheelScrolledEventArgs(cursor, was, now)); + if (this.Monitor.IsVerbose) + this.Monitor.Log($"Events: mouse wheel scrolled to {now}.", LogLevel.Trace); + events.MouseWheelScrolled.Raise(new MouseWheelScrolledEventArgs(cursor, was, now)); + } + else + this.Watchers.MouseWheelScrollWatcher.Reset(); } // raise input button events @@ -599,22 +605,22 @@ namespace StardewModdingAPI.Framework if (this.Monitor.IsVerbose) this.Monitor.Log($"Events: button {button} pressed.", LogLevel.Trace); - this.Events.ButtonPressed.Raise(new ButtonPressedEventArgs(button, cursor, inputState)); + events.ButtonPressed.Raise(new ButtonPressedEventArgs(button, cursor, inputState)); #if !SMAPI_3_0_STRICT // legacy events - this.Events.Legacy_ButtonPressed.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons)); + events.Legacy_ButtonPressed.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons)); if (button.TryGetKeyboard(out Keys key)) { if (key != Keys.None) - this.Events.Legacy_KeyPressed.Raise(new EventArgsKeyPressed(key)); + events.Legacy_KeyPressed.Raise(new EventArgsKeyPressed(key)); } else if (button.TryGetController(out Buttons controllerButton)) { if (controllerButton == Buttons.LeftTrigger || controllerButton == Buttons.RightTrigger) - this.Events.Legacy_ControllerTriggerPressed.Raise(new EventArgsControllerTriggerPressed(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.RealController.Triggers.Left : inputState.RealController.Triggers.Right)); + events.Legacy_ControllerTriggerPressed.Raise(new EventArgsControllerTriggerPressed(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.RealController.Triggers.Left : inputState.RealController.Triggers.Right)); else - this.Events.Legacy_ControllerButtonPressed.Raise(new EventArgsControllerButtonPressed(PlayerIndex.One, controllerButton)); + events.Legacy_ControllerButtonPressed.Raise(new EventArgsControllerButtonPressed(PlayerIndex.One, controllerButton)); } #endif } @@ -623,22 +629,22 @@ namespace StardewModdingAPI.Framework if (this.Monitor.IsVerbose) this.Monitor.Log($"Events: button {button} released.", LogLevel.Trace); - this.Events.ButtonReleased.Raise(new ButtonReleasedEventArgs(button, cursor, inputState)); + events.ButtonReleased.Raise(new ButtonReleasedEventArgs(button, cursor, inputState)); #if !SMAPI_3_0_STRICT // legacy events - this.Events.Legacy_ButtonReleased.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons)); + events.Legacy_ButtonReleased.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons)); if (button.TryGetKeyboard(out Keys key)) { if (key != Keys.None) - this.Events.Legacy_KeyReleased.Raise(new EventArgsKeyPressed(key)); + events.Legacy_KeyReleased.Raise(new EventArgsKeyPressed(key)); } else if (button.TryGetController(out Buttons controllerButton)) { if (controllerButton == Buttons.LeftTrigger || controllerButton == Buttons.RightTrigger) - this.Events.Legacy_ControllerTriggerReleased.Raise(new EventArgsControllerTriggerReleased(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.RealController.Triggers.Left : inputState.RealController.Triggers.Right)); + events.Legacy_ControllerTriggerReleased.Raise(new EventArgsControllerTriggerReleased(PlayerIndex.One, controllerButton, controllerButton == Buttons.LeftTrigger ? inputState.RealController.Triggers.Left : inputState.RealController.Triggers.Right)); else - this.Events.Legacy_ControllerButtonReleased.Raise(new EventArgsControllerButtonReleased(PlayerIndex.One, controllerButton)); + events.Legacy_ControllerButtonReleased.Raise(new EventArgsControllerButtonReleased(PlayerIndex.One, controllerButton)); } #endif } @@ -647,9 +653,9 @@ namespace StardewModdingAPI.Framework #if !SMAPI_3_0_STRICT // raise legacy state-changed events if (inputState.RealKeyboard != previousInputState.RealKeyboard) - this.Events.Legacy_KeyboardChanged.Raise(new EventArgsKeyboardStateChanged(previousInputState.RealKeyboard, inputState.RealKeyboard)); + events.Legacy_KeyboardChanged.Raise(new EventArgsKeyboardStateChanged(previousInputState.RealKeyboard, inputState.RealKeyboard)); if (inputState.RealMouse != previousInputState.RealMouse) - this.Events.Legacy_MouseChanged.Raise(new EventArgsMouseStateChanged(previousInputState.RealMouse, inputState.RealMouse, new Point((int)previousInputState.CursorPosition.ScreenPixels.X, (int)previousInputState.CursorPosition.ScreenPixels.Y), new Point((int)inputState.CursorPosition.ScreenPixels.X, (int)inputState.CursorPosition.ScreenPixels.Y))); + events.Legacy_MouseChanged.Raise(new EventArgsMouseStateChanged(previousInputState.RealMouse, inputState.RealMouse, new Point((int)previousInputState.CursorPosition.ScreenPixels.X, (int)previousInputState.CursorPosition.ScreenPixels.Y), new Point((int)inputState.CursorPosition.ScreenPixels.X, (int)inputState.CursorPosition.ScreenPixels.Y))); #endif } } @@ -667,12 +673,12 @@ namespace StardewModdingAPI.Framework this.Monitor.Log($"Context: menu changed from {was?.GetType().FullName ?? "none"} to {now?.GetType().FullName ?? "none"}.", LogLevel.Trace); // raise menu events - this.Events.MenuChanged.Raise(new MenuChangedEventArgs(was, now)); + events.MenuChanged.Raise(new MenuChangedEventArgs(was, now)); #if !SMAPI_3_0_STRICT if (now != null) - this.Events.Legacy_MenuChanged.Raise(new EventArgsClickableMenuChanged(was, now)); + events.Legacy_MenuChanged.Raise(new EventArgsClickableMenuChanged(was, now)); else - this.Events.Legacy_MenuClosed.Raise(new EventArgsClickableMenuClosed(was)); + events.Legacy_MenuClosed.Raise(new EventArgsClickableMenuClosed(was)); #endif } @@ -700,9 +706,9 @@ namespace StardewModdingAPI.Framework this.Monitor.Log($"Context: location list changed (added {addedText}; removed {removedText}).", LogLevel.Trace); } - this.Events.LocationListChanged.Raise(new LocationListChangedEventArgs(added, removed)); + events.LocationListChanged.Raise(new LocationListChangedEventArgs(added, removed)); #if !SMAPI_3_0_STRICT - this.Events.Legacy_LocationsChanged.Raise(new EventArgsLocationsChanged(added, removed)); + events.Legacy_LocationsChanged.Raise(new EventArgsLocationsChanged(added, removed)); #endif } @@ -719,9 +725,9 @@ namespace StardewModdingAPI.Framework Building[] removed = watcher.BuildingsWatcher.Removed.ToArray(); watcher.BuildingsWatcher.Reset(); - this.Events.BuildingListChanged.Raise(new BuildingListChangedEventArgs(location, added, removed)); + events.BuildingListChanged.Raise(new BuildingListChangedEventArgs(location, added, removed)); #if !SMAPI_3_0_STRICT - this.Events.Legacy_BuildingsChanged.Raise(new EventArgsLocationBuildingsChanged(location, added, removed)); + events.Legacy_BuildingsChanged.Raise(new EventArgsLocationBuildingsChanged(location, added, removed)); #endif } @@ -733,7 +739,7 @@ namespace StardewModdingAPI.Framework Debris[] removed = watcher.DebrisWatcher.Removed.ToArray(); watcher.DebrisWatcher.Reset(); - this.Events.DebrisListChanged.Raise(new DebrisListChangedEventArgs(location, added, removed)); + events.DebrisListChanged.Raise(new DebrisListChangedEventArgs(location, added, removed)); } // large terrain features changed @@ -744,7 +750,7 @@ namespace StardewModdingAPI.Framework LargeTerrainFeature[] removed = watcher.LargeTerrainFeaturesWatcher.Removed.ToArray(); watcher.LargeTerrainFeaturesWatcher.Reset(); - this.Events.LargeTerrainFeatureListChanged.Raise(new LargeTerrainFeatureListChangedEventArgs(location, added, removed)); + events.LargeTerrainFeatureListChanged.Raise(new LargeTerrainFeatureListChangedEventArgs(location, added, removed)); } // NPCs changed @@ -755,7 +761,7 @@ namespace StardewModdingAPI.Framework NPC[] removed = watcher.NpcsWatcher.Removed.ToArray(); watcher.NpcsWatcher.Reset(); - this.Events.NpcListChanged.Raise(new NpcListChangedEventArgs(location, added, removed)); + events.NpcListChanged.Raise(new NpcListChangedEventArgs(location, added, removed)); } // objects changed @@ -766,9 +772,9 @@ namespace StardewModdingAPI.Framework KeyValuePair[] removed = watcher.ObjectsWatcher.Removed.ToArray(); watcher.ObjectsWatcher.Reset(); - this.Events.ObjectListChanged.Raise(new ObjectListChangedEventArgs(location, added, removed)); + events.ObjectListChanged.Raise(new ObjectListChangedEventArgs(location, added, removed)); #if !SMAPI_3_0_STRICT - this.Events.Legacy_ObjectsChanged.Raise(new EventArgsLocationObjectsChanged(location, added, removed)); + events.Legacy_ObjectsChanged.Raise(new EventArgsLocationObjectsChanged(location, added, removed)); #endif } @@ -780,7 +786,7 @@ namespace StardewModdingAPI.Framework KeyValuePair[] removed = watcher.TerrainFeaturesWatcher.Removed.ToArray(); watcher.TerrainFeaturesWatcher.Reset(); - this.Events.TerrainFeatureListChanged.Raise(new TerrainFeatureListChangedEventArgs(location, added, removed)); + events.TerrainFeatureListChanged.Raise(new TerrainFeatureListChangedEventArgs(location, added, removed)); } } } @@ -798,9 +804,9 @@ namespace StardewModdingAPI.Framework if (this.Monitor.IsVerbose) this.Monitor.Log($"Events: time changed from {was} to {now}.", LogLevel.Trace); - this.Events.TimeChanged.Raise(new TimeChangedEventArgs(was, now)); + events.TimeChanged.Raise(new TimeChangedEventArgs(was, now)); #if !SMAPI_3_0_STRICT - this.Events.Legacy_TimeOfDayChanged.Raise(new EventArgsIntChanged(was, now)); + events.Legacy_TimeOfDayChanged.Raise(new EventArgsIntChanged(was, now)); #endif } else @@ -818,9 +824,9 @@ namespace StardewModdingAPI.Framework this.Monitor.Log($"Context: set location to {newLocation.Name}.", LogLevel.Trace); GameLocation oldLocation = playerTracker.LocationWatcher.PreviousValue; - this.Events.Warped.Raise(new WarpedEventArgs(playerTracker.Player, oldLocation, newLocation)); + events.Warped.Raise(new WarpedEventArgs(playerTracker.Player, oldLocation, newLocation)); #if !SMAPI_3_0_STRICT - this.Events.Legacy_PlayerWarped.Raise(new EventArgsPlayerWarped(oldLocation, newLocation)); + events.Legacy_PlayerWarped.Raise(new EventArgsPlayerWarped(oldLocation, newLocation)); #endif } @@ -830,9 +836,9 @@ namespace StardewModdingAPI.Framework if (this.Monitor.IsVerbose) this.Monitor.Log($"Events: player skill '{pair.Key}' changed from {pair.Value.PreviousValue} to {pair.Value.CurrentValue}.", LogLevel.Trace); - this.Events.LevelChanged.Raise(new LevelChangedEventArgs(playerTracker.Player, pair.Key, pair.Value.PreviousValue, pair.Value.CurrentValue)); + events.LevelChanged.Raise(new LevelChangedEventArgs(playerTracker.Player, pair.Key, pair.Value.PreviousValue, pair.Value.CurrentValue)); #if !SMAPI_3_0_STRICT - this.Events.Legacy_LeveledUp.Raise(new EventArgsLevelUp((EventArgsLevelUp.LevelType)pair.Key, pair.Value.CurrentValue)); + events.Legacy_LeveledUp.Raise(new EventArgsLevelUp((EventArgsLevelUp.LevelType)pair.Key, pair.Value.CurrentValue)); #endif } @@ -842,9 +848,9 @@ namespace StardewModdingAPI.Framework { if (this.Monitor.IsVerbose) this.Monitor.Log("Events: player inventory changed.", LogLevel.Trace); - this.Events.InventoryChanged.Raise(new InventoryChangedEventArgs(playerTracker.Player, changedItems)); + events.InventoryChanged.Raise(new InventoryChangedEventArgs(playerTracker.Player, changedItems)); #if !SMAPI_3_0_STRICT - this.Events.Legacy_InventoryChanged.Raise(new EventArgsInventoryChanged(Game1.player.Items, changedItems)); + events.Legacy_InventoryChanged.Raise(new EventArgsInventoryChanged(Game1.player.Items, changedItems)); #endif } @@ -854,7 +860,7 @@ namespace StardewModdingAPI.Framework if (this.Monitor.IsVerbose) this.Monitor.Log($"Context: mine level changed to {mineLevel}.", LogLevel.Trace); #if !SMAPI_3_0_STRICT - this.Events.Legacy_MineLevelChanged.Raise(new EventArgsMineLevelChanged(playerTracker.MineLevelWatcher.PreviousValue, mineLevel)); + events.Legacy_MineLevelChanged.Raise(new EventArgsMineLevelChanged(playerTracker.MineLevelWatcher.PreviousValue, mineLevel)); #endif } } @@ -867,22 +873,21 @@ namespace StardewModdingAPI.Framework /********* ** Game update *********/ - this.TicksElapsed++; - // game launched - if (this.TicksElapsed == 1) - this.Events.GameLaunched.Raise(new GameLaunchedEventArgs()); + bool isFirstTick = Game1.ticks == 0; + if (isFirstTick) + events.GameLaunched.Raise(new GameLaunchedEventArgs()); // preloaded if (Context.IsSaveLoaded && this.LoadStage != LoadStage.Loaded && this.LoadStage != LoadStage.Ready) this.OnLoadStageChanged(LoadStage.Loaded); // update tick - bool isOneSecond = this.TicksElapsed % 60 == 0; - this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed)); - this.Events.UpdateTicking.Raise(new UpdateTickingEventArgs(this.TicksElapsed)); + bool isOneSecond = Game1.ticks % 60 == 0; + events.UnvalidatedUpdateTicking.RaiseEmpty(); + events.UpdateTicking.RaiseEmpty(); if (isOneSecond) - this.Events.OneSecondUpdateTicking.Raise(new OneSecondUpdateTickingEventArgs(this.TicksElapsed)); + events.OneSecondUpdateTicking.RaiseEmpty(); try { this.Input.UpdateSuppression(); @@ -892,35 +897,32 @@ namespace StardewModdingAPI.Framework { this.MonitorForGame.Log($"An error occured in the base update loop: {ex.GetLogSummary()}", LogLevel.Error); } - this.Events.UnvalidatedUpdateTicked.Raise(new UnvalidatedUpdateTickedEventArgs(this.TicksElapsed)); - this.Events.UpdateTicked.Raise(new UpdateTickedEventArgs(this.TicksElapsed)); + events.UnvalidatedUpdateTicked.RaiseEmpty(); + events.UpdateTicked.RaiseEmpty(); if (isOneSecond) - this.Events.OneSecondUpdateTicked.Raise(new OneSecondUpdateTickedEventArgs(this.TicksElapsed)); + events.OneSecondUpdateTicked.RaiseEmpty(); /********* ** Update events *********/ #if !SMAPI_3_0_STRICT - this.Events.Legacy_UnvalidatedUpdateTick.Raise(); - if (this.TicksElapsed == 1) - this.Events.Legacy_FirstUpdateTick.Raise(); - this.Events.Legacy_UpdateTick.Raise(); - if (this.CurrentUpdateTick % 2 == 0) - this.Events.Legacy_SecondUpdateTick.Raise(); - if (this.CurrentUpdateTick % 4 == 0) - this.Events.Legacy_FourthUpdateTick.Raise(); - if (this.CurrentUpdateTick % 8 == 0) - this.Events.Legacy_EighthUpdateTick.Raise(); - if (this.CurrentUpdateTick % 15 == 0) - this.Events.Legacy_QuarterSecondTick.Raise(); - if (this.CurrentUpdateTick % 30 == 0) - this.Events.Legacy_HalfSecondTick.Raise(); - if (this.CurrentUpdateTick % 60 == 0) - this.Events.Legacy_OneSecondTick.Raise(); + events.Legacy_UnvalidatedUpdateTick.Raise(); + if (isFirstTick) + events.Legacy_FirstUpdateTick.Raise(); + events.Legacy_UpdateTick.Raise(); + if (Game1.ticks % 2 == 0) + events.Legacy_SecondUpdateTick.Raise(); + if (Game1.ticks % 4 == 0) + events.Legacy_FourthUpdateTick.Raise(); + if (Game1.ticks % 8 == 0) + events.Legacy_EighthUpdateTick.Raise(); + if (Game1.ticks % 15 == 0) + events.Legacy_QuarterSecondTick.Raise(); + if (Game1.ticks % 30 == 0) + events.Legacy_HalfSecondTick.Raise(); + if (Game1.ticks % 60 == 0) + events.Legacy_OneSecondTick.Raise(); #endif - this.CurrentUpdateTick += 1; - if (this.CurrentUpdateTick >= 60) - this.CurrentUpdateTick = 0; this.UpdateCrashTimer.Reset(); } @@ -988,10 +990,10 @@ namespace StardewModdingAPI.Framework [SuppressMessage("SMAPI.CommonErrors", "AvoidImplicitNetFieldCast", Justification = "copied from game code as-is")] private void DrawImpl(GameTime gameTime) { + var events = this.Events; + if (Game1._newDayTask != null) - { this.GraphicsDevice.Clear(this.bgColor); - } else { if ((double)Game1.options.zoomLevel != 1.0) @@ -1003,17 +1005,17 @@ namespace StardewModdingAPI.Framework if (activeClickableMenu != null) { Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, (DepthStencilState)null, (RasterizerState)null); - this.Events.Rendering.RaiseEmpty(); + events.Rendering.RaiseEmpty(); try { - this.Events.RenderingActiveMenu.RaiseEmpty(); + events.RenderingActiveMenu.RaiseEmpty(); #if !SMAPI_3_0_STRICT - this.Events.Legacy_OnPreRenderGuiEvent.Raise(); + events.Legacy_OnPreRenderGuiEvent.Raise(); #endif activeClickableMenu.draw(Game1.spriteBatch); - this.Events.RenderedActiveMenu.RaiseEmpty(); + events.RenderedActiveMenu.RaiseEmpty(); #if !SMAPI_3_0_STRICT - this.Events.Legacy_OnPostRenderGuiEvent.Raise(); + events.Legacy_OnPostRenderGuiEvent.Raise(); #endif } catch (Exception ex) @@ -1021,9 +1023,9 @@ namespace StardewModdingAPI.Framework this.Monitor.Log($"The {activeClickableMenu.GetType().FullName} menu crashed while drawing itself during save. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); activeClickableMenu.exitThisMenu(); } - this.Events.Rendered.RaiseEmpty(); + events.Rendered.RaiseEmpty(); #if !SMAPI_3_0_STRICT - this.Events.Legacy_OnPostRenderEvent.Raise(); + events.Legacy_OnPostRenderEvent.Raise(); #endif Game1.spriteBatch.End(); @@ -1043,18 +1045,18 @@ namespace StardewModdingAPI.Framework { Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, (DepthStencilState)null, (RasterizerState)null); - this.Events.Rendering.RaiseEmpty(); +