0
0
mirror of https://github.com/sp-tarkov/modules.git synced 2025-02-13 09:50:43 -05:00
modules/project/Aki.SinglePlayer/Patches/MainMenu/PluginErrorNotifierPatch.cs
Terkoiz 337a0733ae Publicized assembly refactor (!58)
Depends on SPT-AKI/SPT-AssemblyTool#3

* Refactored Modules for better consistency and general readability, along with preparing the code for a publicized assembly
* Added `PublicDeclaredFlags` to `PatchConstants` to cover a set of commonly used flags to get methods post-publicizing
* Added a replacement to LINQ's `.Single()` - `.SingleCustom()` which has improved logging to help with debugging Module code. Replaced all `.Single()` usages where applicable
* Replaced most method info fetching with `AccessTools` for consistency and better readability, especially in places where methods were being retrieved by their name anyways

**NOTE:**
As a side effect of publicizing all properties, some property access code such as `Player.Position` will now show "ambiguous reference" errors during compile, due to there being multiple interfaces with the Property name being defined on the class. The way to get around this is to use a cast to an explicit interface
Example:
```cs
Singleton<GameWorld>.Instance.MainPlayer.Position
```
will now need to be
```cs
((IPlayer)Singleton<GameWorld>.Instance.MainPlayer).Position
```

Co-authored-by: Terkoiz <terkoiz@spt.dev>
Reviewed-on: SPT-AKI/Modules#58
Co-authored-by: Terkoiz <terkoiz@noreply.dev.sp-tarkov.com>
Co-committed-by: Terkoiz <terkoiz@noreply.dev.sp-tarkov.com>
2024-01-13 22:08:29 +00:00

73 lines
2.7 KiB
C#

using Aki.Common.Utils;
using Aki.Reflection.Patching;
using BepInEx.Bootstrap;
using EFT.Communications;
using EFT.UI;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
namespace Aki.SinglePlayer.Patches.MainMenu
{
/***
* On the first show of the main menu, check if any BepInEx plugins have failed to load, and inform
* the user. This is done via a toast in the bottom right, with a more detailed console message, as
* well as having the errors forwarded to the server console
**/
internal class PluginErrorNotifierPatch : ModulePatch
{
private static bool _messageShown = false;
protected override MethodBase GetTargetMethod()
{
// We don't really care which "Show" method is returned - either will do
return typeof(MenuScreen).GetMethods().First(m => m.Name == nameof(MenuScreen.Show));
}
[PatchPostfix]
private static void PatchPostfix()
{
var failedPluginCount = Chainloader.DependencyErrors.Count;
// Skip if we've already shown the message, or there are no errors
if (_messageShown || failedPluginCount == 0)
{
return;
}
// Show a toast in the bottom right of the screen indicating how many plugins failed to load
var consoleHeaderMessage = $"{failedPluginCount} plugin{(failedPluginCount > 1 ? "s" : "")} failed to load due to errors";
var toastMessage = $"{consoleHeaderMessage}. Please check the console for details.";
NotificationManagerClass.DisplayMessageNotification(toastMessage, ENotificationDurationType.Infinite, ENotificationIconType.Alert, Color.red);
// Build the error message we'll put in the BepInEx and in-game consoles
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"{consoleHeaderMessage}:");
foreach (string error in Chainloader.DependencyErrors)
{
stringBuilder.AppendLine(error);
}
var errorMessage = stringBuilder.ToString();
// Show an error in the BepInEx console/log file
Logger.LogError(errorMessage);
// Show errors in the server console
ServerLog.Error("Aki.Singleplayer", errorMessage);
// Show an error in the in-game console, we have to write this in reverse order because the
// in-game console shows newer messages at the top
foreach (string line in errorMessage.Split('\n').Reverse())
{
if (line.Length > 0)
{
ConsoleScreen.LogError(line);
}
}
_messageShown = true;
}
}
}