diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2021-01-15 18:48:29 -0500 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2021-01-15 18:48:29 -0500 |
commit | 8a475b35790506a18aa94a68530b40e8326017ca (patch) | |
tree | 633a4cd0d41053dc57be87be89635b94b7abc25e | |
parent | 5953fc3bd083ae0a579d2da1ad833e6163848086 (diff) | |
download | SMAPI-8a475b35790506a18aa94a68530b40e8326017ca.tar.gz SMAPI-8a475b35790506a18aa94a68530b40e8326017ca.tar.bz2 SMAPI-8a475b35790506a18aa94a68530b40e8326017ca.zip |
move error-handling Harmony patches into a new Error Handler bundled mod
39 files changed, 223 insertions, 74 deletions
diff --git a/build/common.targets b/build/common.targets index 3d39be23..c9afb27a 100644 --- a/build/common.targets +++ b/build/common.targets @@ -37,16 +37,24 @@ <Copy SourceFiles="$(TargetDir)\TMXTile.dll" DestinationFolder="$(GamePath)\smapi-internal" /> <Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(GamePath)\smapi-internal\i18n" /> </Target> - <Target Name="CopyDefaultMods" Condition="'$(MSBuildProjectName)' == 'SMAPI.Mods.ConsoleCommands' OR '$(MSBuildProjectName)' == 'SMAPI.Mods.SaveBackup'"> + + <Target Name="CopyDefaultMods" Condition="'$(MSBuildProjectName)' == 'SMAPI.Mods.ConsoleCommands' OR '$(MSBuildProjectName)' == 'SMAPI.Mods.ErrorHandler' OR '$(MSBuildProjectName)' == 'SMAPI.Mods.SaveBackup'"> + <ItemGroup> + <TranslationFiles Include="$(TargetDir)\i18n\*.json" /> + </ItemGroup> + <Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)" /> <Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)" Condition="Exists('$(TargetDir)\$(TargetName).pdb')" /> <Copy SourceFiles="$(TargetDir)\manifest.json" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)" /> + <Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)\i18n" /> </Target> + <Target Name="CopyToolkit" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit' AND $(TargetFramework) == 'net4.5'" AfterTargets="PostBuildEvent"> <Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" /> <Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\smapi-internal" /> <Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" /> </Target> + <Target Name="CopyToolkitCoreInterfaces" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit.CoreInterfaces' AND $(TargetFramework) == 'net4.5'" AfterTargets="PostBuildEvent"> <Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" /> <Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\smapi-internal" /> diff --git a/build/prepare-install-package.targets b/build/prepare-install-package.targets index 7b9d63f9..205040db 100644 --- a/build/prepare-install-package.targets +++ b/build/prepare-install-package.targets @@ -16,6 +16,7 @@ <SmapiBin>$(BuildRootPath)\SMAPI\bin\$(Configuration)</SmapiBin> <ToolkitBin>$(BuildRootPath)\SMAPI.Toolkit\bin\$(Configuration)\net4.5</ToolkitBin> <ConsoleCommandsBin>$(BuildRootPath)\SMAPI.Mods.ConsoleCommands\bin\$(Configuration)</ConsoleCommandsBin> + <ErrorHandlerBin>$(BuildRootPath)\SMAPI.Mods.ErrorHandler\bin\$(Configuration)</ErrorHandlerBin> <SaveBackupBin>$(BuildRootPath)\SMAPI.Mods.SaveBackup\bin\$(Configuration)</SaveBackupBin> <PackagePath>$(OutRootPath)\SMAPI installer</PackagePath> @@ -23,6 +24,7 @@ </PropertyGroup> <ItemGroup> <TranslationFiles Include="$(SmapiBin)\i18n\*.json" /> + <ErrorHandlerTranslationFiles Include="$(ErrorHandlerBin)\i18n\*.json" /> </ItemGroup> <!-- reset package directory --> @@ -64,6 +66,10 @@ <Copy SourceFiles="$(ConsoleCommandsBin)\ConsoleCommands.dll" DestinationFolder="$(PackagePath)\bundle\Mods\ConsoleCommands" /> <Copy SourceFiles="$(ConsoleCommandsBin)\ConsoleCommands.pdb" DestinationFolder="$(PackagePath)\bundle\Mods\ConsoleCommands" /> <Copy SourceFiles="$(ConsoleCommandsBin)\manifest.json" DestinationFolder="$(PackagePath)\bundle\Mods\ConsoleCommands" /> + <Copy SourceFiles="$(ErrorHandlerBin)\ErrorHandler.dll" DestinationFolder="$(PackagePath)\bundle\Mods\ErrorHandler" /> + <Copy SourceFiles="$(ErrorHandlerBin)\ErrorHandler.pdb" DestinationFolder="$(PackagePath)\bundle\Mods\ErrorHandler" /> + <Copy SourceFiles="$(ErrorHandlerBin)\manifest.json" DestinationFolder="$(PackagePath)\bundle\Mods\ErrorHandler" /> + <Copy SourceFiles="@(ErrorHandlerTranslationFiles)" DestinationFolder="$(PackagePath)\bundle\Mods\ErrorHandler\i18n" /> <Copy SourceFiles="$(SaveBackupBin)\SaveBackup.dll" DestinationFolder="$(PackagePath)\bundle\Mods\SaveBackup" /> <Copy SourceFiles="$(SaveBackupBin)\SaveBackup.pdb" DestinationFolder="$(PackagePath)\bundle\Mods\SaveBackup" /> <Copy SourceFiles="$(SaveBackupBin)\manifest.json" DestinationFolder="$(PackagePath)\bundle\Mods\SaveBackup" /> diff --git a/docs/release-notes.md b/docs/release-notes.md index a2ac8b35..7347560e 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -7,6 +7,10 @@ * Migrated to Harmony 2.0 (see [_migrate to Harmony 2.0_](https://stardewvalleywiki.com/Modding:Migrate_to_Harmony_2.0) for more info). --> +## Upcoming release +* For the Error Handler mod: + * Added in SMAPI 3.9. This has vanilla error-handling that was previously added by SMAPI directly. That simplifies the core SMAPI logic, and lets players or modders disable it if needed. + ## 3.8.4 Released 15 January 2021 for Stardew Valley 1.5.3 or later. diff --git a/src/SMAPI.Mods.ErrorHandler/ModEntry.cs b/src/SMAPI.Mods.ErrorHandler/ModEntry.cs new file mode 100644 index 00000000..fa9338f4 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/ModEntry.cs @@ -0,0 +1,73 @@ +using System.Reflection; +using StardewModdingAPI.Events; +using StardewModdingAPI.Framework; +using StardewModdingAPI.Framework.Logging; +using StardewModdingAPI.Framework.Patching; +using StardewModdingAPI.Mods.ErrorHandler.Patches; +using StardewValley; + +namespace StardewModdingAPI.Mods.ErrorHandler +{ + /// <summary>The main entry point for the mod.</summary> + public class ModEntry : Mod + { + /********* + ** Private methods + *********/ + /// <summary>Whether custom content was removed from the save data to avoid a crash.</summary> + private bool IsSaveContentRemoved; + + + /********* + ** Public methods + *********/ + /// <summary>The mod entry point, called after the mod is first loaded.</summary> + /// <param name="helper">Provides simplified APIs for writing mods.</param> + public override void Entry(IModHelper helper) + { + // get SMAPI core types + SCore core = SCore.Instance; + LogManager logManager = core.GetType().GetField("LogManager", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(core) as LogManager; + if (logManager == null) + { + this.Monitor.Log($"Can't access SMAPI's internal log manager. Error-handling patches won't be applied.", LogLevel.Error); + return; + } + + // apply patches + new GamePatcher(this.Monitor).Apply( + new EventErrorPatch(logManager.MonitorForGame), + new DialogueErrorPatch(logManager.MonitorForGame, this.Helper.Reflection), + new ObjectErrorPatch(), + new LoadErrorPatch(this.Monitor, this.OnSaveContentRemoved), + new ScheduleErrorPatch(logManager.MonitorForGame) + ); + + // hook events + this.Helper.Events.GameLoop.SaveLoaded += this.OnSaveLoaded; + } + + + /********* + ** Private methods + *********/ + /// <summary>Raised after custom content is removed from the save data to avoid a crash.</summary> + internal void OnSaveContentRemoved() + { + this.IsSaveContentRemoved = true; + } + + /// <summary>The method invoked when a save is loaded.</summary> + /// <param name="sender">The event sender.</param> + /// <param name="e">The event arguments.</param> + public void OnSaveLoaded(object sender, SaveLoadedEventArgs e) + { + // show in-game warning for removed save content + if (this.IsSaveContentRemoved) + { + this.IsSaveContentRemoved = false; + Game1.addHUDMessage(new HUDMessage(this.Helper.Translation.Get("warn.invalid-content-removed"), HUDMessage.error_type)); + } + } + } +} diff --git a/src/SMAPI/Patches/DialogueErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs index 215df561..ba0ca582 100644 --- a/src/SMAPI/Patches/DialogueErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using StardewModdingAPI.Framework.Patching; -using StardewModdingAPI.Framework.Reflection; using StardewValley; #if HARMONY_2 using HarmonyLib; @@ -12,7 +11,7 @@ using System.Reflection; using Harmony; #endif -namespace StardewModdingAPI.Patches +namespace StardewModdingAPI.Mods.ErrorHandler.Patches { /// <summary>A Harmony patch for the <see cref="Dialogue"/> constructor which intercepts invalid dialogue lines and logs an error instead of crashing.</summary> /// <remarks>Patch methods must be static for Harmony to work correctly. See the Harmony documentation before renaming patch arguments.</remarks> @@ -27,7 +26,7 @@ namespace StardewModdingAPI.Patches private static IMonitor MonitorForGame; /// <summary>Simplifies access to private code.</summary> - private static Reflector Reflection; + private static IReflectionHelper Reflection; /********* @@ -43,7 +42,7 @@ namespace StardewModdingAPI.Patches /// <summary>Construct an instance.</summary> /// <param name="monitorForGame">Writes messages to the console and log file on behalf of the game.</param> /// <param name="reflector">Simplifies access to private code.</param> - public DialogueErrorPatch(IMonitor monitorForGame, Reflector reflector) + public DialogueErrorPatch(IMonitor monitorForGame, IReflectionHelper reflector) { DialogueErrorPatch.MonitorForGame = monitorForGame; DialogueErrorPatch.Reflection = reflector; @@ -167,7 +166,7 @@ namespace StardewModdingAPI.Patches /// <returns>Returns whether to execute the original method.</returns> private static bool Before_NPC_CurrentDialogue(NPC __instance, ref Stack<Dialogue> __result, MethodInfo __originalMethod) { - const string key = nameof(Before_NPC_CurrentDialogue); + const string key = nameof(DialogueErrorPatch.Before_NPC_CurrentDialogue); if (!PatchHelper.StartIntercept(key)) return true; diff --git a/src/SMAPI/Patches/EventErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/EventErrorPatch.cs index 46651387..fabc6cad 100644 --- a/src/SMAPI/Patches/EventErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/EventErrorPatch.cs @@ -9,7 +9,7 @@ using Harmony; using StardewModdingAPI.Framework.Patching; using StardewValley; -namespace StardewModdingAPI.Patches +namespace StardewModdingAPI.Mods.ErrorHandler.Patches { /// <summary>A Harmony patch for <see cref="GameLocation.checkEventPrecondition"/> which intercepts invalid preconditions and logs an error instead of crashing.</summary> /// <remarks>Patch methods must be static for Harmony to work correctly. See the Harmony documentation before renaming patch arguments.</remarks> @@ -89,7 +89,7 @@ namespace StardewModdingAPI.Patches /// <returns>Returns whether to execute the original method.</returns> private static bool Before_GameLocation_CheckEventPrecondition(GameLocation __instance, ref int __result, string precondition, MethodInfo __originalMethod) { - const string key = nameof(Before_GameLocation_CheckEventPrecondition); + const string key = nameof(EventErrorPatch.Before_GameLocation_CheckEventPrecondition); if (!PatchHelper.StartIntercept(key)) return true; diff --git a/src/SMAPI/Patches/LoadErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/LoadErrorPatch.cs index f5ee5d71..2227ea07 100644 --- a/src/SMAPI/Patches/LoadErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/LoadErrorPatch.cs @@ -13,7 +13,7 @@ using StardewValley; using StardewValley.Buildings; using StardewValley.Locations; -namespace StardewModdingAPI.Patches +namespace StardewModdingAPI.Mods.ErrorHandler.Patches { /// <summary>A Harmony patch for <see cref="SaveGame"/> which prevents some errors due to broken save data.</summary> /// <remarks>Patch methods must be static for Harmony to work correctly. See the Harmony documentation before renaming patch arguments.</remarks> diff --git a/src/SMAPI/Patches/ObjectErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/ObjectErrorPatch.cs index 64b8e6b6..70f054cd 100644 --- a/src/SMAPI/Patches/ObjectErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/ObjectErrorPatch.cs @@ -12,7 +12,7 @@ using System.Reflection; using Harmony; #endif -namespace StardewModdingAPI.Patches +namespace StardewModdingAPI.Mods.ErrorHandler.Patches { /// <summary>A Harmony patch for <see cref="SObject.getDescription"/> which intercepts crashes due to the item no longer existing.</summary> /// <remarks>Patch methods must be static for Harmony to work correctly. See the Harmony documentation before renaming patch arguments.</remarks> @@ -103,7 +103,7 @@ namespace StardewModdingAPI.Patches /// <returns>Returns whether to execute the original method.</returns> private static bool Before_Object_loadDisplayName(SObject __instance, ref string __result, MethodInfo __originalMethod) { - const string key = nameof(Before_Object_loadDisplayName); + const string key = nameof(ObjectErrorPatch.Before_Object_loadDisplayName); if (!PatchHelper.StartIntercept(key)) return true; diff --git a/src/SMAPI/Patches/ScheduleErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/ScheduleErrorPatch.cs index 1d58a292..abbd1a8f 100644 --- a/src/SMAPI/Patches/ScheduleErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/ScheduleErrorPatch.cs @@ -11,7 +11,7 @@ using System.Reflection; using Harmony; #endif -namespace StardewModdingAPI.Patches +namespace StardewModdingAPI.Mods.ErrorHandler.Patches { /// <summary>A Harmony patch for <see cref="NPC.parseMasterSchedule"/> which intercepts crashes due to invalid schedule data.</summary> /// <remarks>Patch methods must be static for Harmony to work correctly. See the Harmony documentation before renaming patch arguments.</remarks> @@ -90,7 +90,7 @@ namespace StardewModdingAPI.Patches /// <returns>Returns whether to execute the original method.</returns> private static bool Before_NPC_parseMasterSchedule(string rawData, NPC __instance, ref Dictionary<int, SchedulePathDescription> __result, MethodInfo __originalMethod) { - const string key = nameof(Before_NPC_parseMasterSchedule); + const string key = nameof(ScheduleErrorPatch.Before_NPC_parseMasterSchedule); if (!PatchHelper.StartIntercept(key)) return true; diff --git a/src/SMAPI.Mods.ErrorHandler/SMAPI.Mods.ErrorHandler.csproj b/src/SMAPI.Mods.ErrorHandler/SMAPI.Mods.ErrorHandler.csproj new file mode 100644 index 00000000..5c0cf952 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/SMAPI.Mods.ErrorHandler.csproj @@ -0,0 +1,46 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <AssemblyName>ErrorHandler</AssemblyName> + <RootNamespace>StardewModdingAPI.Mods.ErrorHandler</RootNamespace> + <TargetFramework>net45</TargetFramework> + <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> + <PlatformTarget>x86</PlatformTarget> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\SMAPI\SMAPI.csproj" Private="False" /> + <Reference Include="..\..\build\0Harmony.dll" Private="False" /> + </ItemGroup> + + <ItemGroup> + <Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="False" /> + </ItemGroup> + + <Choose> + <!-- Windows --> + <When Condition="$(OS) == 'Windows_NT'"> + <ItemGroup> + <Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" /> + <Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" /> + <Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" /> + <Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" /> + <Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" /> + </ItemGroup> + </When> + + <!-- Linux/Mac --> + <Otherwise> + <ItemGroup> + <Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" /> + </ItemGroup> + </Otherwise> + </Choose> + + <ItemGroup> + <None Update="i18n\*.json" CopyToOutputDirectory="PreserveNewest" /> + <None Update="manifest.json" CopyToOutputDirectory="PreserveNewest" /> + </ItemGroup> + + <Import Project="..\SMAPI.Internal\SMAPI.Internal.projitems" Label="Shared" /> + <Import Project="..\..\build\common.targets" /> +</Project> diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/de.json b/src/SMAPI.Mods.ErrorHandler/i18n/de.json new file mode 100644 index 00000000..1de6301c --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/i18n/de.json @@ -0,0 +1,4 @@ +{ + // warning messages + "warn.invalid-content-removed": "Ungültiger Inhalt wurde entfernt, um einen Absturz zu verhindern (siehe SMAPI Konsole für weitere Informationen)." +} diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/default.json b/src/SMAPI.Mods.ErrorHandler/i18n/default.json new file mode 100644 index 00000000..b74dcea0 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/i18n/default.json @@ -0,0 +1,4 @@ +{ + // warning messages + "warn.invalid-content-removed": "Invalid content was removed to prevent a crash (see the SMAPI console for info)." +} diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/es.json b/src/SMAPI.Mods.ErrorHandler/i18n/es.json new file mode 100644 index 00000000..8ba10b70 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/i18n/es.json @@ -0,0 +1,4 @@ +{ + // warning messages + "warn.invalid-content-removed": "Se ha quitado contenido inválido para evitar un cierre forzoso (revisa la consola de SMAPI para más información)." +} diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/fr.json b/src/SMAPI.Mods.ErrorHandler/i18n/fr.json new file mode 100644 index 00000000..76978526 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/i18n/fr.json @@ -0,0 +1,4 @@ +{ + // warning messages + "warn.invalid-content-removed": "Le contenu non valide a été supprimé afin d'éviter un plantage (voir la console de SMAPI pour plus d'informations)." +} diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/hu.json b/src/SMAPI.Mods.ErrorHandler/i18n/hu.json new file mode 100644 index 00000000..92aca7d0 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/i18n/hu.json @@ -0,0 +1,4 @@ +{ + // warning messages + "warn.invalid-content-removed": "Érvénytelen elemek kerültek eltávolításra, hogy a játék ne omoljon össze (további információk a SMAPI konzolon)." +} diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/it.json b/src/SMAPI.Mods.ErrorHandler/i18n/it.json new file mode 100644 index 00000000..5182972e --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/i18n/it.json @@ -0,0 +1,4 @@ +{ + // warning messages + "warn.invalid-content-removed": "Contenuto non valido rimosso per prevenire un crash (Guarda la console di SMAPI per maggiori informazioni)." +} diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/ja.json b/src/SMAPI.Mods.ErrorHandler/i18n/ja.json new file mode 100644 index 00000000..559c7fbe --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/i18n/ja.json @@ -0,0 +1,4 @@ +{ + // warning messages + "warn.invalid-content-removed": "クラッシュを防ぐために無効なコンテンツを取り除きました (詳細はSMAPIコンソールを参照)" +} diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/ko.json b/src/SMAPI.Mods.ErrorHandler/i18n/ko.json new file mode 100644 index 00000000..48f05c26 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/i18n/ko.json @@ -0,0 +1,4 @@ +{ + // warning messages + "warn.invalid-content-removed": "충돌을 방지하기 위해 잘못된 컨텐츠가 제거되었습니다 (자세한 내용은 SMAPI 콘솔 참조)." +} diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/pt.json b/src/SMAPI.Mods.ErrorHandler/i18n/pt.json new file mode 100644 index 00000000..8ea8cec9 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/i18n/pt.json @@ -0,0 +1,4 @@ +{ + // warning messages + "warn.invalid-content-removed": "Conteúdo inválido foi removido para prevenir uma falha (veja o console do SMAPI para mais informações)." +} diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/ru.json b/src/SMAPI.Mods.ErrorHandler/i18n/ru.json new file mode 100644 index 00000000..e9c3b313 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/i18n/ru.json @@ -0,0 +1,4 @@ +{ + // warning messages + "warn.invalid-content-removed": "Недопустимое содержимое было удалено, чтобы предотвратить сбой (см. информацию в консоли SMAPI)" +} diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/tr.json b/src/SMAPI.Mods.ErrorHandler/i18n/tr.json new file mode 100644 index 00000000..a05ab152 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/i18n/tr.json @@ -0,0 +1,4 @@ +{ + // warning messages + "warn.invalid-content-removed": "Yanlış paketlenmiş bir içerik, oyunun çökmemesi için yüklenmedi (SMAPI konsol penceresinde detaylı bilgi mevcut)." +} diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/zh.json b/src/SMAPI.Mods.ErrorHandler/i18n/zh.json new file mode 100644 index 00000000..e959aa40 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/i18n/zh.json @@ -0,0 +1,4 @@ +{ + // warning messages + "warn.invalid-content-removed": "非法内容已移除以防游戏闪退(查看SMAPI控制台获得更多信息)" +} diff --git a/src/SMAPI.Mods.ErrorHandler/manifest.json b/src/SMAPI.Mods.ErrorHandler/manifest.json new file mode 100644 index 00000000..f83e2f96 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/manifest.json @@ -0,0 +1,9 @@ +{ + "Name": "Error Handler", + "Author": "SMAPI", + "Version": "3.8.3", + "Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.", + "UniqueID": "SMAPI.ErrorHandler", + "EntryDll": "ErrorHandler.dll", + "MinimumApiVersion": "3.8.3" +} diff --git a/src/SMAPI.sln b/src/SMAPI.sln index b7a84fe4..904ae7f4 100644 --- a/src/SMAPI.sln +++ b/src/SMAPI.sln @@ -71,6 +71,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SMAPI.ModBuildConfig.Analyz EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SMAPI.Mods.ConsoleCommands", "SMAPI.Mods.ConsoleCommands\SMAPI.Mods.ConsoleCommands.csproj", "{0634EA4C-3B8F-42DB-AEA6-CA9E4EF6E92F}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SMAPI.Mods.ErrorHandler", "SMAPI.Mods.ErrorHandler\SMAPI.Mods.ErrorHandler.csproj", "{491E775B-EAD0-44D4-B6CA-F1FC3E316D33}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SMAPI.Mods.SaveBackup", "SMAPI.Mods.SaveBackup\SMAPI.Mods.SaveBackup.csproj", "{CD53AD6F-97F4-4872-A212-50C2A0FD3601}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SMAPI.Toolkit", "SMAPI.Toolkit\SMAPI.Toolkit.csproj", "{08184F74-60AD-4EEE-A78C-F4A35ADE6246}" @@ -83,6 +85,7 @@ Global GlobalSection(SharedMSBuildProjectFiles) = preSolution SMAPI.Internal\SMAPI.Internal.projitems*{0634ea4c-3b8f-42db-aea6-ca9e4ef6e92f}*SharedItemsImports = 5 SMAPI.Internal\SMAPI.Internal.projitems*{0a9bb24f-15ff-4c26-b1a2-81f7ae316518}*SharedItemsImports = 5 + SMAPI.Internal\SMAPI.Internal.projitems*{491e775b-ead0-44d4-b6ca-f1fc3e316d33}*SharedItemsImports = 5 SMAPI.Internal\SMAPI.Internal.projitems*{80efd92f-728f-41e0-8a5b-9f6f49a91899}*SharedItemsImports = 5 SMAPI.Internal\SMAPI.Internal.projitems*{85208f8d-6fd1-4531-be05-7142490f59fe}*SharedItemsImports = 13 SMAPI.Internal\SMAPI.Internal.projitems*{cd53ad6f-97f4-4872-a212-50c2a0fd3601}*SharedItemsImports = 5 @@ -121,6 +124,10 @@ Global {0634EA4C-3B8F-42DB-AEA6-CA9E4EF6E92F}.Debug|Any CPU.Build.0 = Debug|Any CPU {0634EA4C-3B8F-42DB-AEA6-CA9E4EF6E92F}.Release|Any CPU.ActiveCfg = Release|Any CPU {0634EA4C-3B8F-42DB-AEA6-CA9E4EF6E92F}.Release|Any CPU.Build.0 = Release|Any CPU + {491E775B-EAD0-44D4-B6CA-F1FC3E316D33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {491E775B-EAD0-44D4-B6CA-F1FC3E316D33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {491E775B-EAD0-44D4-B6CA-F1FC3E316D33}.Release|Any CPU.ActiveCfg = Release|Any CPU + {491E775B-EAD0-44D4-B6CA-F1FC3E316D33}.Release|Any CPU.Build.0 = Release|Any CPU {CD53AD6F-97F4-4872-A212-50C2A0FD3601}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CD53AD6F-97F4-4872-A212-50C2A0FD3601}.Debug|Any CPU.Build.0 = Debug|Any CPU {CD53AD6F-97F4-4872-A212-50C2A0FD3601}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -151,6 +158,7 @@ Global {680B2641-81EA-467C-86A5-0E81CDC57ED0} = {82D22ED7-A0A7-4D64-8E92-4B6A5E74ED11} {AA95884B-7097-476E-92C8-D0500DE9D6D1} = {82D22ED7-A0A7-4D64-8E92-4B6A5E74ED11} {0634EA4C-3B8F-42DB-AEA6-CA9E4EF6E92F} = {AE9A4D46-E910-4293-8BC4-673F85FFF6A5} + {491E775B-EAD0-44D4-B6CA-F1FC3E316D33} = {AE9A4D46-E910-4293-8BC4-673F85FFF6A5} {CD53AD6F-97F4-4872-A212-50C2A0FD3601} = {AE9A4D46-E910-4293-8BC4-673F85FFF6A5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index f9a36593..00c2de75 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -124,9 +124,6 @@ namespace StardewModdingAPI.Framework /// <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 - /// <summary>Whether custom content was removed from the save data to avoid a crash.</summary> - private bool IsSaveContentRemoved; - /// <summary>Asset interceptors added or removed since the last tick.</summary> private readonly List<AssetInterceptorChange> ReloadAssetInterceptorsQueue = new List<AssetInterceptorChange>(); @@ -145,6 +142,10 @@ namespace StardewModdingAPI.Framework /// <remarks>This is initialized after the game starts. This is accessed directly because it's not part of the normal class model.</remarks> internal static DeprecationManager DeprecationManager { get; private set; } + /// <summary>The singleton instance.</summary> + /// <remarks>This is only intended for use by external code like the Error Handler mod.</remarks> + internal static SCore Instance { get; private set; } + /// <summary>The number of update ticks which have already executed. This is similar to <see cref="Game1.ticks"/>, but incremented more consistently for every tick.</summary> internal static uint TicksElapsed { get; private set; } @@ -157,6 +158,8 @@ namespace StardewModdingAPI.Framework /// <param name="writeToConsole">Whether to output log messages to the console.</param> public SCore(string modsPath, bool writeToConsole) { + SCore.Instance = this; + // init paths this.VerifyPath(modsPath); this.VerifyPath(Constants.LogDir); @@ -245,12 +248,7 @@ namespace StardewModdingAPI.Framework // apply game patches new GamePatcher(this.Monitor).Apply( - new EventErrorPatch(this.LogManager.MonitorForGame), - new DialogueErrorPatch(this.LogManager.MonitorForGame, this.Reflection), - new ObjectErrorPatch(), - new LoadContextPatch(this.Reflection, this.OnLoadStageChanged), - new LoadErrorPatch(this.Monitor, this.OnSaveContentRemoved), - new ScheduleErrorPatch(this.LogManager.MonitorForGame) + new LoadContextPatch(this.Reflection, this.OnLoadStageChanged) ); // add exit handler @@ -517,15 +515,6 @@ namespace StardewModdingAPI.Framework this.ScreenCommandQueue.GetValueForScreen(screenId).Add(Tuple.Create(command, name, args)); } - /********* - ** Show in-game warnings (for main player only) - *********/ - // save content removed - if (this.IsSaveContentRemoved && Context.IsWorldReady) - { - this.IsSaveContentRemoved = false; - Game1.addHUDMessage(new HUDMessage(this.Translator.Get("warn.invalid-content-removed"), HUDMessage.error_type)); - } /********* ** Run game update @@ -1105,12 +1094,6 @@ namespace StardewModdingAPI.Framework Game1.CustomData[migrationKey] = Constants.ApiVersion.ToString(); } - /// <summary>Raised after custom content is removed from the save data to avoid a crash.</summary> - internal void OnSaveContentRemoved() - { - this.IsSaveContentRemoved = true; - } - /// <summary>A callback invoked before <see cref="Game1.newDayAfterFade"/> runs.</summary> protected void OnNewDayAfterFade() { diff --git a/src/SMAPI/Properties/AssemblyInfo.cs b/src/SMAPI/Properties/AssemblyInfo.cs index ee8a1674..ae758e9b 100644 --- a/src/SMAPI/Properties/AssemblyInfo.cs +++ b/src/SMAPI/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("SMAPI.Tests")] +[assembly: InternalsVisibleTo("ErrorHandler")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // Moq for unit testing diff --git a/src/SMAPI/SMAPI.config.json b/src/SMAPI/SMAPI.config.json index 6ba64fe7..f44c422f 100644 --- a/src/SMAPI/SMAPI.config.json +++ b/src/SMAPI/SMAPI.config.json @@ -113,6 +113,7 @@ copy all the settings, or you may cause bugs due to overridden changes in future */ "SuppressUpdateChecks": [ "SMAPI.ConsoleCommands", + "SMAPI.ErrorHandler", "SMAPI.SaveBackup" ] } diff --git a/src/SMAPI/i18n/de.json b/src/SMAPI/i18n/de.json index a8cbd83b..595c3eff 100644 --- a/src/SMAPI/i18n/de.json +++ b/src/SMAPI/i18n/de.json @@ -1,10 +1,6 @@ { - // error messages - "warn.invalid-content-removed": "Ungültiger Inhalt wurde entfernt, um einen Absturz zu verhindern (siehe SMAPI Konsole für weitere Informationen).", - // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}} {{day}}", "generic.date-with-year": "{{season}} {{day}} im Jahr {{year}}" - } diff --git a/src/SMAPI/i18n/default.json b/src/SMAPI/i18n/default.json index 7a3d3ed5..7e1f9c4d 100644 --- a/src/SMAPI/i18n/default.json +++ b/src/SMAPI/i18n/default.json @@ -1,7 +1,4 @@ { - // error messages - "warn.invalid-content-removed": "Invalid content was removed to prevent a crash (see the SMAPI console for info).", - // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}} {{day}}", diff --git a/src/SMAPI/i18n/es.json b/src/SMAPI/i18n/es.json index c9843991..76228d7d 100644 --- a/src/SMAPI/i18n/es.json +++ b/src/SMAPI/i18n/es.json @@ -1,7 +1,4 @@ { - // error messages - "warn.invalid-content-removed": "Se ha quitado contenido inválido para evitar un cierre forzoso (revisa la consola de SMAPI para más información).", - // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{seasonLowercase}} {{day}}", diff --git a/src/SMAPI/i18n/fr.json b/src/SMAPI/i18n/fr.json index 5969aa20..e32ee712 100644 --- a/src/SMAPI/i18n/fr.json +++ b/src/SMAPI/i18n/fr.json @@ -1,7 +1,4 @@ { - // error messages - "warn.invalid-content-removed": "Le contenu non valide a été supprimé afin d'éviter un plantage (voir la console de SMAPI pour plus d'informations).", - // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{day}} {{seasonLowercase}}", diff --git a/src/SMAPI/i18n/hu.json b/src/SMAPI/i18n/hu.json index 785012f4..2e3b7264 100644 --- a/src/SMAPI/i18n/hu.json +++ b/src/SMAPI/i18n/hu.json @@ -1,7 +1,4 @@ { - // error messages - "warn.invalid-content-removed": "Érvénytelen elemek kerültek eltávolításra, hogy a játék ne omoljon össze (további információk a SMAPI konzolon).", - // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}} {{day}}", diff --git a/src/SMAPI/i18n/it.json b/src/SMAPI/i18n/it.json index 3b3351c3..7ada11f0 100644 --- a/src/SMAPI/i18n/it.json +++ b/src/SMAPI/i18n/it.json @@ -1,7 +1,4 @@ { - // error messages - "warn.invalid-content-removed": "Contenuto non valido rimosso per prevenire un crash (Guarda la console di SMAPI per maggiori informazioni).", - // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{day}} {{season}}", diff --git a/src/SMAPI/i18n/ja.json b/src/SMAPI/i18n/ja.json index 1f814bfa..c95ac1b1 100644 --- a/src/SMAPI/i18n/ja.json +++ b/src/SMAPI/i18n/ja.json @@ -1,7 +1,4 @@ { - // error messages - "warn.invalid-content-removed": "クラッシュを防ぐために無効なコンテンツを取り除きました (詳細はSMAPIコンソールを参照)", - // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}} {{day}}日", diff --git a/src/SMAPI/i18n/ko.json b/src/SMAPI/i18n/ko.json index d5bbffa4..8d267e5e 100644 --- a/src/SMAPI/i18n/ko.json +++ b/src/SMAPI/i18n/ko.json @@ -1,7 +1,4 @@ { - // error messages - "warn.invalid-content-removed": "충돌을 방지하기 위해 잘못된 컨텐츠가 제거되었습니다 (자세한 내용은 SMAPI 콘솔 참조).", - // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}} {{day}}", diff --git a/src/SMAPI/i18n/pt.json b/src/SMAPI/i18n/pt.json index e8460922..7a08b08f 100644 --- a/src/SMAPI/i18n/pt.json +++ b/src/SMAPI/i18n/pt.json @@ -1,7 +1,4 @@ { - // error messages - "warn.invalid-content-removed": "Conteúdo inválido foi removido para prevenir uma falha (veja o console do SMAPI para mais informações).", - // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}} {{day}}", diff --git a/src/SMAPI/i18n/ru.json b/src/SMAPI/i18n/ru.json index 002fdbf8..b8ff55c4 100644 --- a/src/SMAPI/i18n/ru.json +++ b/src/SMAPI/i18n/ru.json @@ -1,7 +1,4 @@ { - // error messages - "warn.invalid-content-removed": "Недопустимое содержимое было удалено, чтобы предотвратить сбой (см. информацию в консоли SMAPI)", - // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}}, {{day}}-е число", diff --git a/src/SMAPI/i18n/tr.json b/src/SMAPI/i18n/tr.json index 2a6e83a1..e97a48ba 100644 --- a/src/SMAPI/i18n/tr.json +++ b/src/SMAPI/i18n/tr.json @@ -1,7 +1,4 @@ { - // error messages - "warn.invalid-content-removed": "Yanlış paketlenmiş bir içerik, oyunun çökmemesi için yüklenmedi (SMAPI konsol penceresinde detaylı bilgi mevcut).", - // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{day}} {{season}}", diff --git a/src/SMAPI/i18n/zh.json b/src/SMAPI/i18n/zh.json index cdbe3b74..36d459de 100644 --- a/src/SMAPI/i18n/zh.json +++ b/src/SMAPI/i18n/zh.json @@ -1,7 +1,4 @@ { - // error messages - "warn.invalid-content-removed": "非法内容已移除以防游戏闪退(查看SMAPI控制台获得更多信息)", - // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}}{{day}}日", |