diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-03-26 19:08:25 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-03-26 19:08:25 -0400 |
commit | 03efea26676464933513383eb1c841f1ca5db34d (patch) | |
tree | 6f9845ddca8ebae2d510affadfac025a30b321d6 | |
parent | eebd8d54dc068cff2b5127a4b8f03d0b54b89542 (diff) | |
download | SMAPI-03efea26676464933513383eb1c841f1ca5db34d.tar.gz SMAPI-03efea26676464933513383eb1c841f1ca5db34d.tar.bz2 SMAPI-03efea26676464933513383eb1c841f1ca5db34d.zip |
add LocaleChanged content event (#766)
-rw-r--r-- | src/SMAPI/Events/IContentEvents.cs | 4 | ||||
-rw-r--r-- | src/SMAPI/Events/LocaleChangedEventArgs.cs | 45 | ||||
-rw-r--r-- | src/SMAPI/Framework/Events/EventManager.cs | 4 | ||||
-rw-r--r-- | src/SMAPI/Framework/Events/ModContentEvents.cs | 7 | ||||
-rw-r--r-- | src/SMAPI/Framework/SCore.cs | 62 |
5 files changed, 100 insertions, 22 deletions
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 /// <summary>Raised after an asset is loaded by the content pipeline, after all mod edits specified via <see cref="AssetRequested"/> have been applied.</summary> /// <remarks>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.</remarks> event EventHandler<AssetReadyEventArgs> AssetReady; + + /// <summary>Raised after the game language changes.</summary> + /// <remarks>For non-English players, this may be raised during startup when the game switches to the previously selected language.</remarks> + event EventHandler<LocaleChangedEventArgs> 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 +{ + /// <summary>Event arguments for an <see cref="IContentEvents.LocaleChanged"/> event.</summary> + public class LocaleChangedEventArgs : EventArgs + { + /********* + ** Accessors + *********/ + /// <summary>The previous language enum value.</summary> + /// <remarks>For a custom language, this is always <see cref="LanguageCode.mod"/>.</remarks> + public LanguageCode OldLanguage { get; } + + /// <summary>The previous locale code.</summary> + /// <remarks>This is the locale code as it appears in asset names, like <c>fr-FR</c> in <c>Maps/springobjects.fr-FR</c>. The locale code for English is an empty string.</remarks> + public string OldLocale { get; } + + /// <summary>The new language enum value.</summary> + /// <remarks><inheritdoc cref="OldLanguage" select="remarks" /></remarks> + public LanguageCode NewLanguage { get; } + + /// <summary>The new locale code.</summary> + /// <remarks><inheritdoc cref="OldLocale" select="remarks" /></remarks> + public string NewLocale { get; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="oldLanguage">The previous language enum value.</param> + /// <param name="oldLocale">The previous locale code.</param> + /// <param name="newLanguage">The new language enum value.</param> + /// <param name="newLocale">The new locale code.</param> + 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 /// <inheritdoc cref="IContentEvents.AssetReady" /> public readonly ManagedEvent<AssetReadyEventArgs> AssetReady; + /// <inheritdoc cref="IContentEvents.LocaleChanged" /> + public readonly ManagedEvent<LocaleChangedEventArgs> LocaleChanged; + /**** ** Display @@ -204,6 +207,7 @@ namespace StardewModdingAPI.Framework.Events this.AssetRequested = ManageEventOf<AssetRequestedEventArgs>(nameof(IModEvents.Content), nameof(IContentEvents.AssetRequested)); this.AssetsInvalidated = ManageEventOf<AssetsInvalidatedEventArgs>(nameof(IModEvents.Content), nameof(IContentEvents.AssetsInvalidated)); this.AssetReady = ManageEventOf<AssetReadyEventArgs>(nameof(IModEvents.Content), nameof(IContentEvents.AssetReady)); + this.LocaleChanged = ManageEventOf<LocaleChangedEventArgs>(nameof(IModEvents.Content), nameof(IContentEvents.LocaleChanged)); this.MenuChanged = ManageEventOf<MenuChangedEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.MenuChanged)); this.Rendering = ManageEventOf<RenderingEventArgs>(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); } + /// <inheritdoc /> + public event EventHandler<LocaleChangedEventArgs> 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 ****/ /// <summary>Tracks whether the game should exit immediately and any pending initialization should be cancelled.</summary> - private readonly CancellationTokenSource CancellationToken = new CancellationTokenSource(); + private readonly CancellationTokenSource CancellationToken = new(); /// <summary>Manages the SMAPI console window and log file.</summary> private readonly LogManager LogManager; @@ -71,16 +72,16 @@ namespace StardewModdingAPI.Framework private Monitor Monitor => this.LogManager.Monitor; /// <summary>Simplifies access to private game code.</summary> - private readonly Reflector Reflection = new Reflector(); + private readonly Reflector Reflection = new(); /// <summary>Encapsulates access to SMAPI core translations.</summary> - private readonly Translator Translator = new Translator(); + private readonly Translator Translator = new(); /// <summary>The SMAPI configuration settings.</summary> private readonly SConfig Settings; /// <summary>The mod toolkit used for generic mod interactions.</summary> - private readonly ModToolkit Toolkit = new ModToolkit(); + private readonly ModToolkit Toolkit = new(); /**** ** Higher-level components @@ -99,7 +100,7 @@ namespace StardewModdingAPI.Framework /// <summary>Tracks the installed mods.</summary> /// <remarks>This is initialized after the game starts.</remarks> - private readonly ModRegistry ModRegistry = new ModRegistry(); + private readonly ModRegistry ModRegistry = new(); /// <summary>Manages SMAPI events for mods.</summary> private readonly EventManager EventManager; @@ -129,18 +130,21 @@ namespace StardewModdingAPI.Framework /// <summary>Whether the player just returned to the title screen.</summary> public bool JustReturnedToTitle { get; set; } + /// <summary>The last language set by the game.</summary> + private (string Locale, LanguageCode Code) LastLanguage { get; set; } = ("", LanguageCode.en); + /// <summary>The maximum number of consecutive attempts SMAPI should make to recover from an update error.</summary> - private readonly Countdown UpdateCrashTimer = new Countdown(60); // 60 ticks = roughly one second + private readonly Countdown UpdateCrashTimer = new(60); // 60 ticks = roughly one second /// <summary>Asset interceptors added or removed since the last tick.</summary> - private readonly List<AssetInterceptorChange> ReloadAssetInterceptorsQueue = new List<AssetInterceptorChange>(); + private readonly List<AssetInterceptorChange> ReloadAssetInterceptorsQueue = new(); /// <summary>A list of queued commands to parse and execute.</summary> /// <remarks>This property must be thread-safe, since it's accessed from a separate console input thread.</remarks> - private readonly ConcurrentQueue<string> RawCommandQueue = new ConcurrentQueue<string>(); + private readonly ConcurrentQueue<string> RawCommandQueue = new(); /// <summary>A list of commands to execute on each screen.</summary> - private readonly PerScreen<List<Tuple<Command, string, string[]>>> ScreenCommandQueue = new PerScreen<List<Tuple<Command, string, string[]>>>(() => new List<Tuple<Command, string, string[]>>()); + private readonly PerScreen<List<Tuple<Command, string, string[]>>> ScreenCommandQueue = new(() => new List<Tuple<Command, string, string[]>>()); /********* @@ -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); } /// <summary>Raised when the low-level stage while loading a save changes.</summary> @@ -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<IModMetadata> skippedMods = new List<IModMetadata>(); - 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<string> suppressUpdateChecks = new HashSet<string>(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>(contentPack)); return contentPack; @@ -1982,7 +2000,7 @@ namespace StardewModdingAPI.Framework // read translation files var translations = new Dictionary<string, IDictionary<string, string>>(); errors = new List<string>(); - 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 /// <summary>Delete normal (non-crash) log files created by SMAPI.</summary> private void PurgeNormalLogs() { - DirectoryInfo logsDir = new DirectoryInfo(Constants.LogDir); + DirectoryInfo logsDir = new(Constants.LogDir); if (!logsDir.Exists) return; |