Decompiled source of LethalDiagnostics v1.0.0

LethalDiagnostics.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LethalDiagnostics")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("LethalDiagnostics")]
[assembly: AssemblyTitle("LethalDiagnostics")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LethalDiagnostics
{
	[BepInPlugin("com.nadre.lethaldiagnostics", "LethalDiagnostics", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private sealed class <DebouncedWriteLoop>d__7 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public Plugin <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DebouncedWriteLoop>d__7(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					break;
				case 1:
					<>1__state = -1;
					try
					{
						DiagnosticsManager.WriteReportDebounced();
					}
					catch
					{
					}
					break;
				}
				<>2__current = (object)new WaitForSeconds(5f);
				<>1__state = 1;
				return true;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		public const string ModGUID = "com.nadre.lethaldiagnostics";

		public const string ModName = "LethalDiagnostics";

		public const string ModVersion = "1.0.0";

		private readonly Harmony harmony = new Harmony("com.nadre.lethaldiagnostics");

		internal static Plugin Instance;

		internal static ManualLogSource LoggerInstance;

		private void Awake()
		{
			Instance = this;
			LoggerInstance = ((BaseUnityPlugin)this).Logger;
			LoggerInstance.LogInfo((object)"LethalDiagnostics v1.0.0 en cours de chargement...");
			Logger.Listeners.Add((ILogListener)(object)new DiagnosticsLogListener());
			SceneManager.sceneLoaded += OnSceneLoaded;
			SceneManager.sceneUnloaded += OnSceneUnloaded;
			harmony.PatchAll(typeof(Plugin).Assembly);
			DiagnosticsManager.InitializeReport();
			((MonoBehaviour)this).StartCoroutine(DebouncedWriteLoop());
			LoggerInstance.LogInfo((object)"LethalDiagnostics v1.0.0 initialisé avec succès.");
		}

		[IteratorStateMachine(typeof(<DebouncedWriteLoop>d__7))]
		private IEnumerator DebouncedWriteLoop()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DebouncedWriteLoop>d__7(0)
			{
				<>4__this = this
			};
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			DiagnosticsManager.OnSceneLoaded(((Scene)(ref scene)).name);
		}

		private void OnSceneUnloaded(Scene scene)
		{
			DiagnosticsManager.OnSceneUnloaded(((Scene)(ref scene)).name);
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			SceneManager.sceneUnloaded -= OnSceneUnloaded;
			try
			{
				DiagnosticsManager.ForceWriteReport();
			}
			catch
			{
			}
		}
	}
	public class DiagnosticsLogListener : ILogListener, IDisposable
	{
		public void LogEvent(object sender, LogEventArgs eventArgs)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				DiagnosticsManager.AnalyzeLog(eventArgs.Source.SourceName, eventArgs.Level, eventArgs.Data?.ToString());
			}
			catch
			{
			}
		}

		public void Dispose()
		{
		}
	}
	public static class DiagnosticsManager
	{
		private static readonly object _lock = new object();

		private static readonly List<string> LoadedPluginsList = new List<string>();

		private static readonly List<string> SceneLoadHistory = new List<string>();

		private static string _currentLoadingScene = "Aucune (Menu Principal)";

		private static DateTime _sceneLoadStartTime = DateTime.MinValue;

		private static readonly HashSet<string> CriticalExceptions = new HashSet<string>();

		private static readonly HashSet<string> MissingComponents = new HashSet<string>();

		private static readonly HashSet<string> MapGenerationAlerts = new HashSet<string>();

		private static readonly HashSet<string> NetworkSyncAlerts = new HashSet<string>();

		private static readonly HashSet<string> MimicDiagnostics = new HashSet<string>();

		private static readonly HashSet<string> ModInterferences = new HashSet<string>();

		private static readonly Dictionary<string, int> ExceptionCounts = new Dictionary<string, int>();

		private static readonly Dictionary<string, int> MissingComponentCounts = new Dictionary<string, int>();

		private static readonly Dictionary<string, int> MapGenAlertCounts = new Dictionary<string, int>();

		private static readonly Dictionary<string, int> NetworkSyncCounts = new Dictionary<string, int>();

		private static readonly Dictionary<string, int> MimicDiagCounts = new Dictionary<string, int>();

		private static readonly Dictionary<string, int> ModInterferenceCounts = new Dictionary<string, int>();

		private static bool _needsWrite = false;

		private static bool _isWriting = false;

		private static readonly object _writeLock = new object();

		public static string GetReportPath()
		{
			try
			{
				string text = Path.Combine(Paths.BepInExRootPath, "cache", "LethalDiagnostics");
				if (!Directory.Exists(text))
				{
					Directory.CreateDirectory(text);
				}
				return Path.Combine(text, "LethalDiagnosticsReport.md");
			}
			catch
			{
				return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LethalDiagnosticsReport.md");
			}
		}

		public static void InitializeReport()
		{
			lock (_lock)
			{
				ScanLoadedPlugins();
				RequestWrite();
				ForceWriteReport();
			}
		}

		public static void ScanLoadedPlugins()
		{
			lock (_lock)
			{
				LoadedPluginsList.Clear();
				try
				{
					foreach (PluginInfo value in Chainloader.PluginInfos.Values)
					{
						string name = value.Metadata.Name;
						string text = value.Metadata.Version.ToString();
						string gUID = value.Metadata.GUID;
						LoadedPluginsList.Add("- **" + name + "** (`" + gUID + "`) - v" + text);
					}
				}
				catch (Exception ex)
				{
					LoadedPluginsList.Add("- *Erreur lors du scan des plugins : " + ex.Message + "*");
				}
			}
		}

		public static void OnSceneLoaded(string sceneName)
		{
			lock (_lock)
			{
				if (!(sceneName == "InitScene"))
				{
					if (_sceneLoadStartTime != DateTime.MinValue)
					{
						TimeSpan timeSpan = DateTime.Now - _sceneLoadStartTime;
						string arg = ((timeSpan.TotalSeconds > 10.0) ? $"⚠\ufe0f **{timeSpan.TotalSeconds:F2}s** (Lent - potentiel goulot d'étranglement)" : $"**{timeSpan.TotalSeconds:F2}s** (Normal)");
						SceneLoadHistory.Add($"- **{sceneName}** : Chargé en {arg} [Le {DateTime.Now:HH:mm:ss}]");
					}
					else
					{
						SceneLoadHistory.Add($"- **{sceneName}** : Chargé directement [Le {DateTime.Now:HH:mm:ss}]");
					}
					_currentLoadingScene = sceneName;
					_sceneLoadStartTime = DateTime.MinValue;
					RequestWrite();
				}
			}
		}

		public static void OnSceneUnloaded(string sceneName)
		{
			lock (_lock)
			{
				_currentLoadingScene = "Chargement en cours depuis " + sceneName + "...";
				_sceneLoadStartTime = DateTime.Now;
			}
		}

		public static void AnalyzeLog(string source, LogLevel level, string? message)
		{
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Invalid comparison between Unknown and I4
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Invalid comparison between Unknown and I4
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Invalid comparison between Unknown and I4
			//IL_0355: Unknown result type (might be due to invalid IL or missing references)
			//IL_0357: Invalid comparison between Unknown and I4
			//IL_0417: Unknown result type (might be due to invalid IL or missing references)
			//IL_0419: Invalid comparison between Unknown and I4
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Invalid comparison between Unknown and I4
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Invalid comparison between Unknown and I4
			if (string.IsNullOrEmpty(message))
			{
				return;
			}
			lock (_lock)
			{
				if (message.Contains("Scene that began loading:"))
				{
					_sceneLoadStartTime = DateTime.Now;
					return;
				}
				if ((message.Contains("RPC") || message.Contains("packet") || message.Contains("desync") || message.Contains("NetworkVariable") || message.Contains("client received") || message.Contains("NetworkBehaviour") || message.Contains("loot") || message.Contains("item")) && (message.Contains("NetworkVariable") || message.Contains("RPC") || message.Contains("desync") || message.Contains("packet loss") || message.Contains("Lag") || (int)level == 4 || (int)level == 2))
				{
					string text = "[" + source + "] " + GetFirstLine(message);
					if (!NetworkSyncAlerts.Contains(text))
					{
						NetworkSyncAlerts.Add(text);
						NetworkSyncCounts[text] = 1;
					}
					else
					{
						NetworkSyncCounts[text]++;
					}
					RequestWrite();
				}
				if ((source.Contains("Mirage") || source.Contains("Masked") || message.Contains("MaskedPlayerEnemy") || message.Contains("mimic") || message.Contains("Zombie") || message.Contains("voice") || message.Contains("Dissonance") || source.Contains("Dissonance")) && (message.Contains("spawn") || message.Contains("kill") || message.Contains("voice") || message.Contains("sync") || message.Contains("mimic") || message.Contains("recording") || message.Contains("buffer") || (int)level == 4 || (int)level == 2))
				{
					string text2 = "[" + source + "] " + GetFirstLine(message);
					if (!MimicDiagnostics.Contains(text2))
					{
						MimicDiagnostics.Add(text2);
						MimicDiagCounts[text2] = 1;
					}
					else
					{
						MimicDiagCounts[text2]++;
					}
					RequestWrite();
				}
				if ((source.Contains("MoreShipUpgrades") || source.Contains("DungeonPlus") || source.Contains("LethalLevelLoader") || message.Contains("MoreShipUpgrades") || message.Contains("DungeonPlus") || message.Contains("LethalLevelLoader") || message.Contains("compatibility") || message.Contains("conflict") || message.Contains("override")) && ((int)level == 4 || (int)level == 2 || message.Contains("conflict") || message.Contains("failed")))
				{
					string text3 = "[" + source + "] " + GetFirstLine(message);
					if (!ModInterferences.Contains(text3))
					{
						ModInterferences.Add(text3);
						ModInterferenceCounts[text3] = 1;
					}
					else
					{
						ModInterferenceCounts[text3]++;
					}
					RequestWrite();
				}
				if ((message.Contains("Exception") || message.Contains("Error") || message.Contains("Reference not set") || (int)level == 2) && (message.Contains("NullReferenceException") || message.Contains("MissingMethodException") || message.Contains("TypeLoadException") || message.Contains("ReflectionTypeLoadException") || message.Contains("IndexOutOfRangeException") || message.Contains("Stack overflow") || message.Contains("ArgumentNullException")))
				{
					string text4 = "Exception Générique";
					Match match = Regex.Match(message, "\\w+Exception");
					if (match.Success)
					{
						text4 = match.Value;
					}
					string firstLine = GetFirstLine(message);
					string text5 = "[" + text4 + "] " + firstLine + " (Source: " + source + ")";
					if (!CriticalExceptions.Contains(text5))
					{
						CriticalExceptions.Add(text5);
						ExceptionCounts[text5] = 1;
					}
					else
					{
						ExceptionCounts[text5]++;
					}
					RequestWrite();
				}
				else if ((message.Contains("missing") || message.Contains("referenced script") || message.Contains("could not be instantiated")) && (message.Contains("referenced script") || message.Contains("Behaviour is missing") || message.Contains("Game Object")))
				{
					string text6 = message;
					Match match2 = Regex.Match(message, "Game Object '([^']+)'");
					text6 = ((!match2.Success) ? GetFirstLine(message) : ("Script manquant sur l'objet '" + match2.Groups[1].Value + "' (Signalé par " + source + ")"));
					if (!MissingComponents.Contains(text6))
					{
						MissingComponents.Add(text6);
						MissingComponentCounts[text6] = 1;
					}
					else
					{
						MissingComponentCounts[text6]++;
					}
					RequestWrite();
				}
				else if ((message.Contains("RuntimeNavMeshBuilder") || message.Contains("Dungeon") || message.Contains("NavMesh")) && (message.Contains("skipped because it does not allow read access") || message.Contains("RuntimeDungeon component missing") || message.Contains("Dungeon flow") || message.Contains("failed to generate")))
				{
					string text7 = "[" + source + "] " + GetFirstLine(message);
					if (!MapGenerationAlerts.Contains(text7))
					{
						MapGenerationAlerts.Add(text7);
						MapGenAlertCounts[text7] = 1;
					}
					else
					{
						MapGenAlertCounts[text7]++;
					}
					RequestWrite();
				}
			}
		}

		private static string GetFirstLine(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return string.Empty;
			}
			int num = text.IndexOf('\n');
			if (num > 0)
			{
				return text.Substring(0, num).Trim();
			}
			return text.Trim();
		}

		public static void RequestWrite()
		{
			lock (_lock)
			{
				_needsWrite = true;
			}
		}

		public static void WriteReportDebounced()
		{
			lock (_writeLock)
			{
				if (!_needsWrite || _isWriting)
				{
					return;
				}
				_isWriting = true;
				_needsWrite = false;
			}
			string reportContent;
			lock (_lock)
			{
				reportContent = GenerateReportContent();
			}
			Task.Run(delegate
			{
				try
				{
					string reportPath = GetReportPath();
					string directoryName = Path.GetDirectoryName(reportPath);
					if (!Directory.Exists(directoryName))
					{
						Directory.CreateDirectory(directoryName);
					}
					File.WriteAllText(reportPath, reportContent, Encoding.UTF8);
				}
				catch
				{
				}
				finally
				{
					lock (_writeLock)
					{
						_isWriting = false;
					}
				}
			});
		}

		public static void ForceWriteReport()
		{
			lock (_lock)
			{
				try
				{
					string contents = GenerateReportContent();
					string reportPath = GetReportPath();
					string directoryName = Path.GetDirectoryName(reportPath);
					if (!Directory.Exists(directoryName))
					{
						Directory.CreateDirectory(directoryName);
					}
					File.WriteAllText(reportPath, contents, Encoding.UTF8);
				}
				catch
				{
				}
			}
		}

		private static string GetSessionStatus()
		{
			if ((Object)(object)StartOfRound.Instance == (Object)null)
			{
				return "- **Statut de session :** `En attente du lancement d'une partie (Menu Principal)`\n";
			}
			string text = (((Object)(object)StartOfRound.Instance.currentLevel != (Object)null) ? StartOfRound.Instance.currentLevel.PlanetName : "Orbite / Vaisseau");
			int num = 0;
			if (StartOfRound.Instance.allPlayerScripts != null)
			{
				PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
				foreach (PlayerControllerB val in allPlayerScripts)
				{
					if ((Object)(object)val != (Object)null && val.isPlayerControlled)
					{
						num++;
					}
				}
			}
			string text2 = "Inconnu";
			if (StartOfRound.Instance.allPlayerScripts != null && StartOfRound.Instance.allPlayerScripts.Length != 0 && (Object)(object)StartOfRound.Instance.allPlayerScripts[0] != (Object)null)
			{
				text2 = StartOfRound.Instance.allPlayerScripts[0].playerUsername;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("- **Lune active :** `" + text + "`");
			stringBuilder.AppendLine($"- **Joueurs actifs contrôlés :** `{num}`");
			stringBuilder.AppendLine("- **Nom de l'Hôte (Host) :** `" + text2 + "`");
			stringBuilder.AppendLine("- **Scène active Unity :** `" + _currentLoadingScene + "`");
			return stringBuilder.ToString();
		}

		private static string GenerateReportContent()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# \ud83d\udee0\ufe0f Rapport de Diagnostics & Analyse de Bugs (Lethal Company)");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("*Généré dynamiquement par le mod **LethalDiagnostics** dans le cache du profil actif.*  ");
			stringBuilder.AppendLine($"*Dernière mise à jour : {DateTime.Now:dd/MM/yyyy à HH:mm:ss}*  ");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("---");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("## ⏱\ufe0f Statut de Session & Temps de Chargement");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine(GetSessionStatus());
			stringBuilder.AppendLine("### Historique de navigation des lunes :");
			if (SceneLoadHistory.Count == 0)
			{
				stringBuilder.AppendLine("*Aucune lune chargée pour le moment. En attente du décollage depuis l'orbite...*");
			}
			else
			{
				foreach (string item in SceneLoadHistory)
				{
					stringBuilder.AppendLine(item);
				}
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("---");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("## \ud83c\udfad Diagnostics des Mimics & Voix (Mirage / Masked)");
			stringBuilder.AppendLine("Suivi de l'apparition des mimics, de l'imitation vocale et des alertes de micro / tampon audio.");
			stringBuilder.AppendLine();
			if (MimicDiagnostics.Count == 0)
			{
				stringBuilder.AppendLine("> [!NOTE]\n> **Aucun diagnostic de Mimic.** Mirage et les autres systèmes de voix n'ont pas encore signalé d'anomalies.");
			}
			else
			{
				foreach (string mimicDiagnostic in MimicDiagnostics)
				{
					int num = MimicDiagCounts[mimicDiagnostic];
					stringBuilder.AppendLine($"- \ud83d\udc64 `{mimicDiagnostic}` (**x{num}**)");
				}
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("---");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("## \ud83d\udea8 Alertes de Synchronisation Réseau, RPC & Desync de Loot");
			stringBuilder.AppendLine("Ce panneau regroupe les pertes de paquets, les retards d'RPC et les décalages de variables réseau pouvant provoquer des objets flottants ou des desyncs entre joueurs.");
			stringBuilder.AppendLine();
			if (NetworkSyncAlerts.Count == 0)
			{
				stringBuilder.AppendLine("> [!NOTE]\n> **Réseau stable.** Aucune anomalie d'RPC, perte de paquet majeure ou désynchronisation de loot n'a été détectée.");
			}
			else
			{
				foreach (string networkSyncAlert in NetworkSyncAlerts)
				{
					int num2 = NetworkSyncCounts[networkSyncAlert];
					stringBuilder.AppendLine($"- \ud83d\udce1 `{networkSyncAlert}` (**x{num2}**)");
				}
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("---");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("## \ud83c\udf9b\ufe0f Diagnostics d'Interférences entre Mods (LGU, DungeonPlus, LLL)");
			stringBuilder.AppendLine("Affiche les conflits d'injection de scripts, les surcharges d'assets ou les erreurs de compatibilité liées aux upgrades et intérieurs.");
			stringBuilder.AppendLine();
			if (ModInterferences.Count == 0)
			{
				stringBuilder.AppendLine("> [!NOTE]\n> **Compatibilité optimale.** Aucun conflit majeur signalé par MoreShipUpgrades, DungeonPlus ou LethalLevelLoader.");
			}
			else
			{
				foreach (string modInterference in ModInterferences)
				{
					int num3 = ModInterferenceCounts[modInterference];
					stringBuilder.AppendLine($"- ⚠\ufe0f `{modInterference}` (**x{num3}**)");
				}
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("---");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("## \ud83d\uded1 Exceptions Critiques & Erreurs de Code");
			stringBuilder.AppendLine("Erreurs majeures de programmation (NullReference, MissingMethod) pouvant causer des plantages ou des freezes d'interface.");
			stringBuilder.AppendLine();
			if (CriticalExceptions.Count == 0)
			{
				stringBuilder.AppendLine("> [!NOTE]\n> **Aucune exception critique détectée.** Tous les scripts et liaisons réseau de vos mods fonctionnent normalement.");
			}
			else
			{
				foreach (string criticalException in CriticalExceptions)
				{
					int num4 = ExceptionCounts[criticalException];
					stringBuilder.AppendLine($"- \ud83d\uded1 `{criticalException}` (**x{num4}**)");
				}
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("---");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("## \ud83e\udde9 Composants ou Scripts Manquants");
			stringBuilder.AppendLine("Indique si des cartes ou des éléments de scrap tentent d'instancier des composants absents de vos dossiers.");
			stringBuilder.AppendLine();
			if (MissingComponents.Count == 0)
			{
				stringBuilder.AppendLine("> [!NOTE]\n> **Aucune dépendance manquante.** Tous les objets et éléments du donjon sont correctement résolus.");
			}
			else
			{
				foreach (string missingComponent in MissingComponents)
				{
					int num5 = MissingComponentCounts[missingComponent];
					stringBuilder.AppendLine($"- \ud83e\udde9 {missingComponent} (**x{num5}**)");
				}
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("---");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("## \ud83d\uddfa\ufe0f Anomalies de Génération de Donjon (DunGen / NavMesh)");
			stringBuilder.AppendLine("Erreurs bloquant les chemins de navigation des monstres ou empêchant la liaison des dalles.");
			stringBuilder.AppendLine();
			if (MapGenerationAlerts.Count == 0)
			{
				stringBuilder.AppendLine("> [!NOTE]\n> **NavMesh et Donjon optimaux.** L'IA des monstres peut circuler normalement et les dalles se sont générées correctement.");
			}
			else
			{
				foreach (string mapGenerationAlert in MapGenerationAlerts)
				{
					int num6 = MapGenAlertCounts[mapGenerationAlert];
					stringBuilder.AppendLine($"- \ud83c\udf9b\ufe0f {mapGenerationAlert} (**x{num6}**)");
				}
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("---");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("## \ud83d\udd0c Fiche Technique : Liste des Mods Actifs (Chainloader BepInEx)");
			stringBuilder.AppendLine("Copie cette liste si tu dois partager ton diagnostic avec ton groupe ou le créateur d'un mod.");
			stringBuilder.AppendLine();
			if (LoadedPluginsList.Count == 0)
			{
				stringBuilder.AppendLine("*Aucun plugin actif détecté ou scan non complété.*");
			}
			else
			{
				foreach (string loadedPlugins in LoadedPluginsList)
				{
					stringBuilder.AppendLine(loadedPlugins);
				}
			}
			return stringBuilder.ToString();
		}
	}
}