From 4da9e954df3846d01aa0536f4e8143466a1d62f3 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 11 Feb 2022 00:49:49 -0500 Subject: use Array.Empty to avoid unneeded array allocations --- src/SMAPI/Framework/Events/ManagedEvent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/SMAPI/Framework/Events') diff --git a/src/SMAPI/Framework/Events/ManagedEvent.cs b/src/SMAPI/Framework/Events/ManagedEvent.cs index fa20a079..f48c3aeb 100644 --- a/src/SMAPI/Framework/Events/ManagedEvent.cs +++ b/src/SMAPI/Framework/Events/ManagedEvent.cs @@ -21,7 +21,7 @@ namespace StardewModdingAPI.Framework.Events private readonly List> Handlers = new List>(); /// A cached snapshot of , or null to rebuild it next raise. - private ManagedEventHandler[] CachedHandlers = new ManagedEventHandler[0]; + private ManagedEventHandler[] CachedHandlers = Array.Empty>(); /// The total number of event handlers registered for this events, regardless of whether they're still registered. private int RegistrationIndex; -- cgit From d3fbdf484a4d90365a55fb5058d75a8623f17d0f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 22 Mar 2022 20:46:21 -0400 Subject: reduce duplicated doc blocks --- src/SMAPI/Framework/Events/EventManager.cs | 105 ++++++++++----------- src/SMAPI/Framework/Events/ManagedEvent.cs | 4 +- src/SMAPI/Framework/Events/ManagedEventHandler.cs | 4 +- src/SMAPI/Framework/Events/ModDisplayEvents.cs | 23 +++-- src/SMAPI/Framework/Events/ModEvents.cs | 16 ++-- src/SMAPI/Framework/Events/ModGameLoopEvents.cs | 30 +++--- src/SMAPI/Framework/Events/ModInputEvents.cs | 12 +-- src/SMAPI/Framework/Events/ModMultiplayerEvents.cs | 10 +- src/SMAPI/Framework/Events/ModPlayerEvents.cs | 8 +- src/SMAPI/Framework/Events/ModSpecialisedEvents.cs | 8 +- src/SMAPI/Framework/Events/ModWorldEvents.cs | 20 ++-- src/SMAPI/Framework/Input/GamePadStateBuilder.cs | 7 +- src/SMAPI/Framework/Input/KeyboardStateBuilder.cs | 7 +- src/SMAPI/Framework/Input/MouseStateBuilder.cs | 7 +- src/SMAPI/Mod.cs | 16 ++-- 15 files changed, 132 insertions(+), 145 deletions(-) (limited to 'src/SMAPI/Framework/Events') diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index dfc289ed..fa4d564d 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Reflection; using StardewModdingAPI.Events; namespace StardewModdingAPI.Framework.Events @@ -15,166 +13,166 @@ namespace StardewModdingAPI.Framework.Events /**** ** Display ****/ - /// Raised after a game menu is opened, closed, or replaced. + /// public readonly ManagedEvent MenuChanged; - /// Raised before the game draws anything to the screen in a draw tick, as soon as the sprite batch is opened. The sprite batch may be closed and reopened multiple times after this event is called, but it's only raised once per draw tick. This event isn't useful for drawing to the screen, since the game will draw over it. + /// public readonly ManagedEvent Rendering; - /// Raised after the game draws to the sprite patch in a draw tick, just before the final sprite batch is rendered to the screen. Since the game may open/close the sprite batch multiple times in a draw tick, the sprite batch may not contain everything being drawn and some things may already be rendered to the screen. Content drawn to the sprite batch at this point will be drawn over all vanilla content (including menus, HUD, and cursor). + /// public readonly ManagedEvent Rendered; - /// Raised before the game world is drawn to the screen. + /// public readonly ManagedEvent RenderingWorld; - /// Raised after the game world is drawn to the sprite patch, before it's rendered to the screen. + /// public readonly ManagedEvent RenderedWorld; - /// When a menu is open ( isn't null), raised before that menu is drawn to the screen. + /// public readonly ManagedEvent RenderingActiveMenu; - /// When a menu is open ( isn't null), raised after that menu is drawn to the sprite batch but before it's rendered to the screen. + /// public readonly ManagedEvent RenderedActiveMenu; - /// Raised before drawing the HUD (item toolbar, clock, etc) to the screen. + /// public readonly ManagedEvent RenderingHud; - /// Raised after drawing the HUD (item toolbar, clock, etc) to the sprite batch, but before it's rendered to the screen. + /// public readonly ManagedEvent RenderedHud; - /// Raised after the game window is resized. + /// public readonly ManagedEvent WindowResized; /**** ** Game loop ****/ - /// Raised after the game is launched, right before the first update tick. + /// public readonly ManagedEvent GameLaunched; - /// Raised before the game performs its overall update tick (≈60 times per second). + /// public readonly ManagedEvent UpdateTicking; - /// 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; - /// Raised after the game finishes creating the save file. + /// public readonly ManagedEvent SaveCreated; - /// Raised before the game begins writes data to the save file (except the initial save creation). + /// public readonly ManagedEvent Saving; - /// 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 and the world is initialized. + /// public readonly ManagedEvent SaveLoaded; - /// Raised after the game begins a new day, including when loading a save. + /// public readonly ManagedEvent DayStarted; - /// Raised before the game ends the current day. This happens before it starts setting up the next day and before . + /// public readonly ManagedEvent DayEnding; - /// Raised after the in-game clock time changes. + /// public readonly ManagedEvent TimeChanged; - /// Raised after the game returns to the title screen. + /// public readonly ManagedEvent ReturnedToTitle; /**** ** Input ****/ - /// Raised after the player presses or releases any buttons on the keyboard, controller, or mouse. + /// public readonly ManagedEvent ButtonsChanged; - /// Raised after the player presses a button on the keyboard, controller, or mouse. + /// public readonly ManagedEvent ButtonPressed; - /// Raised after the player released a button on the keyboard, controller, or mouse. + /// public readonly ManagedEvent ButtonReleased; - /// Raised after the player moves the in-game cursor. + /// public readonly ManagedEvent CursorMoved; - /// Raised after the player scrolls the mouse wheel. + /// public readonly ManagedEvent MouseWheelScrolled; /**** ** Multiplayer ****/ - /// Raised after the mod context for a peer is received. This happens before the game approves the connection (), so the player doesn't yet exist in the game. This is the earliest point where messages can be sent to the peer via SMAPI. + /// public readonly ManagedEvent PeerContextReceived; - /// Raised after a peer connection is approved by the game. + /// public readonly ManagedEvent PeerConnected; - /// Raised after a mod message is received over the network. + /// public readonly ManagedEvent ModMessageReceived; - /// Raised after the connection with a peer is severed. + /// public readonly ManagedEvent PeerDisconnected; /**** ** Player ****/ - /// Raised after items are added or removed to a player's inventory. + /// public readonly ManagedEvent InventoryChanged; - /// Raised after a player skill level changes. This happens as soon as they level up, not when the game notifies the player after their character goes to bed. + /// public readonly ManagedEvent LevelChanged; - /// Raised after a player warps to a new location. + /// public readonly ManagedEvent Warped; /**** ** World ****/ - /// Raised after a game location is added or removed. + /// public readonly ManagedEvent LocationListChanged; - /// Raised after buildings are added or removed in a location. + /// public readonly ManagedEvent BuildingListChanged; - /// Raised after debris are added or removed in a location. + /// public readonly ManagedEvent DebrisListChanged; - /// Raised after large terrain features (like bushes) are added or removed in a location. + /// public readonly ManagedEvent LargeTerrainFeatureListChanged; - /// Raised after NPCs are added or removed in a location. + /// public readonly ManagedEvent NpcListChanged; - /// Raised after objects are added or removed in a location. + /// public readonly ManagedEvent ObjectListChanged; - /// Raised after items are added or removed from a chest. + /// public readonly ManagedEvent ChestInventoryChanged; - /// Raised after terrain features (like floors and trees) are added or removed in a location. + /// public readonly ManagedEvent TerrainFeatureListChanged; - /// Raised after furniture are added or removed in a location. + /// public readonly ManagedEvent FurnitureListChanged; /**** ** Specialized ****/ - /// 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; - /// Raised after the game performs its overall update tick (≈60 times per second). See notes on . + /// public readonly ManagedEvent UnvalidatedUpdateTicked; @@ -247,12 +245,5 @@ namespace StardewModdingAPI.Framework.Events this.UnvalidatedUpdateTicking = ManageEventOf(nameof(IModEvents.Specialized), nameof(ISpecializedEvents.UnvalidatedUpdateTicking), isPerformanceCritical: true); this.UnvalidatedUpdateTicked = ManageEventOf(nameof(IModEvents.Specialized), nameof(ISpecializedEvents.UnvalidatedUpdateTicked), isPerformanceCritical: true); } - - /// Get all managed events. - public IEnumerable GetAllEvents() - { - foreach (FieldInfo field in this.GetType().GetFields()) - yield return (IManagedEvent)field.GetValue(this); - } } } diff --git a/src/SMAPI/Framework/Events/ManagedEvent.cs b/src/SMAPI/Framework/Events/ManagedEvent.cs index f48c3aeb..a200393d 100644 --- a/src/SMAPI/Framework/Events/ManagedEvent.cs +++ b/src/SMAPI/Framework/Events/ManagedEvent.cs @@ -33,10 +33,10 @@ namespace StardewModdingAPI.Framework.Events /********* ** Accessors *********/ - /// A human-readable name for the event. + /// public string EventName { get; } - /// Whether the event is typically called at least once per second. + /// public bool IsPerformanceCritical { get; } diff --git a/src/SMAPI/Framework/Events/ManagedEventHandler.cs b/src/SMAPI/Framework/Events/ManagedEventHandler.cs index b32a2a22..28e88be0 100644 --- a/src/SMAPI/Framework/Events/ManagedEventHandler.cs +++ b/src/SMAPI/Framework/Events/ManagedEventHandler.cs @@ -39,9 +39,7 @@ namespace StardewModdingAPI.Framework.Events this.SourceMod = sourceMod; } - /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. - /// An object to compare with this instance. - /// is not the same type as this instance. + /// public int CompareTo(object obj) { if (!(obj is ManagedEventHandler other)) diff --git a/src/SMAPI/Framework/Events/ModDisplayEvents.cs b/src/SMAPI/Framework/Events/ModDisplayEvents.cs index 54d40dee..48f55324 100644 --- a/src/SMAPI/Framework/Events/ModDisplayEvents.cs +++ b/src/SMAPI/Framework/Events/ModDisplayEvents.cs @@ -1,79 +1,78 @@ using System; using StardewModdingAPI.Events; -using StardewValley; namespace StardewModdingAPI.Framework.Events { - /// Events related to UI and drawing to the screen. + /// internal class ModDisplayEvents : ModEventsBase, IDisplayEvents { /********* ** Accessors *********/ - /// Raised after a game menu is opened, closed, or replaced. + /// public event EventHandler MenuChanged { add => this.EventManager.MenuChanged.Add(value, this.Mod); remove => this.EventManager.MenuChanged.Remove(value); } - /// Raised before the game draws anything to the screen in a draw tick, as soon as the sprite batch is opened. The sprite batch may be closed and reopened multiple times after this event is called, but it's only raised once per draw tick. This event isn't useful for drawing to the screen, since the game will draw over it. + /// public event EventHandler Rendering { add => this.EventManager.Rendering.Add(value, this.Mod); remove => this.EventManager.Rendering.Remove(value); } - /// Raised after the game draws to the sprite patch in a draw tick, just before the final sprite batch is rendered to the screen. Since the game may open/close the sprite batch multiple times in a draw tick, the sprite batch may not contain everything being drawn and some things may already be rendered to the screen. Content drawn to the sprite batch at this point will be drawn over all vanilla content (including menus, HUD, and cursor). + /// public event EventHandler Rendered { add => this.EventManager.Rendered.Add(value, this.Mod); remove => this.EventManager.Rendered.Remove(value); } - /// Raised before the game world is drawn to the screen. This event isn't useful for drawing to the screen, since the game will draw over it. + /// public event EventHandler RenderingWorld { add => this.EventManager.RenderingWorld.Add(value, this.Mod); remove => this.EventManager.RenderingWorld.Remove(value); } - /// Raised after the game world is drawn to the sprite patch, before it's rendered to the screen. Content drawn to the sprite batch at this point will be drawn over the world, but under any active menu, HUD elements, or cursor. + /// public event EventHandler RenderedWorld { add => this.EventManager.RenderedWorld.Add(value, this.Mod); remove => this.EventManager.RenderedWorld.Remove(value); } - /// When a menu is open ( isn't null), raised before that menu is drawn to the screen. This includes the game's internal menus like the title screen. Content drawn to the sprite batch at this point will appear under the menu. + /// public event EventHandler RenderingActiveMenu { add => this.EventManager.RenderingActiveMenu.Add(value, this.Mod); remove => this.EventManager.RenderingActiveMenu.Remove(value); } - /// When a menu is open ( isn't null), raised after that menu is drawn to the sprite batch but before it's rendered to the screen. Content drawn to the sprite batch at this point will appear over the menu and menu cursor. + /// public event EventHandler RenderedActiveMenu { add => this.EventManager.RenderedActiveMenu.Add(value, this.Mod); remove => this.EventManager.RenderedActiveMenu.Remove(value); } - /// Raised before drawing the HUD (item toolbar, clock, etc) to the screen. The vanilla HUD may be hidden at this point (e.g. because a menu is open). Content drawn to the sprite batch at this point will appear under the HUD. + /// public event EventHandler RenderingHud { add => this.EventManager.RenderingHud.Add(value, this.Mod); remove => this.EventManager.RenderingHud.Remove(value); } - /// Raised after drawing the HUD (item toolbar, clock, etc) to the sprite batch, but before it's rendered to the screen. The vanilla HUD may be hidden at this point (e.g. because a menu is open). Content drawn to the sprite batch at this point will appear over the HUD. + /// public event EventHandler RenderedHud { add => this.EventManager.RenderedHud.Add(value, this.Mod); remove => this.EventManager.RenderedHud.Remove(value); } - /// Raised after the game window is resized. + /// public event EventHandler WindowResized { add => this.EventManager.WindowResized.Add(value, this.Mod); diff --git a/src/SMAPI/Framework/Events/ModEvents.cs b/src/SMAPI/Framework/Events/ModEvents.cs index 1d1c92c6..0c365d42 100644 --- a/src/SMAPI/Framework/Events/ModEvents.cs +++ b/src/SMAPI/Framework/Events/ModEvents.cs @@ -2,31 +2,31 @@ using StardewModdingAPI.Events; namespace StardewModdingAPI.Framework.Events { - /// Manages access to events raised by SMAPI. + /// internal class ModEvents : IModEvents { /********* ** Accessors *********/ - /// Events related to UI and drawing to the screen. + /// public IDisplayEvents Display { get; } - /// Events linked to the game's update loop. The update loop runs roughly ≈60 times/second to run game logic like state changes, action handling, etc. These can be useful, but you should consider more semantic events like if possible. + /// public IGameLoopEvents GameLoop { get; } - /// Events raised when the player provides input using a controller, keyboard, or mouse. + /// public IInputEvents Input { get; } - /// Events raised for multiplayer messages and connections. + /// public IMultiplayerEvents Multiplayer { get; } - /// Events raised when the player data changes. + /// public IPlayerEvents Player { get; } - /// Events raised when something changes in the world. + /// public IWorldEvents World { get; } - /// Events serving specialized edge cases that shouldn't be used by most mods. + /// public ISpecializedEvents Specialized { get; } diff --git a/src/SMAPI/Framework/Events/ModGameLoopEvents.cs b/src/SMAPI/Framework/Events/ModGameLoopEvents.cs index 1150d641..5f0db369 100644 --- a/src/SMAPI/Framework/Events/ModGameLoopEvents.cs +++ b/src/SMAPI/Framework/Events/ModGameLoopEvents.cs @@ -3,104 +3,104 @@ using StardewModdingAPI.Events; namespace StardewModdingAPI.Framework.Events { - /// Events linked to the game's update loop. The update loop runs roughly ≈60 times/second to run game logic like state changes, action handling, etc. These can be useful, but you should consider more semantic events like if possible. + /// internal class ModGameLoopEvents : ModEventsBase, IGameLoopEvents { /********* ** Accessors *********/ - /// Raised after the game is launched, right before the first update tick. + /// public event EventHandler GameLaunched { add => this.EventManager.GameLaunched.Add(value, this.Mod); remove => this.EventManager.GameLaunched.Remove(value); } - /// Raised before the game performs its overall update tick (≈60 times per second). + /// public event EventHandler UpdateTicking { add => this.EventManager.UpdateTicking.Add(value, this.Mod); remove => this.EventManager.UpdateTicking.Remove(value); } - /// Raised after the game performs its overall update tick (≈60 times per second). + /// public event EventHandler UpdateTicked { add => this.EventManager.UpdateTicked.Add(value, this.Mod); 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, this.Mod); 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, this.Mod); remove => this.EventManager.OneSecondUpdateTicked.Remove(value); } - /// Raised before the game creates a new save file. + /// public event EventHandler SaveCreating { add => this.EventManager.SaveCreating.Add(value, this.Mod); remove => this.EventManager.SaveCreating.Remove(value); } - /// Raised after the game finishes creating the save file. + /// public event EventHandler SaveCreated { add => this.EventManager.SaveCreated.Add(value, this.Mod); remove => this.EventManager.SaveCreated.Remove(value); } - /// Raised before the game begins writes data to the save file. + /// public event EventHandler Saving { add => this.EventManager.Saving.Add(value, this.Mod); remove => this.EventManager.Saving.Remove(value); } - /// Raised after the game finishes writing data to the save file. + /// public event EventHandler Saved { add => this.EventManager.Saved.Add(value, this.Mod); remove => this.EventManager.Saved.Remove(value); } - /// Raised after the player loads a save slot and the world is initialized. + /// public event EventHandler SaveLoaded { add => this.EventManager.SaveLoaded.Add(value, this.Mod); remove => this.EventManager.SaveLoaded.Remove(value); } - /// Raised after the game begins a new day (including when the player loads a save). + /// public event EventHandler DayStarted { add => this.EventManager.DayStarted.Add(value, this.Mod); remove => this.EventManager.DayStarted.Remove(value); } - /// Raised before the game ends the current day. This happens before it starts setting up the next day and before . + /// public event EventHandler DayEnding { add => this.EventManager.DayEnding.Add(value, this.Mod); remove => this.EventManager.DayEnding.Remove(value); } - /// Raised after the in-game clock time changes. + /// public event EventHandler TimeChanged { add => this.EventManager.TimeChanged.Add(value, this.Mod); remove => this.EventManager.TimeChanged.Remove(value); } - /// Raised after the game returns to the title screen. + /// public event EventHandler ReturnedToTitle { add => this.EventManager.ReturnedToTitle.Add(value, this.Mod); diff --git a/src/SMAPI/Framework/Events/ModInputEvents.cs b/src/SMAPI/Framework/Events/ModInputEvents.cs index 6f423e5d..40edf806 100644 --- a/src/SMAPI/Framework/Events/ModInputEvents.cs +++ b/src/SMAPI/Framework/Events/ModInputEvents.cs @@ -3,41 +3,41 @@ using StardewModdingAPI.Events; namespace StardewModdingAPI.Framework.Events { - /// Events raised when the player provides input using a controller, keyboard, or mouse. + /// internal class ModInputEvents : ModEventsBase, IInputEvents { /********* ** Accessors *********/ - /// Raised after the player presses or releases any buttons on the keyboard, controller, or mouse. + /// public event EventHandler ButtonsChanged { add => this.EventManager.ButtonsChanged.Add(value, this.Mod); remove => this.EventManager.ButtonsChanged.Remove(value); } - /// Raised after the player presses a button on the keyboard, controller, or mouse. + /// public event EventHandler ButtonPressed { add => this.EventManager.ButtonPressed.Add(value, this.Mod); remove => this.EventManager.ButtonPressed.Remove(value); } - /// Raised after the player releases a button on the keyboard, controller, or mouse. + /// public event EventHandler ButtonReleased { add => this.EventManager.ButtonReleased.Add(value, this.Mod); remove => this.EventManager.ButtonReleased.Remove(value); } - /// Raised after the player moves the in-game cursor. + /// public event EventHandler CursorMoved { add => this.EventManager.CursorMoved.Add(value, this.Mod); remove => this.EventManager.CursorMoved.Remove(value); } - /// Raised after the player scrolls the mouse wheel. + /// public event EventHandler MouseWheelScrolled { add => this.EventManager.MouseWheelScrolled.Add(value, this.Mod); diff --git a/src/SMAPI/Framework/Events/ModMultiplayerEvents.cs b/src/SMAPI/Framework/Events/ModMultiplayerEvents.cs index 2f9b9482..b90f64fa 100644 --- a/src/SMAPI/Framework/Events/ModMultiplayerEvents.cs +++ b/src/SMAPI/Framework/Events/ModMultiplayerEvents.cs @@ -3,34 +3,34 @@ using StardewModdingAPI.Events; namespace StardewModdingAPI.Framework.Events { - /// Events raised for multiplayer messages and connections. + /// internal class ModMultiplayerEvents : ModEventsBase, IMultiplayerEvents { /********* ** Accessors *********/ - /// Raised after the mod context for a peer is received. This happens before the game approves the connection (), so the player doesn't yet exist in the game. This is the earliest point where messages can be sent to the peer via SMAPI. + /// public event EventHandler PeerContextReceived { add => this.EventManager.PeerContextReceived.Add(value, this.Mod); remove => this.EventManager.PeerContextReceived.Remove(value); } - /// Raised after a peer connection is approved by the game. + /// public event EventHandler PeerConnected { add => this.EventManager.PeerConnected.Add(value, this.Mod); remove => this.EventManager.PeerConnected.Remove(value); } - /// Raised after a mod message is received over the network. + /// public event EventHandler ModMessageReceived { add => this.EventManager.ModMessageReceived.Add(value, this.Mod); remove => this.EventManager.ModMessageReceived.Remove(value); } - /// Raised after the connection with a peer is severed. + /// public event EventHandler PeerDisconnected { add => this.EventManager.PeerDisconnected.Add(value, this.Mod); diff --git a/src/SMAPI/Framework/Events/ModPlayerEvents.cs b/src/SMAPI/Framework/Events/ModPlayerEvents.cs index 240beb8d..b2d89e9a 100644 --- a/src/SMAPI/Framework/Events/ModPlayerEvents.cs +++ b/src/SMAPI/Framework/Events/ModPlayerEvents.cs @@ -3,27 +3,27 @@ using StardewModdingAPI.Events; namespace StardewModdingAPI.Framework.Events { - /// Events raised when the player data changes. + /// internal class ModPlayerEvents : ModEventsBase, IPlayerEvents { /********* ** Accessors *********/ - /// Raised after items are added or removed to a player's inventory. NOTE: this event is currently only raised for the local player. + /// public event EventHandler InventoryChanged { add => this.EventManager.InventoryChanged.Add(value, this.Mod); remove => this.EventManager.InventoryChanged.Remove(value); } - /// Raised after a player skill level changes. This happens as soon as they level up, not when the game notifies the player after their character goes to bed. NOTE: this event is currently only raised for the local player. + /// public event EventHandler LevelChanged { add => this.EventManager.LevelChanged.Add(value, this.Mod); remove => this.EventManager.LevelChanged.Remove(value); } - /// Raised after a player warps to a new location. NOTE: this event is currently only raised for the local player. + /// public event EventHandler Warped { add => this.EventManager.Warped.Add(value, this.Mod); diff --git a/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs b/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs index 1d6788e1..7980208b 100644 --- a/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs +++ b/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs @@ -3,27 +3,27 @@ using StardewModdingAPI.Events; namespace StardewModdingAPI.Framework.Events { - /// Events serving specialized edge cases that shouldn't be used by most mods. + /// internal class ModSpecializedEvents : ModEventsBase, ISpecializedEvents { /********* ** Accessors *********/ - /// 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.LoadStageChanged.Add(value, this.Mod); 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. + /// public event EventHandler UnvalidatedUpdateTicking { add => this.EventManager.UnvalidatedUpdateTicking.Add(value, this.Mod); remove => this.EventManager.UnvalidatedUpdateTicking.Remove(value); } - /// Raised after 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 UnvalidatedUpdateTicked { add => this.EventManager.UnvalidatedUpdateTicked.Add(value, this.Mod); diff --git a/src/SMAPI/Framework/Events/ModWorldEvents.cs b/src/SMAPI/Framework/Events/ModWorldEvents.cs index f4c40abc..a7b7d799 100644 --- a/src/SMAPI/Framework/Events/ModWorldEvents.cs +++ b/src/SMAPI/Framework/Events/ModWorldEvents.cs @@ -3,69 +3,69 @@ using StardewModdingAPI.Events; namespace StardewModdingAPI.Framework.Events { - /// Events raised when something changes in the world. + /// internal class ModWorldEvents : ModEventsBase, IWorldEvents { /********* ** Accessors *********/ - /// Raised after a game location is added or removed. + /// public event EventHandler LocationListChanged { add => this.EventManager.LocationListChanged.Add(value, this.Mod); remove => this.EventManager.LocationListChanged.Remove(value); } - /// Raised after buildings are added or removed in a location. + /// public event EventHandler BuildingListChanged { add => this.EventManager.BuildingListChanged.Add(value, this.Mod); remove => this.EventManager.BuildingListChanged.Remove(value); } - /// Raised after debris are added or removed in a location. + /// public event EventHandler DebrisListChanged { add => this.EventManager.DebrisListChanged.Add(value, this.Mod); remove => this.EventManager.DebrisListChanged.Remove(value); } - /// Raised after large terrain features (like bushes) are added or removed in a location. + /// public event EventHandler LargeTerrainFeatureListChanged { add => this.EventManager.LargeTerrainFeatureListChanged.Add(value, this.Mod); remove => this.EventManager.LargeTerrainFeatureListChanged.Remove(value); } - /// Raised after NPCs are added or removed in a location. + /// public event EventHandler NpcListChanged { add => this.EventManager.NpcListChanged.Add(value, this.Mod); remove => this.EventManager.NpcListChanged.Remove(value); } - /// Raised after objects are added or removed in a location. + /// public event EventHandler ObjectListChanged { add => this.EventManager.ObjectListChanged.Add(value, this.Mod); remove => this.EventManager.ObjectListChanged.Remove(value); } - /// Raised after items are added or removed from a chest. + /// public event EventHandler ChestInventoryChanged { add => this.EventManager.ChestInventoryChanged.Add(value, this.Mod); remove => this.EventManager.ChestInventoryChanged.Remove(value); } - /// Raised after terrain features (like floors and trees) are added or removed in a location. + /// public event EventHandler TerrainFeatureListChanged { add => this.EventManager.TerrainFeatureListChanged.Add(value, this.Mod); remove => this.EventManager.TerrainFeatureListChanged.Remove(value); } - /// Raised after furniture are added or removed in a location. + /// public event EventHandler FurnitureListChanged { add => this.EventManager.FurnitureListChanged.Add(value, this.Mod); diff --git a/src/SMAPI/Framework/Input/GamePadStateBuilder.cs b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs index 3a99214f..ad254828 100644 --- a/src/SMAPI/Framework/Input/GamePadStateBuilder.cs +++ b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs @@ -85,8 +85,7 @@ namespace StardewModdingAPI.Framework.Input this.RightStickPos = sticks.Right; } - /// Override the states for a set of buttons. - /// The button state overrides. + /// public GamePadStateBuilder OverrideButtons(IDictionary overrides) { if (!this.IsConnected) @@ -151,7 +150,7 @@ namespace StardewModdingAPI.Framework.Input return this; } - /// Get the currently pressed buttons. + /// public IEnumerable GetPressedButtons() { if (!this.IsConnected) @@ -191,7 +190,7 @@ namespace StardewModdingAPI.Framework.Input } } - /// Get the equivalent state. + /// public GamePadState GetState() { this.State ??= new GamePadState( diff --git a/src/SMAPI/Framework/Input/KeyboardStateBuilder.cs b/src/SMAPI/Framework/Input/KeyboardStateBuilder.cs index 620ad442..eadb7a8a 100644 --- a/src/SMAPI/Framework/Input/KeyboardStateBuilder.cs +++ b/src/SMAPI/Framework/Input/KeyboardStateBuilder.cs @@ -31,8 +31,7 @@ namespace StardewModdingAPI.Framework.Input this.PressedButtons.Add(button); } - /// Override the states for a set of buttons. - /// The button state overrides. + /// public KeyboardStateBuilder OverrideButtons(IDictionary overrides) { foreach (var pair in overrides) @@ -51,14 +50,14 @@ namespace StardewModdingAPI.Framework.Input return this; } - /// Get the currently pressed buttons. + /// public IEnumerable GetPressedButtons() { foreach (Keys key in this.PressedButtons) yield return key.ToSButton(); } - /// Get the equivalent state. + /// public KeyboardState GetState() { return diff --git a/src/SMAPI/Framework/Input/MouseStateBuilder.cs b/src/SMAPI/Framework/Input/MouseStateBuilder.cs index a1ac5492..c2a0891b 100644 --- a/src/SMAPI/Framework/Input/MouseStateBuilder.cs +++ b/src/SMAPI/Framework/Input/MouseStateBuilder.cs @@ -51,8 +51,7 @@ namespace StardewModdingAPI.Framework.Input this.ScrollWheelValue = state.ScrollWheelValue; } - /// Override the states for a set of buttons. - /// The button state overrides. + /// public MouseStateBuilder OverrideButtons(IDictionary overrides) { foreach (var pair in overrides) @@ -67,7 +66,7 @@ namespace StardewModdingAPI.Framework.Input return this; } - /// Get the currently pressed buttons. + /// public IEnumerable GetPressedButtons() { foreach (var pair in this.ButtonStates) @@ -77,7 +76,7 @@ namespace StardewModdingAPI.Framework.Input } } - /// Get the equivalent state. + /// public MouseState GetState() { this.State ??= new MouseState( diff --git a/src/SMAPI/Mod.cs b/src/SMAPI/Mod.cs index 0e5be1c1..9af55cd4 100644 --- a/src/SMAPI/Mod.cs +++ b/src/SMAPI/Mod.cs @@ -8,25 +8,27 @@ namespace StardewModdingAPI /********* ** Accessors *********/ - /// Provides simplified APIs for writing mods. + /// public IModHelper Helper { get; internal set; } - /// Writes messages to the console and log file. + /// public IMonitor Monitor { get; internal set; } - /// The mod's manifest. + /// public IManifest ModManifest { get; internal set; } /********* ** Public methods *********/ - /// The mod entry point, called after the mod is first loaded. - /// Provides simplified APIs for writing mods. + /// public abstract void Entry(IModHelper helper); - /// Get an API that other mods can access. This is always called after . - public virtual object GetApi() => null; + /// + public virtual object GetApi() + { + return null; + } /// Release or reset unmanaged resources. public void Dispose() -- cgit From 584725bb8e554e314843315facca1fd15868bee4 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 23 Mar 2022 01:06:11 -0400 Subject: add initial AssetRequested content event (#766) --- docs/release-notes.md | 1 + src/SMAPI/Events/AssetRequestedEventArgs.cs | 94 ++++++++++++++++++++++++++ src/SMAPI/Events/IContentEvents.cs | 17 +++++ src/SMAPI/Events/IModEvents.cs | 3 + src/SMAPI/Framework/ContentCoordinator.cs | 12 +++- src/SMAPI/Framework/Events/EventManager.cs | 11 ++- src/SMAPI/Framework/Events/ManagedEvent.cs | 10 ++- src/SMAPI/Framework/Events/ModContentEvents.cs | 29 ++++++++ src/SMAPI/Framework/Events/ModEvents.cs | 4 ++ src/SMAPI/Framework/SCore.cs | 41 ++++++++++- src/SMAPI/IContentHelper.cs | 2 +- 11 files changed, 219 insertions(+), 5 deletions(-) create mode 100644 src/SMAPI/Events/AssetRequestedEventArgs.cs create mode 100644 src/SMAPI/Events/IContentEvents.cs create mode 100644 src/SMAPI/Framework/Events/ModContentEvents.cs (limited to 'src/SMAPI/Framework/Events') diff --git a/docs/release-notes.md b/docs/release-notes.md index b9385e3f..2d1cdf44 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -11,6 +11,7 @@ * Fixed `--no-terminal` still opening a terminal window, even if nothing is logged to it (thanks to Ryhon0!). * For mod authors: + * Added [`AssetRequested` content event](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events#Content), which will eventually replace `IAssetEditor` and `IAssetLoader`. * Overhauled [mod-provided APIs](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations#Mod-provided_APIs) (thanks to Shockah!). _This adds support for many previously-unsupported cases: proxied interfaces in return values or input arguments, proxied enums if their values match, generic methods, and more._ * Added `Constants.ContentPath`. diff --git a/src/SMAPI/Events/AssetRequestedEventArgs.cs b/src/SMAPI/Events/AssetRequestedEventArgs.cs new file mode 100644 index 00000000..b17250b0 --- /dev/null +++ b/src/SMAPI/Events/AssetRequestedEventArgs.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework.Graphics; +using StardewModdingAPI.Framework; +using StardewModdingAPI.Framework.Content; +using xTile; + +namespace StardewModdingAPI.Events +{ + /// Event arguments for an event. + public class AssetRequestedEventArgs : EventArgs + { + /********* + ** Fields + *********/ + /// The mod handling the event. + private readonly IModMetadata Mod; + + + /********* + ** Accessors + *********/ + /// The name of the asset being requested. + public IAssetName Name { get; } + + /// The load operations requested by the event handler. + internal IList LoadOperations { get; } = new List(); + + /// The edit operations requested by the event handler. + internal IList EditOperations { get; } = new List(); + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The mod handling the event. + /// The name of the asset being requested. + internal AssetRequestedEventArgs(IModMetadata mod, IAssetName name) + { + this.Mod = mod; + this.Name = name; + } + + /// Provide the initial instance for the asset, instead of trying to load it from the game's Content folder. + /// Get the initial instance of an asset. + /// + /// Usage notes: + /// + /// The asset doesn't need to exist in the game's Content folder. If any mod loads the asset, the game will see it as an existing asset as if it was in that folder. + /// Each asset can logically only have one initial instance. If multiple loads apply at the same time, SMAPI will raise an error and ignore all of them. If you're making changes to the existing asset instead of replacing it, you should use instead to avoid those limitations and improve mod compatibility. + /// + /// + public void LoadFrom(Func load) + { + this.LoadOperations.Add( + new AssetLoadOperation(this.Mod, _ => load()) + ); + } + + /// Provide the initial instance for the asset from a file in your mod folder, instead of trying to load it from the game's Content folder. + /// The expected data type. The main supported types are , , dictionaries, and lists; other types may be supported by the game's content pipeline. + /// The relative path to the file in your mod folder. + /// + /// Usage notes: + /// + /// The asset doesn't need to exist in the game's Content folder. If any mod loads the asset, the game will see it as an existing asset as if it was in that folder. + /// Each asset can logically only have one initial instance. If multiple loads apply at the same time, SMAPI will raise an error and ignore all of them. If you're making changes to the existing asset instead of replacing it, you should use instead to avoid those limitations and improve mod compatibility. + /// + /// + public void LoadFromModFile(string relativePath) + { + this.LoadOperations.Add( + new AssetLoadOperation(this.Mod, _ => this.Mod.Mod.Helper.Content.Load(relativePath)) + ); + } + + /// Edit the asset after it's loaded. + /// Apply changes to the asset. + /// + /// Usage notes: + /// + /// Editing an asset which doesn't exist has no effect. This is applied after the asset is loaded from the game's Content folder, or from any mod's or . + /// You can apply any number of edits to the asset. Each edit will be applied on top of the previous one (i.e. it'll see the merged asset from all previous edits as its input). + /// + /// + public void Edit(Action apply) + { + this.EditOperations.Add( + new AssetEditOperation(this.Mod, apply) + ); + } + } +} diff --git a/src/SMAPI/Events/IContentEvents.cs b/src/SMAPI/Events/IContentEvents.cs new file mode 100644 index 00000000..feaf9c0a --- /dev/null +++ b/src/SMAPI/Events/IContentEvents.cs @@ -0,0 +1,17 @@ +using System; +using StardewValley; + +namespace StardewModdingAPI.Events +{ + /// Events related to assets loaded from the content pipeline (including data, maps, and textures). + public interface IContentEvents + { + /// Raised when an asset is being requested from the content pipeline. + /// + /// The asset isn't necessarily being loaded yet (e.g. the game may be checking if it exists). Mods can register the changes they want to apply using methods on the parameter. These will be applied when the asset is actually loaded. + /// + /// If the asset is requested multiple times in the same tick (e.g. once to check if it exists and once to load it), SMAPI might only raise the event once and reuse the cached result. + /// + event EventHandler AssetRequested; + } +} diff --git a/src/SMAPI/Events/IModEvents.cs b/src/SMAPI/Events/IModEvents.cs index 1f892b31..2603961b 100644 --- a/src/SMAPI/Events/IModEvents.cs +++ b/src/SMAPI/Events/IModEvents.cs @@ -3,6 +3,9 @@ namespace StardewModdingAPI.Events /// Manages access to events raised by SMAPI. public interface IModEvents { + /// Events related to assets loaded from the content pipeline (including data, maps, and textures). + IContentEvents Content { get; } + /// Events related to UI and drawing to the screen. IDisplayEvents Display { get; } diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index bf944e23..22ae0a18 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text; using System.Threading; using Microsoft.Xna.Framework.Content; +using StardewModdingAPI.Events; using StardewModdingAPI.Framework.Content; using StardewModdingAPI.Framework.ContentManagers; using StardewModdingAPI.Framework.Reflection; @@ -70,6 +71,9 @@ namespace StardewModdingAPI.Framework /// The language enum values indexed by locale code. private Lazy> LocaleCodes; + /// Get the load/edit operations to apply to an asset by querying registered event handlers. + private readonly Func> RequestAssetOperations; + /// The cached asset load/edit operations to apply, indexed by asset name. private readonly TickCacheDictionary AssetOperationsByKey = new(); @@ -105,13 +109,15 @@ namespace StardewModdingAPI.Framework /// Encapsulates SMAPI's JSON file parsing. /// A callback to invoke the first time *any* game content manager loads an asset. /// Whether to enable more aggressive memory optimizations. - public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations) + /// Get the load/edit operations to apply to an asset by querying registered event handlers. + public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations, Func> requestAssetOperations) { this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations; this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor)); this.Reflection = reflection; this.JsonHelper = jsonHelper; this.OnLoadingFirstAsset = onLoadingFirstAsset; + this.RequestAssetOperations = requestAssetOperations; this.FullRootDirectory = Path.Combine(Constants.GamePath, rootDirectory); this.ContentManagers.Add( this.MainContentManager = new GameContentManager( @@ -560,6 +566,10 @@ namespace StardewModdingAPI.Framework /// The asset info to load or edit. private IEnumerable GetAssetOperationsWithoutCache(IAssetInfo info) { + // new content API + foreach (AssetOperationGroup group in this.RequestAssetOperations(info)) + yield return group; + // legacy load operations foreach (ModLinked loader in this.Loaders) { diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index fa4d564d..8142f00e 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -10,6 +10,13 @@ namespace StardewModdingAPI.Framework.Events /********* ** Events *********/ + /**** + ** Content + ****/ + /// + public readonly ManagedEvent AssetRequested; + + /**** ** Display ****/ @@ -189,7 +196,9 @@ namespace StardewModdingAPI.Framework.Events return new ManagedEvent($"{typeName}.{eventName}", modRegistry, isPerformanceCritical); } - // init events (new) + // init events + this.AssetRequested = ManageEventOf(nameof(IModEvents.Content), nameof(IContentEvents.AssetRequested)); + this.MenuChanged = ManageEventOf(nameof(IModEvents.Display), nameof(IDisplayEvents.MenuChanged)); this.Rendering = ManageEventOf(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendering), isPerformanceCritical: true); this.Rendered = ManageEventOf(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendered), isPerformanceCritical: true); diff --git a/src/SMAPI/Framework/Events/ManagedEvent.cs b/src/SMAPI/Framework/Events/ManagedEvent.cs index a200393d..154ef659 100644 --- a/src/SMAPI/Framework/Events/ManagedEvent.cs +++ b/src/SMAPI/Framework/Events/ManagedEvent.cs @@ -99,6 +99,14 @@ namespace StardewModdingAPI.Framework.Events /// The event arguments to pass. /// A lambda which returns true if the event should be raised for the given mod. public void Raise(TEventArgs args, Func match = null) + { + this.Raise((_, invoke) => invoke(args), match); + } + + /// Raise the event and notify all handlers. + /// Invoke an event handler. This receives the mod which registered the handler, and should invoke the callback with the event arguments to pass it. + /// A lambda which returns true if the event should be raised for the given mod. + public void Raise(Action> invoke, Func match = null) { // skip if no handlers if (this.Handlers.Count == 0) @@ -128,7 +136,7 @@ namespace StardewModdingAPI.Framework.Events try { - handler.Handler.Invoke(null, args); + invoke(handler.SourceMod, args => handler.Handler.Invoke(null, args)); } catch (Exception ex) { diff --git a/src/SMAPI/Framework/Events/ModContentEvents.cs b/src/SMAPI/Framework/Events/ModContentEvents.cs new file mode 100644 index 00000000..b4d4279c --- /dev/null +++ b/src/SMAPI/Framework/Events/ModContentEvents.cs @@ -0,0 +1,29 @@ +using System; +using StardewModdingAPI.Events; + +namespace StardewModdingAPI.Framework.Events +{ + /// + internal class ModContentEvents : ModEventsBase, IContentEvents + { + /********* + ** Accessors + *********/ + /// + public event EventHandler AssetRequested + { + add => this.EventManager.AssetRequested.Add(value, this.Mod); + remove => this.EventManager.AssetRequested.Remove(value); + } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The mod which uses this instance. + /// The underlying event manager. + internal ModContentEvents(IModMetadata mod, EventManager eventManager) + : base(mod, eventManager) { } + } +} diff --git a/src/SMAPI/Framework/Events/ModEvents.cs b/src/SMAPI/Framework/Events/ModEvents.cs index 0c365d42..1fb3482c 100644 --- a/src/SMAPI/Framework/Events/ModEvents.cs +++ b/src/SMAPI/Framework/Events/ModEvents.cs @@ -8,6 +8,9 @@ namespace StardewModdingAPI.Framework.Events /********* ** Accessors *********/ + /// + public IContentEvents Content { get; } + /// public IDisplayEvents Display { get; } @@ -38,6 +41,7 @@ namespace StardewModdingAPI.Framework.Events /// The underlying event manager. public ModEvents(IModMetadata mod, EventManager eventManager) { + this.Content = new ModContentEvents(mod, eventManager); this.Display = new ModDisplayEvents(mod, eventManager); this.GameLoop = new ModGameLoopEvents(mod, eventManager); this.Input = new ModInputEvents(mod, eventManager); diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 342d6415..f0340cf5 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -1106,6 +1106,35 @@ namespace StardewModdingAPI.Framework this.EventManager.DayEnding.RaiseEmpty(); } + /// Get the load/edit operations to apply to an asset by querying registered event handlers. + /// The asset info being requested. + private IList RequestAssetOperations(IAssetInfo asset) + { + List operations = new(); + + this.EventManager.AssetRequested.Raise( + invoke: (mod, invoke) => + { + AssetRequestedEventArgs args = new(mod, asset.Name); + + invoke(args); + + if (args.LoadOperations.Any() || args.EditOperations.Any()) + { + operations.Add( + new AssetOperationGroup( + mod, + args.LoadOperations.Select(p => new AssetLoadOperation(mod, assetInfo => p.GetData(assetInfo))).ToArray(), + args.EditOperations.Select(p => new AssetEditOperation(mod, assetInfo => p.ApplyEdit(assetInfo))).ToArray() + ) + ); + } + } + ); + + return operations; + } + /// Raised immediately before the player returns to the title screen. private void OnReturningToTitle() { @@ -1142,7 +1171,17 @@ namespace StardewModdingAPI.Framework // Game1._temporaryContent initializing from SGame constructor if (this.ContentCore == null) { - this.ContentCore = new ContentCoordinator(serviceProvider, rootDirectory, Thread.CurrentThread.CurrentUICulture, this.Monitor, this.Reflection, this.Toolkit.JsonHelper, this.InitializeBeforeFirstAssetLoaded, this.Settings.AggressiveMemoryOptimizations); + this.ContentCore = new ContentCoordinator( + serviceProvider: serviceProvider, + rootDirectory: rootDirectory, + currentCulture: Thread.CurrentThread.CurrentUICulture, + monitor: this.Monitor, + reflection: this.Reflection, + jsonHelper: this.Toolkit.JsonHelper, + onLoadingFirstAsset: this.InitializeBeforeFirstAssetLoaded, + aggressiveMemoryOptimizations: this.Settings.AggressiveMemoryOptimizations, + requestAssetOperations: this.RequestAssetOperations + ); if (this.ContentCore.Language != this.Translator.LocaleEnum) this.Translator.SetLocale(this.ContentCore.GetLocale(), this.ContentCore.Language); diff --git a/src/SMAPI/IContentHelper.cs b/src/SMAPI/IContentHelper.cs index 2936ecfb..207b4a33 100644 --- a/src/SMAPI/IContentHelper.cs +++ b/src/SMAPI/IContentHelper.cs @@ -31,7 +31,7 @@ namespace StardewModdingAPI ** Public methods *********/ /// Load content from the game folder or mod folder (if not already cached), and return it. When loading a .png file, this must be called outside the game's draw loop. - /// The expected data type. The main supported types are , , and dictionaries; other types may be supported by the game's content pipeline. + /// The expected data type. The main supported types are , , dictionaries, and lists; other types may be supported by the game's content pipeline. /// The asset key to fetch (if the is ), or the local path to a content file relative to the mod folder. /// Where to search for a matching content asset. /// The is empty or contains invalid characters. -- cgit From 2b0ce2bb4d6690b7d00da0a243855db9bffffbf0 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 24 Mar 2022 22:55:55 -0400 Subject: add AssetInvalidated content event (#766) --- docs/release-notes.md | 2 +- src/SMAPI/Events/AssetsInvalidatedEventArgs.cs | 27 +++++++++++++++++++++++ src/SMAPI/Events/IContentEvents.cs | 3 +++ src/SMAPI/Framework/ContentCoordinator.cs | 30 ++++++++++++++++---------- src/SMAPI/Framework/Events/EventManager.cs | 4 ++++ src/SMAPI/Framework/Events/ModContentEvents.cs | 7 ++++++ src/SMAPI/Framework/SCore.cs | 9 ++++++++ 7 files changed, 70 insertions(+), 12 deletions(-) create mode 100644 src/SMAPI/Events/AssetsInvalidatedEventArgs.cs (limited to 'src/SMAPI/Framework/Events') diff --git a/docs/release-notes.md b/docs/release-notes.md index 2d1cdf44..8bf451a5 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -11,7 +11,7 @@ * Fixed `--no-terminal` still opening a terminal window, even if nothing is logged to it (thanks to Ryhon0!). * For mod authors: - * Added [`AssetRequested` content event](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events#Content), which will eventually replace `IAssetEditor` and `IAssetLoader`. + * Added [content events](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events#Content), which will replace `IAssetEditor` and `IAssetLoader` in SMAPI 4.0.0. * Overhauled [mod-provided APIs](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations#Mod-provided_APIs) (thanks to Shockah!). _This adds support for many previously-unsupported cases: proxied interfaces in return values or input arguments, proxied enums if their values match, generic methods, and more._ * Added `Constants.ContentPath`. diff --git a/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs b/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs new file mode 100644 index 00000000..0127f83a --- /dev/null +++ b/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace StardewModdingAPI.Events +{ + /// Event arguments for an event. + public class AssetsInvalidatedEventArgs : EventArgs + { + /********* + ** Accessors + *********/ + /// The asset names that were invalidated. + public IEnumerable Names { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The asset names that were invalidated. + internal AssetsInvalidatedEventArgs(IEnumerable names) + { + this.Names = names.ToArray(); + } + } +} diff --git a/src/SMAPI/Events/IContentEvents.cs b/src/SMAPI/Events/IContentEvents.cs index feaf9c0a..ede9ea23 100644 --- a/src/SMAPI/Events/IContentEvents.cs +++ b/src/SMAPI/Events/IContentEvents.cs @@ -13,5 +13,8 @@ namespace StardewModdingAPI.Events /// If the asset is requested multiple times in the same tick (e.g. once to check if it exists and once to load it), SMAPI might only raise the event once and reuse the cached result. /// event EventHandler AssetRequested; + + /// Raised after one or more assets were invalidated from the content cache by a mod, so they'll be reloaded next time they're requested. If the assets will be reloaded or propagated automatically, this event is raised before that happens. + event EventHandler AssetsInvalidated; } } diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index 22ae0a18..4f696928 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -49,6 +49,12 @@ namespace StardewModdingAPI.Framework /// A callback to invoke the first time *any* game content manager loads an asset. private readonly Action OnLoadingFirstAsset; + /// A callback to invoke when any asset names have been invalidated from the cache. + private readonly Action> OnAssetsInvalidated; + + /// Get the load/edit operations to apply to an asset by querying registered event handlers. + private readonly Func> RequestAssetOperations; + /// The loaded content managers (including the ). private readonly List ContentManagers = new(); @@ -71,9 +77,6 @@ namespace StardewModdingAPI.Framework /// The language enum values indexed by locale code. private Lazy> LocaleCodes; - /// Get the load/edit operations to apply to an asset by querying registered event handlers. - private readonly Func> RequestAssetOperations; - /// The cached asset load/edit operations to apply, indexed by asset name. private readonly TickCacheDictionary AssetOperationsByKey = new(); @@ -109,14 +112,16 @@ namespace StardewModdingAPI.Framework /// Encapsulates SMAPI's JSON file parsing. /// A callback to invoke the first time *any* game content manager loads an asset. /// Whether to enable more aggressive memory optimizations. + /// A callback to invoke when any asset names have been invalidated from the cache. /// Get the load/edit operations to apply to an asset by querying registered event handlers. - public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations, Func> requestAssetOperations) + public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations, Action> onAssetsInvalidated, Func> requestAssetOperations) { this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations; this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor)); this.Reflection = reflection; this.JsonHelper = jsonHelper; this.OnLoadingFirstAsset = onLoadingFirstAsset; + this.OnAssetsInvalidated = onAssetsInvalidated; this.RequestAssetOperations = requestAssetOperations; this.FullRootDirectory = Path.Combine(Constants.GamePath, rootDirectory); this.ContentManagers.Add( @@ -257,7 +262,7 @@ namespace StardewModdingAPI.Framework // Note that we *must* propagate changes here, otherwise when mods invalidate the cache later to reapply // their changes, the assets won't be found in the cache so no changes will be propagated. if (LocalizedContentManager.CurrentLanguageCode != LocalizedContentManager.LanguageCode.en) - this.InvalidateCache((contentManager, key, type) => contentManager is GameContentManager); + this.InvalidateCache((contentManager, _, _) => contentManager is GameContentManager); } /// Parse a raw asset name. @@ -347,7 +352,7 @@ namespace StardewModdingAPI.Framework public IEnumerable InvalidateCache(Func predicate, bool dispose = false) { string locale = this.GetLocale(); - return this.InvalidateCache((contentManager, rawName, type) => + return this.InvalidateCache((_, rawName, type) => { IAssetName assetName = this.ParseAssetName(rawName); IAssetInfo info = new AssetInfo(locale, assetName, type, this.MainContentManager.AssertAndNormalizeAssetName); @@ -393,13 +398,16 @@ namespace StardewModdingAPI.Framework } }); - // clear cached editor checks - foreach (IAssetName name in invalidatedAssets.Keys) - this.AssetOperationsByKey.Remove(name); - - // reload core game assets + // handle invalidation if (invalidatedAssets.Any()) { + // clear cached editor checks + foreach (IAssetName name in invalidatedAssets.Keys) + this.AssetOperationsByKey.Remove(name); + + // raise event + this.OnAssetsInvalidated(invalidatedAssets.Keys); + // propagate changes to the game this.CoreAssets.Propagate( assets: invalidatedAssets.ToDictionary(p => p.Key, p => p.Value), diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index 8142f00e..96582380 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -16,6 +16,9 @@ namespace StardewModdingAPI.Framework.Events /// public readonly ManagedEvent AssetRequested; + /// + public readonly ManagedEvent AssetsInvalidated; + /**** ** Display @@ -198,6 +201,7 @@ namespace StardewModdingAPI.Framework.Events // init events this.AssetRequested = ManageEventOf(nameof(IModEvents.Content), nameof(IContentEvents.AssetRequested)); + this.AssetsInvalidated = ManageEventOf(nameof(IModEvents.Content), nameof(IContentEvents.AssetsInvalidated)); this.MenuChanged = ManageEventOf(nameof(IModEvents.Display), nameof(IDisplayEvents.MenuChanged)); this.Rendering = ManageEventOf(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendering), isPerformanceCritical: true); diff --git a/src/SMAPI/Framework/Events/ModContentEvents.cs b/src/SMAPI/Framework/Events/ModContentEvents.cs index b4d4279c..4d0cfb97 100644 --- a/src/SMAPI/Framework/Events/ModContentEvents.cs +++ b/src/SMAPI/Framework/Events/ModContentEvents.cs @@ -16,6 +16,13 @@ namespace StardewModdingAPI.Framework.Events remove => this.EventManager.AssetRequested.Remove(value); } + /// + public event EventHandler AssetsInvalidated + { + add => this.EventManager.AssetsInvalidated.Add(value, this.Mod); + remove => this.EventManager.AssetsInvalidated.Remove(value); + } + /********* ** Public methods diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index f9f84206..e9bc9a2b 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -1106,6 +1106,14 @@ namespace StardewModdingAPI.Framework this.EventManager.DayEnding.RaiseEmpty(); } + /// A callback invoked after assets have been invalidated from the content cache. + /// The invalidated asset names. + private void OnAssetsInvalidated(IEnumerable assetNames) + { + if (this.EventManager.AssetsInvalidated.HasListeners()) + this.EventManager.AssetsInvalidated.Raise(new AssetsInvalidatedEventArgs(assetNames)); + } + /// Get the load/edit operations to apply to an asset by querying registered event handlers. /// The asset info being requested. private IList RequestAssetOperations(IAssetInfo asset) @@ -1175,6 +1183,7 @@ namespace StardewModdingAPI.Framework reflection: this.Reflection, jsonHelper: this.Toolkit.JsonHelper, onLoadingFirstAsset: this.InitializeBeforeFirstAssetLoaded, + onAssetsInvalidated: this.OnAssetsInvalidated, aggressiveMemoryOptimizations: this.Settings.AggressiveMemoryOptimizations, requestAssetOperations: this.RequestAssetOperations ); -- cgit From b77eab6e0a09099998aa806302694e82216e79f8 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 25 Mar 2022 00:35:31 -0400 Subject: add AssetReady content event (#766) --- src/SMAPI/Events/AssetReadyEventArgs.cs | 25 ++++++++++++++++++++++ src/SMAPI/Events/IContentEvents.cs | 7 ++++-- src/SMAPI/Framework/ContentCoordinator.cs | 10 ++++++++- .../ContentManagers/GameContentManager.cs | 12 +++++++++-- .../GameContentManagerForAssetPropagation.cs | 4 ++-- src/SMAPI/Framework/Events/EventManager.cs | 6 ++++-- src/SMAPI/Framework/Events/ModContentEvents.cs | 7 ++++++ src/SMAPI/Framework/SCore.cs | 11 ++++++++++ 8 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 src/SMAPI/Events/AssetReadyEventArgs.cs (limited to 'src/SMAPI/Framework/Events') diff --git a/src/SMAPI/Events/AssetReadyEventArgs.cs b/src/SMAPI/Events/AssetReadyEventArgs.cs new file mode 100644 index 00000000..946c9173 --- /dev/null +++ b/src/SMAPI/Events/AssetReadyEventArgs.cs @@ -0,0 +1,25 @@ +using System; + +namespace StardewModdingAPI.Events +{ + /// Event arguments for an event. + public class AssetReadyEventArgs : EventArgs + { + /********* + ** Accessors + *********/ + /// The name of the asset being requested. + public IAssetName Name { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The name of the asset being requested. + internal AssetReadyEventArgs(IAssetName name) + { + this.Name = name; + } + } +} diff --git a/src/SMAPI/Events/IContentEvents.cs b/src/SMAPI/Events/IContentEvents.cs index ede9ea23..abbaaf33 100644 --- a/src/SMAPI/Events/IContentEvents.cs +++ b/src/SMAPI/Events/IContentEvents.cs @@ -1,5 +1,4 @@ using System; -using StardewValley; namespace StardewModdingAPI.Events { @@ -8,7 +7,7 @@ namespace StardewModdingAPI.Events { /// Raised when an asset is being requested from the content pipeline. /// - /// The asset isn't necessarily being loaded yet (e.g. the game may be checking if it exists). Mods can register the changes they want to apply using methods on the parameter. These will be applied when the asset is actually loaded. + /// The asset isn't necessarily being loaded yet (e.g. the game may be checking if it exists). Mods can register the changes they want to apply using methods on the event arguments. These will be applied when the asset is actually loaded. /// /// If the asset is requested multiple times in the same tick (e.g. once to check if it exists and once to load it), SMAPI might only raise the event once and reuse the cached result. /// @@ -16,5 +15,9 @@ namespace StardewModdingAPI.Events /// Raised after one or more assets were invalidated from the content cache by a mod, so they'll be reloaded next time they're requested. If the assets will be reloaded or propagated automatically, this event is raised before that happens. event EventHandler AssetsInvalidated; + + /// Raised after an asset is loaded by the content pipeline, after all mod edits specified via have been applied. + /// This event is only raised if something requested the asset from the content pipeline. Invalidating an asset from the content cache won't necessarily reload it automatically. + event EventHandler AssetReady; } } diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index 4f696928..4dbbae15 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -49,6 +49,9 @@ namespace StardewModdingAPI.Framework /// A callback to invoke the first time *any* game content manager loads an asset. private readonly Action OnLoadingFirstAsset; + /// A callback to invoke when an asset is fully loaded. + private readonly Action OnAssetLoaded; + /// A callback to invoke when any asset names have been invalidated from the cache. private readonly Action> OnAssetsInvalidated; @@ -111,16 +114,18 @@ namespace StardewModdingAPI.Framework /// Simplifies access to private code. /// Encapsulates SMAPI's JSON file parsing. /// A callback to invoke the first time *any* game content manager loads an asset. + /// A callback to invoke when an asset is fully loaded. /// Whether to enable more aggressive memory optimizations. /// A callback to invoke when any asset names have been invalidated from the cache. /// Get the load/edit operations to apply to an asset by querying registered event handlers. - public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations, Action> onAssetsInvalidated, Func> requestAssetOperations) + public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, Action onAssetLoaded, bool aggressiveMemoryOptimizations, Action> onAssetsInvalidated, Func> requestAssetOperations) { this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations; this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor)); this.Reflection = reflection; this.JsonHelper = jsonHelper; this.OnLoadingFirstAsset = onLoadingFirstAsset; + this.OnAssetLoaded = onAssetLoaded; this.OnAssetsInvalidated = onAssetsInvalidated; this.RequestAssetOperations = requestAssetOperations; this.FullRootDirectory = Path.Combine(Constants.GamePath, rootDirectory); @@ -135,6 +140,7 @@ namespace StardewModdingAPI.Framework reflection: reflection, onDisposing: this.OnDisposing, onLoadingFirstAsset: onLoadingFirstAsset, + onAssetLoaded: onAssetLoaded, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations ) ); @@ -148,6 +154,7 @@ namespace StardewModdingAPI.Framework reflection: reflection, onDisposing: this.OnDisposing, onLoadingFirstAsset: onLoadingFirstAsset, + onAssetLoaded: onAssetLoaded, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations ); this.ContentManagers.Add(contentManagerForAssetPropagation); @@ -172,6 +179,7 @@ namespace StardewModdingAPI.Framework reflection: this.Reflection, onDisposing: this.OnDisposing, onLoadingFirstAsset: this.OnLoadingFirstAsset, + onAssetLoaded: this.OnAssetLoaded, aggressiveMemoryOptimizations: this.AggressiveMemoryOptimizations ); this.ContentManagers.Add(manager); diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs index 642e526c..12ed5506 100644 --- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs @@ -35,6 +35,9 @@ namespace StardewModdingAPI.Framework.ContentManagers /// A callback to invoke the first time *any* game content manager loads an asset. private readonly Action OnLoadingFirstAsset; + /// A callback to invoke when an asset is fully loaded. + private readonly Action OnAssetLoaded; + /********* ** Public methods @@ -49,11 +52,13 @@ namespace StardewModdingAPI.Framework.ContentManagers /// Simplifies access to private code. /// A callback to invoke when the content manager is being disposed. /// A callback to invoke the first time *any* game content manager loads an asset. + /// A callback to invoke when an asset is fully loaded. /// Whether to enable more aggressive memory optimizations. - public GameContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action onDisposing, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations) + public GameContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action onDisposing, Action onLoadingFirstAsset, Action onAssetLoaded, bool aggressiveMemoryOptimizations) : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: false, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations) { this.OnLoadingFirstAsset = onLoadingFirstAsset; + this.OnAssetLoaded = onAssetLoaded; } /// @@ -129,8 +134,11 @@ namespace StardewModdingAPI.Framework.ContentManagers }); } - // update cache & return data + // update cache this.TrackAsset(assetName, data, language, useCache); + + // raise event & return data + this.OnAssetLoaded(this, assetName); return data; } diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs b/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs index 206ece30..847e2ce1 100644 --- a/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs +++ b/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs @@ -21,8 +21,8 @@ namespace StardewModdingAPI.Framework.ContentManagers ** Public methods *********/ /// - public GameContentManagerForAssetPropagation(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action onDisposing, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations) - : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, onLoadingFirstAsset, aggressiveMemoryOptimizations) { } + public GameContentManagerForAssetPropagation(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action onDisposing, Action onLoadingFirstAsset, Action onAssetLoaded, bool aggressiveMemoryOptimizations) + : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, onLoadingFirstAsset, onAssetLoaded, aggressiveMemoryOptimizations) { } /// public override T Load(IAssetName assetName, LanguageCode language, bool useCache) diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index 96582380..bcfd7dd7 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -1,10 +1,8 @@ -using System.Diagnostics.CodeAnalysis; using StardewModdingAPI.Events; namespace StardewModdingAPI.Framework.Events { /// Manages SMAPI events. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Private fields are deliberately named to simplify organisation.")] internal class EventManager { /********* @@ -19,6 +17,9 @@ namespace StardewModdingAPI.Framework.Events /// public readonly ManagedEvent AssetsInvalidated; + /// + public readonly ManagedEvent AssetReady; + /**** ** Display @@ -202,6 +203,7 @@ namespace StardewModdingAPI.Framework.Events // init events this.AssetRequested = ManageEventOf(nameof(IModEvents.Content), nameof(IContentEvents.AssetRequested)); this.AssetsInvalidated = ManageEventOf(nameof(IModEvents.Content), nameof(IContentEvents.AssetsInvalidated)); + this.AssetReady = ManageEventOf(nameof(IModEvents.Content), nameof(IContentEvents.AssetReady)); this.MenuChanged = ManageEventOf(nameof(IModEvents.Display), nameof(IDisplayEvents.MenuChanged)); this.Rendering = ManageEventOf(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendering), isPerformanceCritical: true); diff --git a/src/SMAPI/Framework/Events/ModContentEvents.cs b/src/SMAPI/Framework/Events/ModContentEvents.cs index 4d0cfb97..cb242e99 100644 --- a/src/SMAPI/Framework/Events/ModContentEvents.cs +++ b/src/SMAPI/Framework/Events/ModContentEvents.cs @@ -23,6 +23,13 @@ namespace StardewModdingAPI.Framework.Events remove => this.EventManager.AssetsInvalidated.Remove(value); } + /// + public event EventHandler AssetReady + { + add => this.EventManager.AssetReady.Add(value, this.Mod); + remove => this.EventManager.AssetReady.Remove(value); + } + /********* ** Public methods diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index e9bc9a2b..9d97ec7d 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -19,6 +19,7 @@ using Newtonsoft.Json; using StardewModdingAPI.Enums; using StardewModdingAPI.Events; using StardewModdingAPI.Framework.Content; +using StardewModdingAPI.Framework.ContentManagers; using StardewModdingAPI.Framework.Events; using StardewModdingAPI.Framework.Exceptions; using StardewModdingAPI.Framework.Input; @@ -1106,6 +1107,15 @@ namespace StardewModdingAPI.Framework this.EventManager.DayEnding.RaiseEmpty(); } + /// A callback invoked after an asset is fully loaded through a content manager. + /// The content manager through which the asset was loaded. + /// The asset name that was loaded. + private void OnAssetLoaded(IContentManager contentManager, IAssetName assetName) + { + if (this.EventManager.AssetReady.HasListeners()) + this.EventManager.AssetReady.Raise(new AssetReadyEventArgs(assetName)); + } + /// A callback invoked after assets have been invalidated from the content cache. /// The invalidated asset names. private void OnAssetsInvalidated(IEnumerable assetNames) @@ -1183,6 +1193,7 @@ namespace StardewModdingAPI.Framework reflection: this.Reflection, jsonHelper: this.Toolkit.JsonHelper, onLoadingFirstAsset: this.InitializeBeforeFirstAssetLoaded, + onAssetLoaded: this.OnAssetLoaded, onAssetsInvalidated: this.OnAssetsInvalidated, aggressiveMemoryOptimizations: this.Settings.AggressiveMemoryOptimizations, requestAssetOperations: this.RequestAssetOperations -- cgit From 03efea26676464933513383eb1c841f1ca5db34d Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 26 Mar 2022 19:08:25 -0400 Subject: add LocaleChanged content event (#766) --- src/SMAPI/Events/IContentEvents.cs | 4 ++ src/SMAPI/Events/LocaleChangedEventArgs.cs | 45 +++++++++++++++++++ src/SMAPI/Framework/Events/EventManager.cs | 4 ++ src/SMAPI/Framework/Events/ModContentEvents.cs | 7 +++ src/SMAPI/Framework/SCore.cs | 62 +++++++++++++++++--------- 5 files changed, 100 insertions(+), 22 deletions(-) create mode 100644 src/SMAPI/Events/LocaleChangedEventArgs.cs (limited to 'src/SMAPI/Framework/Events') diff --git a/src/SMAPI/Events/IContentEvents.cs b/src/SMAPI/Events/IContentEvents.cs index abbaaf33..d537db70 100644 --- a/src/SMAPI/Events/IContentEvents.cs +++ b/src/SMAPI/Events/IContentEvents.cs @@ -19,5 +19,9 @@ namespace StardewModdingAPI.Events /// Raised after an asset is loaded by the content pipeline, after all mod edits specified via have been applied. /// This event is only raised if something requested the asset from the content pipeline. Invalidating an asset from the content cache won't necessarily reload it automatically. event EventHandler AssetReady; + + /// Raised after the game language changes. + /// For non-English players, this may be raised during startup when the game switches to the previously selected language. + event EventHandler LocaleChanged; } } diff --git a/src/SMAPI/Events/LocaleChangedEventArgs.cs b/src/SMAPI/Events/LocaleChangedEventArgs.cs new file mode 100644 index 00000000..09d3f6e5 --- /dev/null +++ b/src/SMAPI/Events/LocaleChangedEventArgs.cs @@ -0,0 +1,45 @@ +using System; +using LanguageCode = StardewValley.LocalizedContentManager.LanguageCode; + +namespace StardewModdingAPI.Events +{ + /// Event arguments for an event. + public class LocaleChangedEventArgs : EventArgs + { + /********* + ** Accessors + *********/ + /// The previous language enum value. + /// For a custom language, this is always . + public LanguageCode OldLanguage { get; } + + /// The previous locale code. + /// This is the locale code as it appears in asset names, like fr-FR in Maps/springobjects.fr-FR. The locale code for English is an empty string. + public string OldLocale { get; } + + /// The new language enum value. + /// + public LanguageCode NewLanguage { get; } + + /// The new locale code. + /// + public string NewLocale { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The previous language enum value. + /// The previous locale code. + /// The new language enum value. + /// The new locale code. + internal LocaleChangedEventArgs(LanguageCode oldLanguage, string oldLocale, LanguageCode newLanguage, string newLocale) + { + this.OldLanguage = oldLanguage; + this.OldLocale = oldLocale; + this.NewLanguage = newLanguage; + this.NewLocale = newLocale; + } + } +} diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index bcfd7dd7..41540047 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -20,6 +20,9 @@ namespace StardewModdingAPI.Framework.Events /// public readonly ManagedEvent AssetReady; + /// + public readonly ManagedEvent LocaleChanged; + /**** ** Display @@ -204,6 +207,7 @@ namespace StardewModdingAPI.Framework.Events this.AssetRequested = ManageEventOf(nameof(IModEvents.Content), nameof(IContentEvents.AssetRequested)); this.AssetsInvalidated = ManageEventOf(nameof(IModEvents.Content), nameof(IContentEvents.AssetsInvalidated)); this.AssetReady = ManageEventOf(nameof(IModEvents.Content), nameof(IContentEvents.AssetReady)); + this.LocaleChanged = ManageEventOf(nameof(IModEvents.Content), nameof(IContentEvents.LocaleChanged)); this.MenuChanged = ManageEventOf(nameof(IModEvents.Display), nameof(IDisplayEvents.MenuChanged)); this.Rendering = ManageEventOf(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendering), isPerformanceCritical: true); diff --git a/src/SMAPI/Framework/Events/ModContentEvents.cs b/src/SMAPI/Framework/Events/ModContentEvents.cs index cb242e99..beb96031 100644 --- a/src/SMAPI/Framework/Events/ModContentEvents.cs +++ b/src/SMAPI/Framework/Events/ModContentEvents.cs @@ -30,6 +30,13 @@ namespace StardewModdingAPI.Framework.Events remove => this.EventManager.AssetReady.Remove(value); } + /// + public event EventHandler LocaleChanged + { + add => this.EventManager.LocaleChanged.Add(value, this.Mod); + remove => this.EventManager.LocaleChanged.Remove(value); + } + /********* ** Public methods diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index eab977ac..5deb177c 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -46,6 +46,7 @@ using StardewModdingAPI.Utilities; using StardewValley; using StardewValley.Menus; using xTile.Display; +using LanguageCode = StardewValley.LocalizedContentManager.LanguageCode; using MiniMonoModHotfix = MonoMod.Utils.MiniMonoModHotfix; using PathUtilities = StardewModdingAPI.Toolkit.Utilities.PathUtilities; using SObject = StardewValley.Object; @@ -62,7 +63,7 @@ namespace StardewModdingAPI.Framework ** Low-level components ****/ /// Tracks whether the game should exit immediately and any pending initialization should be cancelled. - private readonly CancellationTokenSource CancellationToken = new CancellationTokenSource(); + private readonly CancellationTokenSource CancellationToken = new(); /// Manages the SMAPI console window and log file. private readonly LogManager LogManager; @@ -71,16 +72,16 @@ namespace StardewModdingAPI.Framework private Monitor Monitor => this.LogManager.Monitor; /// Simplifies access to private game code. - private readonly Reflector Reflection = new Reflector(); + private readonly Reflector Reflection = new(); /// Encapsulates access to SMAPI core translations. - private readonly Translator Translator = new Translator(); + private readonly Translator Translator = new(); /// The SMAPI configuration settings. private readonly SConfig Settings; /// The mod toolkit used for generic mod interactions. - private readonly ModToolkit Toolkit = new ModToolkit(); + private readonly ModToolkit Toolkit = new(); /**** ** Higher-level components @@ -99,7 +100,7 @@ namespace StardewModdingAPI.Framework /// Tracks the installed mods. /// This is initialized after the game starts. - private readonly ModRegistry ModRegistry = new ModRegistry(); + private readonly ModRegistry ModRegistry = new(); /// Manages SMAPI events for mods. private readonly EventManager EventManager; @@ -129,18 +130,21 @@ namespace StardewModdingAPI.Framework /// Whether the player just returned to the title screen. public bool JustReturnedToTitle { get; set; } + /// The last language set by the game. + private (string Locale, LanguageCode Code) LastLanguage { get; set; } = ("", LanguageCode.en); + /// The maximum number of consecutive attempts SMAPI should make to recover from an update error. - private readonly Countdown UpdateCrashTimer = new Countdown(60); // 60 ticks = roughly one second + private readonly Countdown UpdateCrashTimer = new(60); // 60 ticks = roughly one second /// Asset interceptors added or removed since the last tick. - private readonly List ReloadAssetInterceptorsQueue = new List(); + private readonly List ReloadAssetInterceptorsQueue = new(); /// A list of queued commands to parse and execute. /// This property must be thread-safe, since it's accessed from a separate console input thread. - private readonly ConcurrentQueue RawCommandQueue = new ConcurrentQueue(); + private readonly ConcurrentQueue RawCommandQueue = new(); /// A list of commands to execute on each screen. - private readonly PerScreen>> ScreenCommandQueue = new PerScreen>>(() => new List>()); + private readonly PerScreen>> ScreenCommandQueue = new(() => new List>()); /********* @@ -369,13 +373,13 @@ namespace StardewModdingAPI.Framework xTile.Format.FormatManager.Instance.RegisterMapFormat(new TMXTile.TMXFormat(Game1.tileSize / Game1.pixelZoom, Game1.tileSize / Game1.pixelZoom, Game1.pixelZoom, Game1.pixelZoom)); // load mod data - ModToolkit toolkit = new ModToolkit(); + ModToolkit toolkit = new(); ModDatabase modDatabase = toolkit.GetModDatabase(Constants.ApiMetadataPath); // load mods { this.Monitor.Log("Loading mod metadata...", LogLevel.Debug); - ModResolver resolver = new ModResolver(); + ModResolver resolver = new(); // log loose files { @@ -1048,7 +1052,7 @@ namespace StardewModdingAPI.Framework // get locale string locale = this.ContentCore.GetLocale(); - LocalizedContentManager.LanguageCode languageCode = this.ContentCore.Language; + LanguageCode languageCode = this.ContentCore.Language; // update core translations this.Translator.SetLocale(locale, languageCode); @@ -1061,6 +1065,20 @@ namespace StardewModdingAPI.Framework foreach (ContentPack contentPack in mod.GetFakeContentPacks()) contentPack.TranslationImpl.SetLocale(locale, languageCode); } + + // raise event + if (this.EventManager.LocaleChanged.HasListeners()) + { + this.EventManager.LocaleChanged.Raise( + new LocaleChangedEventArgs( + oldLanguage: this.LastLanguage.Code, + oldLocale: this.LastLanguage.Locale, + newLanguage: languageCode, + newLocale: locale + ) + ); + } + this.LastLanguage = (locale, languageCode); } /// Raised when the low-level stage while loading a save changes. @@ -1385,7 +1403,7 @@ namespace StardewModdingAPI.Framework { // create client string url = this.Settings.WebApiBaseUrl; - WebApiClient client = new WebApiClient(url, Constants.ApiVersion); + WebApiClient client = new(url, Constants.ApiVersion); this.Monitor.Log("Checking for updates..."); // check SMAPI version @@ -1569,7 +1587,7 @@ namespace StardewModdingAPI.Framework // load mods IList skippedMods = new List(); - using (AssemblyLoader modAssemblyLoader = new AssemblyLoader(Constants.Platform, this.Monitor, this.Settings.ParanoidWarnings, this.Settings.RewriteMods)) + using (AssemblyLoader modAssemblyLoader = new(Constants.Platform, this.Monitor, this.Settings.ParanoidWarnings, this.Settings.RewriteMods)) { // init HashSet suppressUpdateChecks = new HashSet(this.Settings.SuppressUpdateChecks, StringComparer.OrdinalIgnoreCase); @@ -1758,7 +1776,7 @@ namespace StardewModdingAPI.Framework IManifest manifest = mod.Manifest; IMonitor monitor = this.LogManager.GetMonitor(mod.DisplayName); IContentHelper contentHelper = new ContentHelper(this.ContentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, monitor); - TranslationHelper translationHelper = new TranslationHelper(manifest.UniqueID, contentCore.GetLocale(), contentCore.Language); + TranslationHelper translationHelper = new(manifest.UniqueID, contentCore.GetLocale(), contentCore.Language); IContentPack contentPack = new ContentPack(mod.DirectoryPath, manifest, contentHelper, translationHelper, jsonHelper); mod.SetMod(contentPack, monitor, translationHelper); this.ModRegistry.Add(mod); @@ -1831,16 +1849,16 @@ namespace StardewModdingAPI.Framework // init mod helpers IMonitor monitor = this.LogManager.GetMonitor(mod.DisplayName); - TranslationHelper translationHelper = new TranslationHelper(manifest.UniqueID, contentCore.GetLocale(), contentCore.Language); + TranslationHelper translationHelper = new(manifest.UniqueID, contentCore.GetLocale(), contentCore.Language); IModHelper modHelper; { IContentPack CreateFakeContentPack(string packDirPath, IManifest packManifest) { IMonitor packMonitor = this.LogManager.GetMonitor(packManifest.Name); IContentHelper packContentHelper = new ContentHelper(contentCore, packDirPath, packManifest.UniqueID, packManifest.Name, packMonitor); - TranslationHelper packTranslationHelper = new TranslationHelper(packManifest.UniqueID, contentCore.GetLocale(), contentCore.Language); + TranslationHelper packTranslationHelper = new(packManifest.UniqueID, contentCore.GetLocale(), contentCore.Language); - ContentPack contentPack = new ContentPack(packDirPath, packManifest, packContentHelper, packTranslationHelper, this.Toolkit.JsonHelper); + ContentPack contentPack = new(packDirPath, packManifest, packContentHelper, packTranslationHelper, this.Toolkit.JsonHelper); this.ReloadTranslationsForTemporaryContentPack(mod, contentPack); mod.FakeContentPacks.Add(new WeakReference(contentPack)); return contentPack; @@ -1982,7 +2000,7 @@ namespace StardewModdingAPI.Framework // read translation files var translations = new Dictionary>(); errors = new List(); - DirectoryInfo translationsDir = new DirectoryInfo(folderPath); + DirectoryInfo translationsDir = new(folderPath); if (translationsDir.Exists) { foreach (FileInfo file in translationsDir.EnumerateFiles("*.json")) @@ -2038,7 +2056,7 @@ namespace StardewModdingAPI.Framework { // default path { - FileInfo defaultFile = new FileInfo(Path.Combine(Constants.LogDir, $"{Constants.LogFilename}.{Constants.LogExtension}")); + FileInfo defaultFile = new(Path.Combine(Constants.LogDir, $"{Constants.LogFilename}.{Constants.LogExtension}")); if (!defaultFile.Exists) return defaultFile.FullName; } @@ -2046,7 +2064,7 @@ namespace StardewModdingAPI.Framework // get first disambiguated path for (int i = 2; i < int.MaxValue; i++) { - FileInfo file = new FileInfo(Path.Combine(Constants.LogDir, $"{Constants.LogFilename}.player-{i}.{Constants.LogExtension}")); + FileInfo file = new(Path.Combine(Constants.LogDir, $"{Constants.LogFilename}.player-{i}.{Constants.LogExtension}")); if (!file.Exists) return file.FullName; } @@ -2058,7 +2076,7 @@ namespace StardewModdingAPI.Framework /// Delete normal (non-crash) log files created by SMAPI. private void PurgeNormalLogs() { - DirectoryInfo logsDir = new DirectoryInfo(Constants.LogDir); + DirectoryInfo logsDir = new(Constants.LogDir); if (!logsDir.Exists) return; -- cgit From a593eda30f82af474887d91458b0e9158f66fefc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 6 Apr 2022 18:24:59 -0400 Subject: use target-typed new --- src/SMAPI.Installer/Framework/InstallerContext.cs | 2 +- src/SMAPI.Installer/InteractiveInstaller.cs | 18 ++++++------ src/SMAPI.Installer/Program.cs | 6 ++-- src/SMAPI.Internal.Patching/HarmonyPatcher.cs | 2 +- src/SMAPI.Internal.Patching/PatchHelper.cs | 2 +- .../Mock/StardewValley/Item.cs | 16 +++++------ .../Mock/StardewValley/Object.cs | 2 +- .../NetFieldAnalyzerTests.cs | 4 +-- .../ObsoleteFieldAnalyzerTests.cs | 2 +- .../NetFieldAnalyzer.cs | 4 +-- .../ObsoleteFieldAnalyzer.cs | 2 +- src/SMAPI.ModBuildConfig/DeployModTask.cs | 4 +-- .../Framework/ModFileManager.cs | 8 +++--- .../Framework/Commands/Player/AddCommand.cs | 2 +- .../Commands/Player/ListItemTypesCommand.cs | 2 +- .../Framework/Commands/Player/ListItemsCommand.cs | 2 +- .../Framework/ItemRepository.cs | 2 +- src/SMAPI.Mods.ConsoleCommands/ModEntry.cs | 2 +- .../Patches/SaveGamePatcher.cs | 2 +- src/SMAPI.Mods.SaveBackup/ModEntry.cs | 12 ++++---- src/SMAPI.Tests/Core/TranslationTests.cs | 6 ++-- src/SMAPI.Tests/Sample.cs | 2 +- src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs | 22 +++++++-------- src/SMAPI.Tests/Utilities/SDateTests.cs | 4 +-- src/SMAPI.Tests/Utilities/SemanticVersionTests.cs | 2 +- .../Framework/Clients/WebApi/WebApiClient.cs | 4 +-- .../Framework/Clients/Wiki/WikiClient.cs | 2 +- .../Framework/GameScanning/GameScanner.cs | 4 +-- .../Framework/LowLevelEnvironmentUtility.cs | 2 +- .../Framework/ModData/ModDataRecord.cs | 2 +- .../Framework/ModScanning/ModScanner.cs | 12 ++++---- src/SMAPI.Toolkit/ModToolkit.cs | 2 +- src/SMAPI.Toolkit/SemanticVersionComparer.cs | 2 +- src/SMAPI.Toolkit/Serialization/JsonHelper.cs | 2 +- src/SMAPI.Toolkit/Utilities/PathUtilities.cs | 4 +-- src/SMAPI.Web/Controllers/IndexController.cs | 2 +- .../Controllers/JsonValidatorController.cs | 2 +- src/SMAPI.Web/Controllers/ModsApiController.cs | 2 +- .../Clients/Chucklefish/ChucklefishClient.cs | 2 +- .../Clients/CurseForge/CurseForgeClient.cs | 2 +- .../Framework/Clients/Nexus/NexusClient.cs | 2 +- src/SMAPI.Web/Framework/Compression/GzipHelper.cs | 8 +++--- src/SMAPI.Web/Framework/Extensions.cs | 4 +-- .../Framework/JobDashboardAuthorizationFilter.cs | 2 +- .../Framework/LogParsing/LogMessageBuilder.cs | 2 +- src/SMAPI.Web/Framework/LogParsing/LogParser.cs | 32 +++++++++++----------- src/SMAPI.Web/Framework/Storage/StorageProvider.cs | 4 +-- src/SMAPI.Web/ViewModels/LogParserModel.cs | 2 +- src/SMAPI/Context.cs | 4 +-- src/SMAPI/Framework/CommandManager.cs | 2 +- .../Framework/Commands/HarmonySummaryCommand.cs | 2 +- src/SMAPI/Framework/Content/AssetDataForMap.cs | 4 +-- src/SMAPI/Framework/DeprecationManager.cs | 2 +- src/SMAPI/Framework/Events/ManagedEvent.cs | 2 +- src/SMAPI/Framework/Input/KeyboardStateBuilder.cs | 2 +- src/SMAPI/Framework/Input/SInputState.cs | 10 +++---- src/SMAPI/Framework/ModLoading/AssemblyLoader.cs | 16 +++++------ src/SMAPI/Framework/ModLoading/ModResolver.cs | 2 +- .../ModLoading/Symbols/SymbolReaderProvider.cs | 2 +- src/SMAPI/Framework/ModRegistry.cs | 4 +-- src/SMAPI/Framework/Models/SConfig.cs | 2 +- src/SMAPI/Framework/Networking/SGalaxyNetServer.cs | 6 ++-- src/SMAPI/Framework/Networking/SLidgrenServer.cs | 4 +-- src/SMAPI/Framework/Reflection/Reflector.cs | 4 +-- src/SMAPI/Framework/SGame.cs | 8 +++--- src/SMAPI/Framework/SGameRunner.cs | 2 +- src/SMAPI/Framework/SMultiplayer.cs | 18 ++++++------ src/SMAPI/Framework/Singleton.cs | 2 +- src/SMAPI/Framework/SnapshotListDiff.cs | 4 +-- src/SMAPI/Framework/StateTracking/ChestTracker.cs | 4 +-- .../FieldWatchers/ComparableListWatcher.cs | 4 +-- .../FieldWatchers/ImmutableCollectionWatcher.cs | 2 +- .../FieldWatchers/NetCollectionWatcher.cs | 4 +-- .../FieldWatchers/ObservableCollectionWatcher.cs | 6 ++-- .../Framework/StateTracking/LocationTracker.cs | 2 +- src/SMAPI/Framework/StateTracking/PlayerTracker.cs | 2 +- .../StateTracking/Snapshots/LocationSnapshot.cs | 14 +++++----- .../StateTracking/Snapshots/PlayerSnapshot.cs | 4 +-- .../StateTracking/Snapshots/WatcherSnapshot.cs | 16 +++++------ .../Snapshots/WorldLocationsSnapshot.cs | 4 +-- .../Framework/TemporaryHacks/MiniMonoModHotfix.cs | 4 +-- src/SMAPI/Framework/WatcherCore.cs | 2 +- src/SMAPI/Program.cs | 2 +- 83 files changed, 203 insertions(+), 203 deletions(-) (limited to 'src/SMAPI/Framework/Events') diff --git a/src/SMAPI.Installer/Framework/InstallerContext.cs b/src/SMAPI.Installer/Framework/InstallerContext.cs index bb973230..abc7adde 100644 --- a/src/SMAPI.Installer/Framework/InstallerContext.cs +++ b/src/SMAPI.Installer/Framework/InstallerContext.cs @@ -12,7 +12,7 @@ namespace StardewModdingAPI.Installer.Framework ** Fields *********/ /// The underlying toolkit game scanner. - private readonly GameScanner GameScanner = new GameScanner(); + private readonly GameScanner GameScanner = new(); /********* diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs index b3bba883..a126c3b8 100644 --- a/src/SMAPI.Installer/InteractiveInstaller.cs +++ b/src/SMAPI.Installer/InteractiveInstaller.cs @@ -126,7 +126,7 @@ namespace StardewModdingApi.Installer /**** ** Get basic info & set window title ****/ - ModToolkit toolkit = new ModToolkit(); + ModToolkit toolkit = new(); var context = new InstallerContext(); Console.Title = $"SMAPI {context.GetInstallerVersion()} installer on {context.Platform} {context.PlatformName}"; Console.WriteLine(); @@ -246,7 +246,7 @@ namespace StardewModdingApi.Installer } // get folders - DirectoryInfo bundleDir = new DirectoryInfo(this.BundlePath); + DirectoryInfo bundleDir = new(this.BundlePath); paths = new InstallerPaths(bundleDir, installDir); } @@ -354,8 +354,8 @@ namespace StardewModdingApi.Installer // move global save data folder (changed in 3.2) { string dataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley"); - DirectoryInfo oldDir = new DirectoryInfo(Path.Combine(dataPath, "Saves", ".smapi")); - DirectoryInfo newDir = new DirectoryInfo(Path.Combine(dataPath, ".smapi")); + DirectoryInfo oldDir = new(Path.Combine(dataPath, "Saves", ".smapi")); + DirectoryInfo newDir = new(Path.Combine(dataPath, ".smapi")); if (oldDir.Exists) { @@ -428,7 +428,7 @@ namespace StardewModdingApi.Installer } // add or replace bundled mods - DirectoryInfo bundledModsDir = new DirectoryInfo(Path.Combine(paths.BundlePath, "Mods")); + DirectoryInfo bundledModsDir = new(Path.Combine(paths.BundlePath, "Mods")); if (bundledModsDir.Exists && bundledModsDir.EnumerateDirectories().Any()) { this.PrintDebug("Adding bundled mods..."); @@ -450,7 +450,7 @@ namespace StardewModdingApi.Installer // find target folder ModFolder targetMod = targetMods.FirstOrDefault(p => p.Manifest?.UniqueID?.Equals(sourceMod.Manifest.UniqueID, StringComparison.OrdinalIgnoreCase) == true); - DirectoryInfo defaultTargetFolder = new DirectoryInfo(Path.Combine(paths.ModsPath, sourceMod.Directory.Name)); + DirectoryInfo defaultTargetFolder = new(Path.Combine(paths.ModsPath, sourceMod.Directory.Name)); DirectoryInfo targetFolder = targetMod?.Directory ?? defaultTargetFolder; this.PrintDebug(targetFolder.FullName == defaultTargetFolder.FullName ? $" adding {sourceMod.Manifest.Name}..." @@ -593,7 +593,7 @@ namespace StardewModdingApi.Installer break; case DirectoryInfo sourceDir: - DirectoryInfo targetSubfolder = new DirectoryInfo(Path.Combine(targetFolder.FullName, sourceDir.Name)); + DirectoryInfo targetSubfolder = new(Path.Combine(targetFolder.FullName, sourceDir.Name)); foreach (var entry in sourceDir.EnumerateFileSystemInfos()) this.RecursiveCopy(entry, targetSubfolder, filter); break; @@ -717,7 +717,7 @@ namespace StardewModdingApi.Installer // get directory if (File.Exists(path)) path = Path.GetDirectoryName(path); - DirectoryInfo directory = new DirectoryInfo(path); + DirectoryInfo directory = new(path); // validate path if (!directory.Exists) @@ -795,7 +795,7 @@ namespace StardewModdingApi.Installer // get path string appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley"); - DirectoryInfo modDir = new DirectoryInfo(Path.Combine(appDataPath, "Mods")); + DirectoryInfo modDir = new(Path.Combine(appDataPath, "Mods")); // check if migration needed if (!modDir.Exists) diff --git a/src/SMAPI.Installer/Program.cs b/src/SMAPI.Installer/Program.cs index 45cfea75..2c9b2c0a 100644 --- a/src/SMAPI.Installer/Program.cs +++ b/src/SMAPI.Installer/Program.cs @@ -31,7 +31,7 @@ namespace StardewModdingApi.Installer public static void Main(string[] args) { // find install bundle - FileInfo zipFile = new FileInfo(Path.Combine(Program.InstallerPath, "install.dat")); + FileInfo zipFile = new(Path.Combine(Program.InstallerPath, "install.dat")); if (!zipFile.Exists) { Console.WriteLine($"Oops! Some of the installer files are missing; try re-downloading the installer. (Missing file: {zipFile.FullName})"); @@ -40,7 +40,7 @@ namespace StardewModdingApi.Installer } // unzip bundle into temp folder - DirectoryInfo bundleDir = new DirectoryInfo(Program.ExtractedBundlePath); + DirectoryInfo bundleDir = new(Program.ExtractedBundlePath); Console.WriteLine("Extracting install files..."); ZipFile.ExtractToDirectory(zipFile.FullName, bundleDir.FullName); @@ -70,7 +70,7 @@ namespace StardewModdingApi.Installer { try { - AssemblyName name = new AssemblyName(e.Name); + AssemblyName name = new(e.Name); foreach (FileInfo dll in new DirectoryInfo(Program.InternalFilesPath).EnumerateFiles("*.dll")) { if (name.Name.Equals(AssemblyName.GetAssemblyName(dll.FullName).Name, StringComparison.OrdinalIgnoreCase)) diff --git a/src/SMAPI.Internal.Patching/HarmonyPatcher.cs b/src/SMAPI.Internal.Patching/HarmonyPatcher.cs index c07e3b41..6f30c241 100644 --- a/src/SMAPI.Internal.Patching/HarmonyPatcher.cs +++ b/src/SMAPI.Internal.Patching/HarmonyPatcher.cs @@ -15,7 +15,7 @@ namespace StardewModdingAPI.Internal.Patching /// The patchers to apply. public static Harmony Apply(string id, IMonitor monitor, params IPatcher[] patchers) { - Harmony harmony = new Harmony(id); + Harmony harmony = new(id); foreach (IPatcher patcher in patchers) { diff --git a/src/SMAPI.Internal.Patching/PatchHelper.cs b/src/SMAPI.Internal.Patching/PatchHelper.cs index fc79ddf2..c9758616 100644 --- a/src/SMAPI.Internal.Patching/PatchHelper.cs +++ b/src/SMAPI.Internal.Patching/PatchHelper.cs @@ -43,7 +43,7 @@ namespace StardewModdingAPI.Internal.Patching /// The method generic types, or null if it's not generic. public static string GetMethodString(Type type, string name, Type[] parameters = null, Type[] generics = null) { - StringBuilder str = new StringBuilder(); + StringBuilder str = new(); // type str.Append(type.FullName); diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Item.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Item.cs index 1b6317c1..e8da92fa 100644 --- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Item.cs +++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Item.cs @@ -7,27 +7,27 @@ namespace StardewValley public class Item { /// A net int field with an equivalent non-net Category property. - public readonly NetInt category = new NetInt { Value = 42 }; + public readonly NetInt category = new() { Value = 42 }; /// A generic net int field with no equivalent non-net property. - public readonly NetInt netIntField = new NetInt { Value = 42 }; + public readonly NetInt netIntField = new() { Value = 42 }; /// A generic net ref field with no equivalent non-net property. - public readonly NetRef netRefField = new NetRef(); + public readonly NetRef netRefField = new(); /// A generic net int property with no equivalent non-net property. - public NetInt netIntProperty = new NetInt { Value = 42 }; + public NetInt netIntProperty = new() { Value = 42 }; /// A generic net ref property with no equivalent non-net property. - public NetRef netRefProperty { get; } = new NetRef(); + public NetRef netRefProperty { get; } = new(); /// A sample net list. - public readonly NetList netList = new NetList(); + public readonly NetList netList = new(); /// A sample net object list. - public readonly NetObjectList netObjectList = new NetObjectList(); + public readonly NetObjectList netObjectList = new(); /// A sample net collection. - public readonly NetCollection netCollection = new NetCollection(); + public readonly NetCollection netCollection = new(); } } diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Object.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Object.cs index 3dd66a6d..151010a7 100644 --- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Object.cs +++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Object.cs @@ -7,6 +7,6 @@ namespace StardewValley public class Object : Item { /// A net int field with an equivalent non-net property. - public NetInt type = new NetInt { Value = 42 }; + public NetInt type = new() { Value = 42 }; } } diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs index 89bd1be5..f11a59d3 100644 --- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs +++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs @@ -93,7 +93,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests { // arrange string code = NetFieldAnalyzerTests.SampleProgram.Replace("{{test-code}}", codeText); - DiagnosticResult expected = new DiagnosticResult + DiagnosticResult expected = new() { Id = "AvoidImplicitNetFieldCast", Message = $"This implicitly converts '{expression}' from {fromType} to {toType}, but {fromType} has unintuitive implicit conversion rules. Consider comparing against the actual value instead to avoid bugs. See https://smapi.io/package/avoid-implicit-net-field-cast for details.", @@ -135,7 +135,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests { // arrange string code = NetFieldAnalyzerTests.SampleProgram.Replace("{{test-code}}", codeText); - DiagnosticResult expected = new DiagnosticResult + DiagnosticResult expected = new() { Id = "AvoidNetField", Message = $"'{expression}' is a {netType} field; consider using the {suggestedProperty} property instead. See https://smapi.io/package/avoid-net-field for details.", diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs index 12641e1a..76607b8e 100644 --- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs +++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs @@ -64,7 +64,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests { // arrange string code = ObsoleteFieldAnalyzerTests.SampleProgram.Replace("{{test-code}}", codeText); - DiagnosticResult expected = new DiagnosticResult + DiagnosticResult expected = new() { Id = "AvoidObsoleteField", Message = $"The '{oldName}' field is obsolete and should be replaced with '{newName}'. See https://smapi.io/package/avoid-obsolete-field for details.", diff --git a/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs b/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs index e03c72de..8478dc54 100644 --- a/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs +++ b/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs @@ -132,7 +132,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer }; /// The diagnostic info for an implicit net field cast. - private readonly DiagnosticDescriptor AvoidImplicitNetFieldCastRule = new DiagnosticDescriptor( + private readonly DiagnosticDescriptor AvoidImplicitNetFieldCastRule = new( id: "AvoidImplicitNetFieldCast", title: "Netcode types shouldn't be implicitly converted", messageFormat: "This implicitly converts '{0}' from {1} to {2}, but {1} has unintuitive implicit conversion rules. Consider comparing against the actual value instead to avoid bugs. See https://smapi.io/package/avoid-implicit-net-field-cast for details.", @@ -143,7 +143,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer ); /// The diagnostic info for an avoidable net field access. - private readonly DiagnosticDescriptor AvoidNetFieldRule = new DiagnosticDescriptor( + private readonly DiagnosticDescriptor AvoidNetFieldRule = new( id: "AvoidNetField", title: "Avoid Netcode types when possible", messageFormat: "'{0}' is a {1} field; consider using the {2} property instead. See https://smapi.io/package/avoid-net-field for details.", diff --git a/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs b/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs index 722d5227..3184147a 100644 --- a/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs +++ b/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs @@ -24,7 +24,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer /// Describes the diagnostic rule covered by the analyzer. private readonly IDictionary Rules = new Dictionary { - ["AvoidObsoleteField"] = new DiagnosticDescriptor( + ["AvoidObsoleteField"] = new( id: "AvoidObsoleteField", title: "Reference to obsolete field", messageFormat: "The '{0}' field is obsolete and should be replaced with '{1}'. See https://smapi.io/package/avoid-obsolete-field for details.", diff --git a/src/SMAPI.ModBuildConfig/DeployModTask.cs b/src/SMAPI.ModBuildConfig/DeployModTask.cs index 140933bd..c7026ee1 100644 --- a/src/SMAPI.ModBuildConfig/DeployModTask.cs +++ b/src/SMAPI.ModBuildConfig/DeployModTask.cs @@ -88,7 +88,7 @@ namespace StardewModdingAPI.ModBuildConfig Regex[] ignoreFilePatterns = this.GetCustomIgnorePatterns().ToArray(); // get mod info - ModFileManager package = new ModFileManager(this.ProjectDir, this.TargetDir, ignoreFilePaths, ignoreFilePatterns, bundleAssemblyTypes, this.ModDllName, validateRequiredModFiles: this.EnableModDeploy || this.EnableModZip); + ModFileManager package = new(this.ProjectDir, this.TargetDir, ignoreFilePaths, ignoreFilePatterns, bundleAssemblyTypes, this.ModDllName, validateRequiredModFiles: this.EnableModDeploy || this.EnableModZip); // deploy mod files if (this.EnableModDeploy) @@ -246,7 +246,7 @@ namespace StardewModdingAPI.ModBuildConfig // create zip file Directory.CreateDirectory(Path.GetDirectoryName(zipPath)!); using Stream zipStream = new FileStream(zipPath, FileMode.Create, FileAccess.Write); - using ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create); + using ZipArchive archive = new(zipStream, ZipArchiveMode.Create); foreach (var fileEntry in files) { diff --git a/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs b/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs index ad4ffdf9..80955f67 100644 --- a/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs +++ b/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs @@ -136,7 +136,7 @@ namespace StardewModdingAPI.ModBuildConfig.Framework // project manifest bool hasProjectManifest = false; { - FileInfo manifest = new FileInfo(Path.Combine(projectDir, this.ManifestFileName)); + FileInfo manifest = new(Path.Combine(projectDir, this.ManifestFileName)); if (manifest.Exists) { yield return Tuple.Create(this.ManifestFileName, manifest); @@ -146,7 +146,7 @@ namespace StardewModdingAPI.ModBuildConfig.Framework // project i18n files bool hasProjectTranslations = false; - DirectoryInfo translationsFolder = new DirectoryInfo(Path.Combine(projectDir, "i18n")); + DirectoryInfo translationsFolder = new(Path.Combine(projectDir, "i18n")); if (translationsFolder.Exists) { foreach (FileInfo file in translationsFolder.EnumerateFiles()) @@ -156,7 +156,7 @@ namespace StardewModdingAPI.ModBuildConfig.Framework // project assets folder bool hasAssetsFolder = false; - DirectoryInfo assetsFolder = new DirectoryInfo(Path.Combine(projectDir, "assets")); + DirectoryInfo assetsFolder = new(Path.Combine(projectDir, "assets")); if (assetsFolder.Exists) { foreach (FileInfo file in assetsFolder.EnumerateFiles("*", SearchOption.AllDirectories)) @@ -168,7 +168,7 @@ namespace StardewModdingAPI.ModBuildConfig.Framework } // build output - DirectoryInfo buildFolder = new DirectoryInfo(targetDir); + DirectoryInfo buildFolder = new(targetDir); foreach (FileInfo file in buildFolder.EnumerateFiles("*", SearchOption.AllDirectories)) { // get path info diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs index 0e8f7517..fae31c23 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs @@ -13,7 +13,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player ** Fields *********/ /// Provides methods for searching and constructing items. - private readonly ItemRepository Items = new ItemRepository(); + private readonly ItemRepository Items = new(); /// The type names recognized by this command. private readonly string[] ValidTypes = Enum.GetNames(typeof(ItemType)).Concat(new[] { "Name" }).ToArray(); diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs index 1f12e5f9..af362bcd 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs @@ -10,7 +10,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player ** Fields *********/ /// Provides methods for searching and constructing items. - private readonly ItemRepository Items = new ItemRepository(); + private readonly ItemRepository Items = new(); /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs index 67569298..46fc1d9d 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs @@ -12,7 +12,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player ** Fields *********/ /// Provides methods for searching and constructing items. - private readonly ItemRepository Items = new ItemRepository(); + private readonly ItemRepository Items = new(); /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs index 8704a403..9b48fa99 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs @@ -285,7 +285,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework case SObject.flowersCategory: yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 5 + id, _ => { - SObject honey = new SObject(Vector2.Zero, 340, $"{item.Name} Honey", false, true, false, false) + SObject honey = new(Vector2.Zero, 340, $"{item.Name} Honey", false, true, false, false) { Name = $"{item.Name} Honey", preservedParentSheetIndex = { id } diff --git a/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs b/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs index 91437fd3..5e594984 100644 --- a/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs +++ b/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs @@ -71,7 +71,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands /// The command arguments. private void HandleCommand(IConsoleCommand command, string commandName, string[] args) { - ArgumentParser argParser = new ArgumentParser(commandName, args, this.Monitor); + ArgumentParser argParser = new(commandName, args, this.Monitor); command.Handle(this.Monitor, commandName, argParser); } diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/SaveGamePatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/SaveGamePatcher.cs index 0a7ed212..5f6dbcb3 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/SaveGamePatcher.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/SaveGamePatcher.cs @@ -121,7 +121,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches { try { - BluePrint _ = new BluePrint(building.buildingType.Value); + BluePrint _ = new(building.buildingType.Value); } catch (ContentLoadException) { diff --git a/src/SMAPI.Mods.SaveBackup/ModEntry.cs b/src/SMAPI.Mods.SaveBackup/ModEntry.cs index b89bb9c3..f6925707 100644 --- a/src/SMAPI.Mods.SaveBackup/ModEntry.cs +++ b/src/SMAPI.Mods.SaveBackup/ModEntry.cs @@ -38,7 +38,7 @@ namespace StardewModdingAPI.Mods.SaveBackup try { // init backup folder - DirectoryInfo backupFolder = new DirectoryInfo(this.BackupFolder); + DirectoryInfo backupFolder = new(this.BackupFolder); backupFolder.Create(); // back up & prune saves @@ -63,8 +63,8 @@ namespace StardewModdingAPI.Mods.SaveBackup try { // get target path - FileInfo targetFile = new FileInfo(Path.Combine(backupFolder.FullName, this.FileName)); - DirectoryInfo fallbackDir = new DirectoryInfo(Path.Combine(backupFolder.FullName, this.BackupLabel)); + FileInfo targetFile = new(Path.Combine(backupFolder.FullName, this.FileName)); + DirectoryInfo fallbackDir = new(Path.Combine(backupFolder.FullName, this.BackupLabel)); if (targetFile.Exists || fallbackDir.Exists) { this.Monitor.Log("Already backed up today."); @@ -72,7 +72,7 @@ namespace StardewModdingAPI.Mods.SaveBackup } // copy saves to fallback directory (ignore non-save files/folders) - DirectoryInfo savesDir = new DirectoryInfo(Constants.SavesPath); + DirectoryInfo savesDir = new(Constants.SavesPath); if (!this.RecursiveCopy(savesDir, fallbackDir, entry => this.MatchSaveFolders(savesDir, entry), copyRoot: false)) { this.Monitor.Log("No saves found."); @@ -190,8 +190,8 @@ namespace StardewModdingAPI.Mods.SaveBackup /// The destination file to create. private void CompressUsingMacProcess(string sourcePath, FileInfo destination) { - DirectoryInfo saveFolder = new DirectoryInfo(sourcePath); - ProcessStartInfo startInfo = new ProcessStartInfo + DirectoryInfo saveFolder = new(sourcePath); + ProcessStartInfo startInfo = new() { FileName = "zip", Arguments = $"-rq \"{destination.FullName}\" \"{saveFolder.Name}\" -x \"*.DS_Store\" -x \"__MACOSX\"", diff --git a/src/SMAPI.Tests/Core/TranslationTests.cs b/src/SMAPI.Tests/Core/TranslationTests.cs index 457f9fad..58bc59b1 100644 --- a/src/SMAPI.Tests/Core/TranslationTests.cs +++ b/src/SMAPI.Tests/Core/TranslationTests.cs @@ -116,7 +116,7 @@ namespace SMAPI.Tests.Core public void Translation_ToString([ValueSource(nameof(TranslationTests.Samples))] string text) { // act - Translation translation = new Translation("pt-BR", "key", text); + Translation translation = new("pt-BR", "key", text); // assert if (translation.HasValue()) @@ -129,7 +129,7 @@ namespace SMAPI.Tests.Core public void Translation_ImplicitStringConversion([ValueSource(nameof(TranslationTests.Samples))] string text) { // act - Translation translation = new Translation("pt-BR", "key", text); + Translation translation = new("pt-BR", "key", text); // assert if (translation.HasValue()) @@ -182,7 +182,7 @@ namespace SMAPI.Tests.Core string expected = $"{start} tokens are properly replaced (including {middle} {middle}) {end}"; // act - Translation translation = new Translation("pt-BR", "key", input); + Translation translation = new("pt-BR", "key", input); switch (structure) { case "anonymous object": diff --git a/src/SMAPI.Tests/Sample.cs b/src/SMAPI.Tests/Sample.cs index f4f0d88e..9587a100 100644 --- a/src/SMAPI.Tests/Sample.cs +++ b/src/SMAPI.Tests/Sample.cs @@ -9,7 +9,7 @@ namespace SMAPI.Tests ** Fields *********/ /// A random number generator. - private static readonly Random Random = new Random(); + private static readonly Random Random = new(); /********* diff --git a/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs b/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs index ab4c2618..94819c2e 100644 --- a/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs +++ b/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs @@ -14,7 +14,7 @@ namespace SMAPI.Tests.Utilities /// Sample paths used in unit tests. public static readonly SamplePath[] SamplePaths = { // Windows absolute path - new SamplePath + new() { OriginalPath = @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley", @@ -26,7 +26,7 @@ namespace SMAPI.Tests.Utilities }, // Windows absolute path (with trailing slash) - new SamplePath + new() { OriginalPath = @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley\", @@ -38,7 +38,7 @@ namespace SMAPI.Tests.Utilities }, // Windows relative path - new SamplePath + new() { OriginalPath = @"Content\Characters\Dialogue\Abigail", @@ -50,7 +50,7 @@ namespace SMAPI.Tests.Utilities }, // Windows relative path (with directory climbing) - new SamplePath + new() { OriginalPath = @"..\..\Content", @@ -62,7 +62,7 @@ namespace SMAPI.Tests.Utilities }, // Windows UNC path - new SamplePath + new() { OriginalPath = @"\\unc\path", @@ -74,7 +74,7 @@ namespace SMAPI.Tests.Utilities }, // Linux absolute path - new SamplePath + new() { OriginalPath = @"/home/.steam/steam/steamapps/common/Stardew Valley", @@ -86,7 +86,7 @@ namespace SMAPI.Tests.Utilities }, // Linux absolute path (with trailing slash) - new SamplePath + new() { OriginalPath = @"/home/.steam/steam/steamapps/common/Stardew Valley/", @@ -98,7 +98,7 @@ namespace SMAPI.Tests.Utilities }, // Linux absolute path (with ~) - new SamplePath + new() { OriginalPath = @"~/.steam/steam/steamapps/common/Stardew Valley", @@ -110,7 +110,7 @@ namespace SMAPI.Tests.Utilities }, // Linux relative path - new SamplePath + new() { OriginalPath = @"Content/Characters/Dialogue/Abigail", @@ -122,7 +122,7 @@ namespace SMAPI.Tests.Utilities }, // Linux relative path (with directory climbing) - new SamplePath + new() { OriginalPath = @"../../Content", @@ -134,7 +134,7 @@ namespace SMAPI.Tests.Utilities }, // Mixed directory separators - new SamplePath + new() { OriginalPath = @"C:\some/mixed\path/separators", diff --git a/src/SMAPI.Tests/Utilities/SDateTests.cs b/src/SMAPI.Tests/Utilities/SDateTests.cs index 374f4921..886f25cd 100644 --- a/src/SMAPI.Tests/Utilities/SDateTests.cs +++ b/src/SMAPI.Tests/Utilities/SDateTests.cs @@ -61,7 +61,7 @@ namespace SMAPI.Tests.Utilities public void Constructor_SetsExpectedValues([ValueSource(nameof(SDateTests.SampleSeasonValues))] string season, [ValueSource(nameof(SDateTests.ValidDays))] int day, [Values(1, 2, 100)] int year) { // act - SDate date = new SDate(day, season, year); + SDate date = new(day, season, year); // assert Assert.AreEqual(day, date.Day); @@ -254,7 +254,7 @@ namespace SMAPI.Tests.Utilities { foreach (int day in SDateTests.ValidDays) { - SDate date = new SDate(day, season, year); + SDate date = new(day, season, year); int hash = date.GetHashCode(); if (hashes.TryGetValue(hash, out SDate otherDate)) Assert.Fail($"Received identical hash code {hash} for dates {otherDate} and {date}."); diff --git a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs index ac4ef39b..c8270373 100644 --- a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs +++ b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs @@ -395,7 +395,7 @@ namespace SMAPI.Tests.Utilities public void GameVersion(string versionStr) { // act - GameVersion version = new GameVersion(versionStr); + GameVersion version = new(versionStr); // assert Assert.AreEqual(versionStr, version.ToString(), "The game version did not round-trip to the same value."); diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs index c2d906a0..f7d26d21 100644 --- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs +++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs @@ -62,9 +62,9 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi private TResult Post(string url, TBody content) { // note: avoid HttpClient for macOS compatibility - using WebClient client = new WebClient(); + using WebClient client = new(); - Uri fullUrl = new Uri(this.BaseUrl, url); + Uri fullUrl = new(this.BaseUrl, url); string data = JsonConvert.SerializeObject(content); client.Headers["Content-Type"] = "application/json"; diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs index 0f5a0ec3..abbcdc81 100644 --- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs +++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs @@ -127,7 +127,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki string pullRequestUrl = this.GetAttribute(node, "data-pr"); // parse stable compatibility - WikiCompatibilityInfo compatibility = new WikiCompatibilityInfo + WikiCompatibilityInfo compatibility = new() { Status = this.GetAttributeAsEnum(node, "data-status") ?? WikiCompatibilityStatus.Ok, BrokeIn = this.GetAttribute(node, "data-broke-in"), diff --git a/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs b/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs index 8d4198de..768beba1 100644 --- a/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs +++ b/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs @@ -45,7 +45,7 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning // yield valid folders foreach (string path in paths) { - DirectoryInfo folder = new DirectoryInfo(path); + DirectoryInfo folder = new(path); if (this.LooksLikeGameFolder(folder)) yield return folder; } @@ -191,7 +191,7 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning yield break; // get targets file - FileInfo file = new FileInfo(Path.Combine(homePath, "stardewvalley.targets")); + FileInfo file = new(Path.Combine(homePath, "stardewvalley.targets")); if (!file.Exists) yield break; diff --git a/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs b/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs index 8b6eb5fb..c0332331 100644 --- a/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs +++ b/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs @@ -98,7 +98,7 @@ namespace StardewModdingAPI.Toolkit.Framework /// private static bool IsRunningAndroid() { - using Process process = new Process + using Process process = new() { StartInfo = { diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs index 5dd32acf..7e07ffde 100644 --- a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs +++ b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs @@ -82,7 +82,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData /// The manifest to match. public ModDataRecordVersionedFields GetVersionedFields(IManifest manifest) { - ModDataRecordVersionedFields parsed = new ModDataRecordVersionedFields { DisplayName = this.DisplayName, DataRecord = this }; + ModDataRecordVersionedFields parsed = new() { DisplayName = this.DisplayName, DataRecord = this }; foreach (ModDataField field in this.Fields.Where(field => field.IsMatch(manifest))) { switch (field.Key) diff --git a/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs b/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs index e6105f9c..d21ccec0 100644 --- a/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs +++ b/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs @@ -18,7 +18,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning private readonly JsonHelper JsonHelper; /// A list of filesystem entry names to ignore when checking whether a folder should be treated as a mod. - private readonly HashSet IgnoreFilesystemNames = new HashSet + private readonly HashSet IgnoreFilesystemNames = new() { new Regex(@"^__folder_managed_by_vortex$", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Vortex mod manager new Regex(@"(?:^\._|^\.DS_Store$|^__MACOSX$|^mcs$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), // macOS @@ -26,7 +26,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning }; /// A list of file extensions to ignore when searching for mod files. - private readonly HashSet IgnoreFileExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) + private readonly HashSet IgnoreFileExtensions = new(StringComparer.OrdinalIgnoreCase) { // text ".doc", @@ -60,7 +60,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning }; /// The extensions for packed content files. - private readonly HashSet StrictXnbModExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) + private readonly HashSet StrictXnbModExtensions = new(StringComparer.OrdinalIgnoreCase) { ".xgs", ".xnb", @@ -69,7 +69,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning }; /// The extensions for files which an XNB mod may contain, in addition to . - private readonly HashSet PotentialXnbModExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) + private readonly HashSet PotentialXnbModExtensions = new(StringComparer.OrdinalIgnoreCase) { ".json", ".yaml" @@ -96,7 +96,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning /// The root folder containing mods. public IEnumerable GetModFolders(string rootPath) { - DirectoryInfo root = new DirectoryInfo(rootPath); + DirectoryInfo root = new(rootPath); return this.GetModFolders(root, root); } @@ -260,7 +260,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning while (true) { // check for manifest in current folder - FileInfo file = new FileInfo(Path.Combine(folder.FullName, "manifest.json")); + FileInfo file = new(Path.Combine(folder.FullName, "manifest.json")); if (file.Exists) return file; diff --git a/src/SMAPI.Toolkit/ModToolkit.cs b/src/SMAPI.Toolkit/ModToolkit.cs index 38a67ae5..80008df7 100644 --- a/src/SMAPI.Toolkit/ModToolkit.cs +++ b/src/SMAPI.Toolkit/ModToolkit.cs @@ -34,7 +34,7 @@ namespace StardewModdingAPI.Toolkit ** Accessors *********/ /// Encapsulates SMAPI's JSON parsing. - public JsonHelper JsonHelper { get; } = new JsonHelper(); + public JsonHelper JsonHelper { get; } = new(); /********* diff --git a/src/SMAPI.Toolkit/SemanticVersionComparer.cs b/src/SMAPI.Toolkit/SemanticVersionComparer.cs index 9f6b57a2..8eba2c9f 100644 --- a/src/SMAPI.Toolkit/SemanticVersionComparer.cs +++ b/src/SMAPI.Toolkit/SemanticVersionComparer.cs @@ -9,7 +9,7 @@ namespace StardewModdingAPI.Toolkit ** Accessors *********/ /// A singleton instance of the comparer. - public static SemanticVersionComparer Instance { get; } = new SemanticVersionComparer(); + public static SemanticVersionComparer Instance { get; } = new(); /********* diff --git a/src/SMAPI.Toolkit/Serialization/JsonHelper.cs b/src/SMAPI.Toolkit/Serialization/JsonHelper.cs index 00db9903..5c465f3c 100644 --- a/src/SMAPI.Toolkit/Serialization/JsonHelper.cs +++ b/src/SMAPI.Toolkit/Serialization/JsonHelper.cs @@ -14,7 +14,7 @@ namespace StardewModdingAPI.Toolkit.Serialization ** Accessors *********/ /// The JSON settings to use when serializing and deserializing files. - public JsonSerializerSettings JsonSettings { get; } = new JsonSerializerSettings + public JsonSerializerSettings JsonSettings { get; } = new() { Formatting = Formatting.Indented, ObjectCreationHandling = ObjectCreationHandling.Replace, // avoid issue where default ICollection values are duplicated each time the config is loaded diff --git a/src/SMAPI.Toolkit/Utilities/PathUtilities.cs b/src/SMAPI.Toolkit/Utilities/PathUtilities.cs index 2e9e5eac..85e12bfa 100644 --- a/src/SMAPI.Toolkit/Utilities/PathUtilities.cs +++ b/src/SMAPI.Toolkit/Utilities/PathUtilities.cs @@ -100,8 +100,8 @@ namespace StardewModdingAPI.Toolkit.Utilities // though, this is only for compatibility with the mod build package. // convert to URIs - Uri from = new Uri(sourceDir.TrimEnd(PathUtilities.PossiblePathSeparators) + "/"); - Uri to = new Uri(targetPath.TrimEnd(PathUtilities.PossiblePathSeparators) + "/"); + Uri from = new(sourceDir.TrimEnd(PathUtilities.PossiblePathSeparators) + "/"); + Uri to = new(targetPath.TrimEnd(PathUtilities.PossiblePathSeparators) + "/"); if (from.Scheme != to.Scheme) throw new InvalidOperationException($"Can't get path for '{targetPath}' relative to '{sourceDir}'."); diff --git a/src/SMAPI.Web/Controllers/IndexController.cs b/src/SMAPI.Web/Controllers/IndexController.cs index 5097997c..69b54f47 100644 --- a/src/SMAPI.Web/Controllers/IndexController.cs +++ b/src/SMAPI.Web/Controllers/IndexController.cs @@ -94,7 +94,7 @@ namespace StardewModdingAPI.Web.Controllers // strip 'noinclude' blocks from release description if (release != null) { - HtmlDocument doc = new HtmlDocument(); + HtmlDocument doc = new(); doc.LoadHtml(release.Body); foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//*[@class='noinclude']")?.ToArray() ?? Array.Empty()) node.Remove(); diff --git a/src/SMAPI.Web/Controllers/JsonValidatorController.cs b/src/SMAPI.Web/Controllers/JsonValidatorController.cs index e06c1236..bcd4097a 100644 --- a/src/SMAPI.Web/Controllers/JsonValidatorController.cs +++ b/src/SMAPI.Web/Controllers/JsonValidatorController.cs @@ -197,7 +197,7 @@ namespace StardewModdingAPI.Web.Controllers return null; // get matching file - DirectoryInfo schemaDir = new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "schemas")); + DirectoryInfo schemaDir = new(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "schemas")); foreach (FileInfo file in schemaDir.EnumerateFiles("*.json")) { if (file.Name.Equals($"{id}.json")) diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs index dfe2504b..5329df99 100644 --- a/src/SMAPI.Web/Controllers/ModsApiController.cs +++ b/src/SMAPI.Web/Controllers/ModsApiController.cs @@ -135,7 +135,7 @@ namespace StardewModdingAPI.Web.Controllers bool isSmapiBeta = apiVersion.IsPrerelease() && apiVersion.PrereleaseTag.StartsWith("beta"); // get latest versions - ModEntryModel result = new ModEntryModel { ID = search.ID }; + ModEntryModel result = new() { ID = search.ID }; IList errors = new List(); ModEntryVersionModel main = null; ModEntryVersionModel optional = null; diff --git a/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs b/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs index b8b05878..9689807c 100644 --- a/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs +++ b/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs @@ -90,7 +90,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.Chucklefish /// The mod ID. private string GetModUrl(uint id) { - UriBuilder builder = new UriBuilder(this.Client.BaseClient.BaseAddress); + UriBuilder builder = new(this.Client.BaseClient.BaseAddress); builder.Path += string.Format(this.ModPageUrlFormat, id); return builder.Uri.ToString(); } diff --git a/src/SMAPI.Web/Framework/Clients/CurseForge/CurseForgeClient.cs b/src/SMAPI.Web/Framework/Clients/CurseForge/CurseForgeClient.cs index d8008721..50a3336d 100644 --- a/src/SMAPI.Web/Framework/Clients/CurseForge/CurseForgeClient.cs +++ b/src/SMAPI.Web/Framework/Clients/CurseForge/CurseForgeClient.cs @@ -17,7 +17,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.CurseForge private readonly IClient Client; /// A regex pattern which matches a version number in a CurseForge mod file name. - private readonly Regex VersionInNamePattern = new Regex(@"^(?:.+? | *)v?(\d+\.\d+(?:\.\d+)?(?:-.+?)?) *(?:\.(?:zip|rar|7z))?$", RegexOptions.Compiled); + private readonly Regex VersionInNamePattern = new(@"^(?:.+? | *)v?(\d+\.\d+(?:\.\d+)?(?:-.+?)?) *(?:\.(?:zip|rar|7z))?$", RegexOptions.Compiled); /********* diff --git a/src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs b/src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs index 4ba94f81..571f06bc 100644 --- a/src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs +++ b/src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs @@ -195,7 +195,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.Nexus /// The mod ID. private string GetModUrl(uint id) { - UriBuilder builder = new UriBuilder(this.WebClient.BaseClient.BaseAddress); + UriBuilder builder = new(this.WebClient.BaseClient.BaseAddress); builder.Path += string.Format(this.WebModUrlFormat, id); return builder.Uri.ToString(); } diff --git a/src/SMAPI.Web/Framework/Compression/GzipHelper.cs b/src/SMAPI.Web/Framework/Compression/GzipHelper.cs index 676d660d..93cde9d3 100644 --- a/src/SMAPI.Web/Framework/Compression/GzipHelper.cs +++ b/src/SMAPI.Web/Framework/Compression/GzipHelper.cs @@ -29,9 +29,9 @@ namespace StardewModdingAPI.Web.Framework.Compression // compressed byte[] compressedData; - using (MemoryStream stream = new MemoryStream()) + using (MemoryStream stream = new()) { - using (GZipStream zipStream = new GZipStream(stream, CompressionLevel.Optimal, leaveOpen: true)) + using (GZipStream zipStream = new(stream, CompressionLevel.Optimal, leaveOpen: true)) zipStream.Write(buffer, 0, buffer.Length); stream.Position = 0; @@ -69,7 +69,7 @@ namespace StardewModdingAPI.Web.Framework.Compression return rawText; // decompress - using MemoryStream memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); { // read length prefix int dataLength = BitConverter.ToInt32(zipBuffer, 0); @@ -78,7 +78,7 @@ namespace StardewModdingAPI.Web.Framework.Compression // read data byte[] buffer = new byte[dataLength]; memoryStream.Position = 0; - using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress)) + using (GZipStream gZipStream = new(memoryStream, CompressionMode.Decompress)) gZipStream.Read(buffer, 0, buffer.Length); // return original string diff --git a/src/SMAPI.Web/Framework/Extensions.cs b/src/SMAPI.Web/Framework/Extensions.cs index 5305b142..2e767b3d 100644 --- a/src/SMAPI.Web/Framework/Extensions.cs +++ b/src/SMAPI.Web/Framework/Extensions.cs @@ -29,7 +29,7 @@ namespace StardewModdingAPI.Web.Framework public static string PlainAction(this IUrlHelper helper, [AspMvcAction] string action, [AspMvcController] string controller, object values = null, bool absoluteUrl = false) { // get route values - RouteValueDictionary valuesDict = new RouteValueDictionary(values); + RouteValueDictionary valuesDict = new(values); foreach (var value in helper.ActionContext.RouteData.Values) { if (!valuesDict.ContainsKey(value.Key)) @@ -45,7 +45,7 @@ namespace StardewModdingAPI.Web.Framework if (absoluteUrl) { HttpRequest request = helper.ActionContext.HttpContext.Request; - Uri baseUri = new Uri($"{request.Scheme}://{request.Host}"); + Uri baseUri = new($"{request.Scheme}://{request.Host}"); url = new Uri(baseUri, url).ToString(); } diff --git a/src/SMAPI.Web/Framework/JobDashboardAuthorizationFilter.cs b/src/SMAPI.Web/Framework/JobDashboardAuthorizationFilter.cs index 385c0c91..3c1405eb 100644 --- a/src/SMAPI.Web/Framework/JobDashboardAuthorizationFilter.cs +++ b/src/SMAPI.Web/Framework/JobDashboardAuthorizationFilter.cs @@ -9,7 +9,7 @@ namespace StardewModdingAPI.Web.Framework ** Fields *********/ /// An authorization filter that allows local requests. - private static readonly LocalRequestsOnlyAuthorizationFilter LocalRequestsOnlyFilter = new LocalRequestsOnlyAuthorizationFilter(); + private static readonly LocalRequestsOnlyAuthorizationFilter LocalRequestsOnlyFilter = new(); /********* diff --git a/src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs b/src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs index 992876ef..9da27d61 100644 --- a/src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs +++ b/src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs @@ -23,7 +23,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing public string Mod { get; set; } /// The text for the next log message. - private readonly StringBuilder Text = new StringBuilder(); + private readonly StringBuilder Text = new(); /********* diff --git a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs index 887d0105..6a3ea222 100644 --- a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs +++ b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs @@ -14,38 +14,38 @@ namespace StardewModdingAPI.Web.Framework.LogParsing ** Fields *********/ /// A regex pattern matching the start of a SMAPI message. - private readonly Regex MessageHeaderPattern = new Regex(@"^\[(?