Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of AzuDevMod v1.0.10
AzuDevMod.dll
Decompiled 6 days agousing System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using AzuDevMod; using AzuDevMod.Components; using AzuDevMod.Filtering; using AzuDevMod.Processing; using AzuDevMod.Util; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.Cil; using MonoMod.RuntimeDetour; using MonoMod.Utils; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("AzuDevMod")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Azumatt")] [assembly: AssemblyProduct("AzuDevMod")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.0.10")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.10.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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] [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; } } } public class MissingFieldDetector : MonoBehaviour { private static Dictionary<string, List<string>> modsUsingMissingFields = new Dictionary<string, List<string>>(); private static List<Tuple<string, string>> fieldsToCheck = new List<Tuple<string, string>>(); public static void Init(List<Tuple<string, string>> fields) { fieldsToCheck = fields; ScanPatchedMethodsForMissingField(); ReportModsUsingMissingField(); } private static void ScanPatchedMethodsForMissingField() { foreach (Tuple<string, string> item3 in fieldsToCheck) { string className = item3.Item1; string item = item3.Item2; foreach (MethodBase item4 in from m in Harmony.GetAllPatchedMethods() where m.DeclaringType.FullName == className select m) { Patches patchInfo = Harmony.GetPatchInfo(item4); if (patchInfo == null) { continue; } foreach (Patch item5 in patchInfo.Prefixes.Concat(patchInfo.Postfixes).Concat(patchInfo.Transpilers)) { try { if (InstructionsAccessField(MethodBodyReader.ReadInstructions(item5.PatchMethod), className, item)) { string name = item5.PatchMethod.Module.Assembly.GetName().Name; if (!modsUsingMissingFields.ContainsKey(name)) { modsUsingMissingFields[name] = new List<string>(); } string item2 = className + "." + item; if (!modsUsingMissingFields[name].Contains(item2)) { modsUsingMissingFields[name].Add(item2); } } } catch (Exception) { } } } } } private static bool InstructionsAccessField(IEnumerable<Instruction> instructions, string className, string fieldName) { foreach (Instruction instruction in instructions) { object operand = instruction.Operand; FieldReference val = (FieldReference)((operand is FieldReference) ? operand : null); if (val != null && ((MemberReference)((MemberReference)val).DeclaringType).FullName == className && ((MemberReference)val).Name == fieldName) { return true; } } return false; } private static void ReportModsUsingMissingField() { if (modsUsingMissingFields.Count > 0) { foreach (KeyValuePair<string, List<string>> modsUsingMissingField in modsUsingMissingFields) { AzuDevModPlugin.AzuDevModLogger.LogWarning((object)("Mod '" + modsUsingMissingField.Key + "' is using the following missing fields: " + string.Join(", ", modsUsingMissingField.Value))); } return; } AzuDevModPlugin.AzuDevModLogger.LogInfo((object)"No mods found using the missing fields."); } } public static class MethodBodyReader { public static List<Instruction> ReadInstructions(MethodBase method) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown string fullyQualifiedName = method.Module.FullyQualifiedName; ReaderParameters val = new ReaderParameters { ReadSymbols = false }; MethodDefinition? obj = ((IEnumerable<MethodDefinition>)ModuleDefinition.ReadModule(fullyQualifiedName, val).GetType(method.DeclaringType.FullName).Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == method.Name)); object obj2; if (obj == null) { obj2 = null; } else { MethodBody body = obj.Body; obj2 = ((body != null) ? ((IEnumerable<Instruction>)body.Instructions).ToList() : null); } if (obj2 == null) { obj2 = new List<Instruction>(); } return (List<Instruction>)obj2; } } namespace AzuDevMod { [BepInPlugin("zzzzzzzzzzAzumatt.AzuDevMod", "AzuDevMod", "1.0.10")] public class AzuDevModPlugin : BaseUnityPlugin { public enum Toggle { Off, On } internal const string ModName = "AzuDevMod"; internal const string ModVersion = "1.0.10"; internal const string Author = "Azumatt"; private const string ModGUID = "zzzzzzzzzzAzumatt.AzuDevMod"; private static string ConfigFileName = "zzzzzzzzzzAzumatt.AzuDevMod.cfg"; private static string ConfigFileFullPath; internal static readonly Harmony _harmony; public static readonly ManualLogSource AzuDevModLogger; public static ConfigEntry<Toggle> LogDuplicateGameObjectAdditions; public static ConfigEntry<Toggle> LogDestroyedZNetViews; public static ConfigEntry<Toggle> LogUnregisteredZNetViews; public static ConfigEntry<Toggle> LogUnpatchAll; public static ConfigEntry<Toggle> LogAssetBundleIssues; public static ConfigEntry<Toggle> EnableWhitelistPerException; public static ConfigEntry<Toggle> EnableWhitelistPerFrame; public static ConfigEntry<Toggle> EnableBlacklistPerException; public ConfigEntry<Toggle> EnableBlacklistPerFrame; public static ConfigEntry<string> ExceptionBlacklist; public static ConfigEntry<string> FrameBlacklist; private void Awake() { //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Expected O, but got Unknown LogDestroyedZNetViews = ((BaseUnityPlugin)this).Config.Bind<Toggle>("1 - General User", "Log Destroyed ZNetViews", Toggle.On, "Logs invalid ZNetView destructions to the console. Useful for finding mods that has ZNetView's destroyed without being destroyed through the ZNetScene."); LogUnregisteredZNetViews = ((BaseUnityPlugin)this).Config.Bind<Toggle>("1 - General User", "Log Unregistered ZNetViews", Toggle.On, "Logs unregistered ZNetViews to the console. Useful for finding mods that has ZNetView's with prefabs not registered in the ZNetScene."); LogUnpatchAll = ((BaseUnityPlugin)this).Config.Bind<Toggle>("1 - General User", "Log Unpatch All", Toggle.On, "Logs mods that call UnpatchAll to the console. Useful for finding mods that are unpatching all patches at game close causing issues with other mods."); LogAssetBundleIssues = ((BaseUnityPlugin)this).Config.Bind<Toggle>("1 - General User", "Log Asset Bundle Issues", Toggle.On, "Logs asset bundle issues to the console. Useful for identifying mods that load asset bundles incorrectly or attempt to retrieve prefabs from a bundle that doesn't contain them...etc."); LogDuplicateGameObjectAdditions = ((BaseUnityPlugin)this).Config.Bind<Toggle>("1 - Mod Developer", "Log Duplicate GameObject Additions", Toggle.Off, "Logs duplicate GameObject additions to the console. Mainly intended for mod developer debugging. Note that this might not work if your mod is obfuscated. Use this on a clean version of your mod. Useful for finding duplicate key issues for ZNetScene, such as attempting to add duplicate GameObjects to ZNetScene's prefab list."); EnableWhitelistPerException = ((BaseUnityPlugin)this).Config.Bind<Toggle>("2 - ExceptionFilter", "WhitelistEnabled", Toggle.On, "The per-exception whitelist filters out exceptions that do not originate from common game assemblies or BepinEx plugins.\nDisable this to catch everything. Will increase the log file size."); EnableBlacklistPerException = ((BaseUnityPlugin)this).Config.Bind<Toggle>("2 - ExceptionFilter", "BlacklistEnabled", Toggle.Off, "The per-exception blacklist filters out exceptions that match the given patterns.\nThis is useful if you know a specific assembly throws errors you can ignore"); ExceptionBlacklist = ((BaseUnityPlugin)this).Config.Bind<string>("2 - ExceptionFilter", "Blacklist", "UniverseLib.Mono, UnityExplorer.", "A comma-separated list of assembly names to ignore in exception processing\nThis will be matched against the beginning of each assembly name so e.g. `UnityEngine.` will match all UnityEngine assemblies"); EnableWhitelistPerFrame = ((BaseUnityPlugin)this).Config.Bind<Toggle>("3 - FrameFilter", "WhitelistEnabled", Toggle.Off, "The per-frame whitelist will remove frames inside of exceptions that do not originate from common game assemblies.\nEnable this to reduce log file size by cutting out less relevant frames"); EnableBlacklistPerFrame = ((BaseUnityPlugin)this).Config.Bind<Toggle>("3 - FrameFilter", "BlacklistEnabled", Toggle.Off, "The per-frame blacklist filters out exceptions that match the given patterns.\nThis is useful if you know a specific assembly throws errors you can ignore"); FrameBlacklist = ((BaseUnityPlugin)this).Config.Bind<string>("3 - FrameFilter", "Blacklist", "mscorlib", "A comma-separated list of assembly names to ignore in individual frame processing\nThis will be matched against the beginning of each assembly name so e.g. `UnityEngine.` will match all UnityEngine assemblies"); AzuDevModBlacklist.UpdateLists_Internal(); ILHook.OnDetour = (Func<ILHook, MethodBase, Manipulator, bool>)Delegate.Combine(ILHook.OnDetour, new Func<ILHook, MethodBase, Manipulator, bool>(AzuDevModPatchListener.ListenForPatch)); CheckForInvalidPatches(); _harmony.PatchAll(); SetupWatcher(); Object.DontDestroyOnLoad((Object)new GameObject("UDAMain", new Type[2] { typeof(AzuDevModExceptionBroker), typeof(AzuDevModPostChainloadRunner) }) { hideFlags = (HideFlags)61 }); } private void Start() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) AssetLoadTracker.MapPrefabsToBundles(); AssetLoadTracker.MapBundlesToAssemblies(); List<Tuple<string, string>> list = new List<Tuple<string, string>>(); if (Version.CurrentVersion >= new GameVersion(0, 218, 16)) { list.Add(new Tuple<string, string>("Player", "m_firstSpawn")); list.Add(new Tuple<string, string>("Player", "m_valkyrie")); } if (Version.CurrentVersion >= new GameVersion(0, 218, 15)) { list.Add(new Tuple<string, string>("UnityEngine.UI.InputField.Terminal", "m_input")); } list.Add(new Tuple<string, string>("Hud", "m_pieceBarPosX")); list.Add(new Tuple<string, string>("Pickable", "m_respawnTimeMinutes")); MissingFieldDetector.Init(list); } private void CheckForInvalidPatches() { try { List<MethodBase> list = Harmony.GetAllPatchedMethods().ToList(); AzuDevModLogger.LogInfo((object)$"Total patched methods found: {list.Count}"); foreach (MethodBase item in list) { Patches patchInfo = Harmony.GetPatchInfo(item); if (patchInfo == null) { AzuDevModLogger.LogWarning((object)$"No patch info found for method: {item.DeclaringType}.{item.Name}"); continue; } CheckPatches(patchInfo.Prefixes, item, "prefix"); CheckPatches(patchInfo.Postfixes, item, "postfix"); CheckPatches(patchInfo.Transpilers, item, "transpiler"); CheckPatches(patchInfo.Finalizers, item, "finalizer"); } } catch (Exception ex) { AzuDevModLogger.LogError((object)("Error while checking for invalid patches: " + ex.Message)); } } private void CheckPatches(IEnumerable<Patch> patches, MethodBase originalMethod, string patchType) { if (patches == null) { return; } foreach (Patch patch in patches) { try { if (!MethodExists(patch.PatchMethod)) { AzuDevModLogger.LogWarning((object)$"Harmony instance {patch.owner} has a {patchType} patch on {originalMethod.DeclaringType}.{originalMethod.Name} that no longer exists."); } } catch (Exception ex) { AzuDevModLogger.LogError((object)$"Error while checking {patchType} patch from {patch.owner} on {originalMethod.DeclaringType}.{originalMethod.Name}: {ex.Message}"); } } } private bool MethodExists(MethodInfo method) { try { if (method == null) { AzuDevModLogger.LogWarning((object)"Patch method is null"); return false; } Type declaringType = method.DeclaringType; if (declaringType == null) { AzuDevModLogger.LogWarning((object)("Declaring type for method " + method.Name + " is null")); return false; } MethodInfo? methodInfo = declaringType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == method.Name && ParametersMatch(m.GetParameters(), method.GetParameters())); if (methodInfo == null) { AzuDevModLogger.LogWarning((object)("Method " + method.Name + " with specified parameters not found in type " + declaringType.FullName)); } return methodInfo != null; } catch (Exception ex) { AzuDevModLogger.LogError((object)("Error while checking if method exists: " + ex.Message)); return false; } } private bool ParametersMatch(ParameterInfo[] parameters1, ParameterInfo[] parameters2) { if (parameters1.Length != parameters2.Length) { return false; } for (int i = 0; i < parameters1.Length; i++) { if (parameters1[i].ParameterType != parameters2[i].ParameterType) { return false; } } return true; } private void OnDestroy() { ((BaseUnityPlugin)this).Config.Save(); } private void SetupWatcher() { FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); fileSystemWatcher.Changed += ReadConfigValues; fileSystemWatcher.Created += ReadConfigValues; fileSystemWatcher.Renamed += ReadConfigValues; fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; fileSystemWatcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { if (!File.Exists(ConfigFileFullPath)) { return; } try { AzuDevModLogger.LogDebug((object)"ReadConfigValues called"); ((BaseUnityPlugin)this).Config.Reload(); ((BaseUnityPlugin)this).Config.Save(); } catch { AzuDevModLogger.LogError((object)("There was an issue loading your " + ConfigFileName)); AzuDevModLogger.LogError((object)"Please check your config entries for spelling and format!"); } } static AzuDevModPlugin() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; _harmony = new Harmony("zzzzzzzzzzAzumatt.AzuDevMod"); AzuDevModLogger = Logger.CreateLogSource("AzuDevMod"); LogDuplicateGameObjectAdditions = null; LogDestroyedZNetViews = null; LogUnregisteredZNetViews = null; LogUnpatchAll = null; LogAssetBundleIssues = null; } } public static class ToggleExtentions { public static bool IsOn(this AzuDevModPlugin.Toggle toggle) { return toggle == AzuDevModPlugin.Toggle.On; } public static bool IsOff(this AzuDevModPlugin.Toggle toggle) { return toggle == AzuDevModPlugin.Toggle.Off; } } } namespace AzuDevMod.Util { public class AssetLoadTracker { private static readonly Dictionary<string, string> PrefabToBundleMapping = new Dictionary<string, string>(); private static readonly Dictionary<string, Assembly> BundleToAssemblyMapping = new Dictionary<string, Assembly>(); internal static void MapPrefabsToBundles() { foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { foreach (string item in from name in allLoadedAssetBundle.GetAllAssetNames() where name.EndsWith(".prefab") select name) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item); PrefabToBundleMapping[fileNameWithoutExtension] = ((Object)allLoadedAssetBundle).name; } } } internal static void MapBundlesToAssemblies() { List<Assembly> list = Chainloader.PluginInfos.Select((KeyValuePair<string, PluginInfo> keyValuePair) => ((object)keyValuePair.Value.Instance).GetType().Assembly).ToList(); foreach (string bundleName in PrefabToBundleMapping.Values.Distinct()) { foreach (Assembly item in list) { try { if (item.GetManifestResourceNames().Any((string resourceName) => resourceName.EndsWith(bundleName))) { BundleToAssemblyMapping[bundleName] = item; break; } } catch (Exception arg) { AzuDevModPlugin.AzuDevModLogger.LogError((object)$"Error while getting manifest resource names for assembly {item.GetName().Name}: {arg}"); } } } } public static Assembly? GetAssemblyForPrefab(string prefabName) { if (PrefabToBundleMapping.TryGetValue(prefabName, out string value) && BundleToAssemblyMapping.TryGetValue(value, out Assembly value2)) { return value2; } return null; } public static string GetBundleForPrefab(string prefabName) { if (!PrefabToBundleMapping.TryGetValue(prefabName, out string value)) { return ""; } return value; } } internal static class Helpers { internal static Assembly? GetAssemblyByName(string name) { return AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly assembly) => assembly.GetName().Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)); } } public class LoggingMethods { public static void LogWithPrefabInfo(string messagePrefix, string identifier, string additionalInfo = "") { string text = AssetLoadTracker.GetBundleForPrefab(identifier.ToLowerInvariant()) ?? "Unknown Bundle"; Assembly assemblyForPrefab = AssetLoadTracker.GetAssemblyForPrefab(identifier.ToLowerInvariant()); StringBuilder stringBuilder = new StringBuilder(messagePrefix + ": " + identifier + ". "); if (assemblyForPrefab != null && !string.IsNullOrEmpty(text)) { stringBuilder.Append("The prefab is in the bundle '" + text + "' and the assembly '" + assemblyForPrefab.GetName().Name + "'. "); } else if (!string.IsNullOrEmpty(text)) { stringBuilder.Append("The prefab is in the bundle '" + text + "'. "); } else if (assemblyForPrefab != null) { stringBuilder.Append("The prefab is in the assembly '" + assemblyForPrefab.GetName().Name + "'. "); } else { stringBuilder.Append("Couldn't find full information for the prefab's mod. "); } if (!string.IsNullOrEmpty(additionalInfo)) { stringBuilder.AppendLine().Append("Additional Info: ").Append(additionalInfo); } stringBuilder.AppendLine("Full Stack Trace:" + Environment.NewLine + Environment.StackTrace); AzuDevModPlugin.AzuDevModLogger.LogError((object)stringBuilder.ToString()); } } public static class AzuDevModPatchListener { internal static bool ListenForPatch(ILHook hook, MethodBase @base, Manipulator manipulator) { if ((object)@base == null || manipulator == null || hook == null || ((Delegate)(object)manipulator).Target == null) { AzuDevModPlugin.AzuDevModLogger.LogInfo((object)"Skipping a null/empty patch"); return true; } AzuDevModPatchStorage.AddPatchInformation(@base, ((Delegate)(object)manipulator).Target.GetType().Assembly); return true; } } internal static class AzuDevModPatchStorage { private static readonly Dictionary<MethodBase, List<Assembly>> Patches = new Dictionary<MethodBase, List<Assembly>>(); private static readonly Assembly HarmonyAssembly = typeof(Harmony).Assembly; internal static bool AddPatchInformation(MethodBase method, Assembly assembly) { if ((object)assembly == null || assembly == HarmonyAssembly) { return false; } if (!Patches.TryGetValue(method, out List<Assembly> value)) { value = (Patches[method] = new List<Assembly>()); } if (value.Contains(assembly)) { return false; } value.Add(assembly); AzuDevModPlugin.AzuDevModLogger.LogInfo((object)("blame " + assembly.GetName().Name + " for " + GeneralExtensions.FullDescription(method))); return true; } internal static List<Assembly> GetPatchInformation(MethodBase method) { if (!Patches.TryGetValue(method, out List<Assembly> value)) { return new List<Assembly>(); } return value; } } public static class AzuDevModPluginMarshal { internal static readonly Dictionary<Assembly, PluginInfo> InfoCache = new Dictionary<Assembly, PluginInfo>(); public static void Run() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); foreach (string key in Chainloader.PluginInfos.Keys) { Assembly assembly = ((object)Chainloader.PluginInfos[key].Instance).GetType().Assembly; PluginInfo val = Chainloader.PluginInfos[key]; if (val != null) { AddToInfoCache(assembly, val); } } stopwatch.Stop(); AzuDevModPlugin.AzuDevModLogger.LogInfo((object)$"BepinEx Plugins Marshaled in {stopwatch.ElapsedMilliseconds}ms"); } internal static bool AddToInfoCache(Assembly assembly, PluginInfo info) { if (InfoCache.ContainsKey(assembly)) { return false; } InfoCache.Add(assembly, info); return true; } } } namespace AzuDevMod.Processing { public static class AzuDevModExceptionHandler { public static void Handle(Exception ex) { int num = 0; MethodBase? targetSite = ex.TargetSite; StackTrace stackTrace = new StackTrace(ex, fNeedFileInfo: true); Assembly assembly = targetSite?.DeclaringType.Assembly; bool flag = false; StackFrame[] frames; if ((object)assembly == null) { flag = true; _ = stackTrace.FrameCount; frames = stackTrace.GetFrames(); foreach (StackFrame stackFrame in frames) { if (stackFrame.HasMethod()) { assembly = stackFrame.GetMethod().DeclaringType.Assembly; break; } } if ((object)assembly == null) { return; } } if (!AzuDevModWhitelist.IsOnExceptionWhitelist(assembly) || AzuDevModBlacklist.IsOnExceptionBlacklist(assembly)) { return; } StringBuilder stringBuilder = new StringBuilder("\n\n--- Exception Handler ---\n\n"); stringBuilder.Append("Exception Caught: "); stringBuilder.AppendLine(ex.GetType().ToString()); stringBuilder.Append("Assembly: "); stringBuilder.Append(assembly.GetName().Name); if (flag) { stringBuilder.Append(" (Guess)"); } stringBuilder.AppendLine(); if (AzuDevModPluginMarshal.InfoCache.TryGetValue(assembly, out PluginInfo value)) { WritePluginInfo(stringBuilder, value, 1); } stringBuilder.Append("Message: "); stringBuilder.AppendLine(ex.Message); if (ex.Source != null) { stringBuilder.Append("Source: "); stringBuilder.AppendLine(ex.Source); } stringBuilder.AppendLine(); stringBuilder.Append(Tabs(1)); stringBuilder.AppendLine("--- Begin Frames ---"); stringBuilder.AppendLine(); frames = stackTrace.GetFrames(); foreach (StackFrame frame in frames) { StringBuilder stringBuilder2 = new StringBuilder(); if (DumpFrame(stringBuilder2, frame, 2)) { stringBuilder.Append(Tabs(1)); stringBuilder.Append("--FRAME "); num++; stringBuilder.Append(num); stringBuilder.AppendLine(":"); stringBuilder.Append(stringBuilder2.ToString()); } } stringBuilder.Append(Tabs(1)); stringBuilder.AppendLine("--- End Frames ---"); stringBuilder.AppendLine("\n--- End Exception Handler ---"); if (num > 0) { AzuDevModPlugin.AzuDevModLogger.LogError((object)stringBuilder); } static void DoHarmonyBlames(StringBuilder sb, ReadOnlyCollection<Patch>? Patches, int Indent) { if (Patches == null) { return; } foreach (Patch Patch in Patches) { DumpPatch(sb, Patch.PatchMethod.DeclaringType.Assembly, Indent); } } static bool DumpFrame(StringBuilder sb, StackFrame stackFrame2, int Indent) { MethodBase method = stackFrame2.GetMethod(); stackFrame2.GetILOffset(); bool result = false; if ((object)method != null) { Assembly assembly2 = method.DeclaringType.Assembly; if (AzuDevModWhitelist.IsOnFrameWhitelist(assembly2) && !AzuDevModBlacklist.IsOnFrameBlacklist(assembly2)) { result = true; sb.Append(Tabs(Indent)); sb.Append("In Assembly: "); sb.AppendLine(assembly2.GetName().Name); if (stackFrame2.GetFileName() != null) { sb.Append(Tabs(Indent)); sb.Append("Source: "); sb.Append(stackFrame2.GetFileName()); sb.Append(':'); sb.Append(stackFrame2.GetFileLineNumber()); sb.Append(','); sb.AppendLine(stackFrame2.GetFileColumnNumber().ToString()); } sb.Append(Tabs(Indent)); sb.Append("Target Method: "); sb.Append(method.DeclaringType.Name); sb.Append('.'); sb.AppendLine(method.Name); sb.AppendLine(); List<Assembly> patchInformation = AzuDevModPatchStorage.GetPatchInformation(method); Patches patchInfo = Harmony.GetPatchInfo(method); if (patchInformation.Count > 0 || patchInfo != null) { sb.Append(Tabs(Indent)); sb.AppendLine("Patched By:"); } foreach (Assembly item in patchInformation) { DumpPatch(sb, item, Indent + 1); } if (patchInfo != null) { DoHarmonyBlames(sb, patchInfo.Prefixes, Indent + 1); DoHarmonyBlames(sb, patchInfo.Postfixes, Indent + 1); DoHarmonyBlames(sb, patchInfo.Finalizers, Indent + 1); } } } return result; } static void DumpPatch(StringBuilder sb, Assembly assembly2, int Indent) { sb.Append(Tabs(Indent)); sb.AppendLine(assembly2.GetName().Name); if (AzuDevModPluginMarshal.InfoCache.TryGetValue(assembly2, out PluginInfo value2)) { WritePluginInfo(sb, value2, Indent); } } static string GetPluginDirSandboxed(PluginInfo info) { int num2 = info.Location.IndexOf($"{Path.DirectorySeparatorChar}plugins{Path.DirectorySeparatorChar}"); if (num2 < 0) { return "Unknown"; } int length = info.Location.Length - num2; return new string(info.Location.ToCharArray(), num2, length); } static string Tabs(int n) { return new string(' ', n * 2); } static void WritePluginInfo(StringBuilder sb, PluginInfo info, int Indent) { sb.AppendLine(); sb.Append(Tabs(Indent)); sb.AppendLine("Plugin Info"); sb.Append(Tabs(Indent + 1)); sb.Append("GUID: "); sb.AppendLine(info.Metadata.GUID); sb.Append(Tabs(Indent + 1)); sb.Append("NAME/VER: "); sb.Append(info.Metadata.Name); sb.Append("@"); sb.AppendLine(info.Metadata.Version.ToString()); sb.Append(Tabs(Indent + 1)); sb.Append("LOCATION: "); sb.AppendLine(GetPluginDirSandboxed(info)); sb.AppendLine(); } } [MethodImpl(MethodImplOptions.NoInlining)] internal static void DebugThrow() { throw new Exception("Debug throw"); } } internal static class AzuDevModExceptionProcessor { internal static readonly ConcurrentStack<Exception> Storage = new ConcurrentStack<Exception>(); internal static readonly int[] LastInExceptions = new int[10]; internal static int LastInPosition = 0; private static bool OperationUnderway = false; internal static void PushException(Exception ex) { int hashCode = ex.GetHashCode(); int[] lastInExceptions = LastInExceptions; foreach (int num in lastInExceptions) { if (hashCode == num) { return; } } LastInExceptions[LastInPosition] = hashCode; LastInPosition++; LastInPosition %= LastInExceptions.Length; Storage.Push(ex); } internal static void Run(Exception __instance) { if (OperationUnderway) { AzuDevModPlugin.AzuDevModLogger.LogWarning((object)"Exception processor is busy"); return; } OperationUnderway = true; try { if (__instance is MissingFieldException ex) { try { AzuDevModPlugin.AzuDevModLogger.LogWarning((object)("Exception: " + ex.Message)); AzuDevModPlugin.AzuDevModLogger.LogWarning((object)("StackTrace: " + ex.StackTrace)); foreach (MethodBase allPatchedMethod in Harmony.GetAllPatchedMethods()) { if (!allPatchedMethod.Name.Contains("CustomUpdate")) { continue; } Patches patchInfo = Harmony.GetPatchInfo(allPatchedMethod); if (patchInfo == null) { continue; } AzuDevModPlugin.AzuDevModLogger.LogWarning((object)("Patched Method: " + GeneralExtensions.FullDescription(allPatchedMethod))); foreach (string owner in patchInfo.Owners) { AzuDevModPlugin.AzuDevModLogger.LogWarning((object)("Patch Owner (Harmony ID): " + owner)); PluginInfo val = ((IEnumerable<PluginInfo>)Chainloader.PluginInfos.Values).FirstOrDefault((Func<PluginInfo, bool>)((PluginInfo plugin) => plugin.Metadata.GUID == owner || plugin.Metadata.Name.Contains(owner))); if (val != null) { AzuDevModPlugin.AzuDevModLogger.LogWarning((object)$"Mapped Harmony ID '{owner}' to Plugin: {val.Metadata.Name} by {val.Metadata.GUID}, Version: {val.Metadata.Version}"); AzuDevModPlugin.AzuDevModLogger.LogWarning((object)("Plugin Path: " + val.Location)); } else { AzuDevModPlugin.AzuDevModLogger.LogWarning((object)("Couldn't match Harmony ID '" + owner + "' to any loaded plugin.")); } } } } catch (Exception ex2) { AzuDevModPlugin.AzuDevModLogger.LogError((object)("Failed to process exception details: " + ex2.Message)); } } else { AzuDevModPlugin.AzuDevModLogger.LogError((object)("Unhandled Exception: " + __instance.GetType().Name + " - " + __instance.Message)); } try { AzuDevModExceptionHandler.Handle(__instance); } catch (Exception ex3) { AzuDevModPlugin.AzuDevModLogger.LogError((object)("Exception handler failed: " + ex3.Message)); } } finally { OperationUnderway = false; } } } } namespace AzuDevMod.Patches { [HarmonyPatch(typeof(AssetBundle), "LoadAsset", new Type[] { typeof(string), typeof(Type) })] internal static class AssetBundleLoadAssetPatch { private static void Postfix(AssetBundle __instance, string name, Type type, ref Object __result) { if (AzuDevModPlugin.LogAssetBundleIssues.Value != AzuDevModPlugin.Toggle.Off) { if ((Object)(object)__instance == (Object)null) { AzuDevModPlugin.AzuDevModLogger.LogError((object)$"AssetBundle is null when loading asset '{name}' of type '{type}'."); } else if (__result == (Object)null) { AzuDevModPlugin.AzuDevModLogger.LogError((object)$"Failed to load asset '{name}' of type '{type}'."); } } } } [HarmonyPatch(typeof(Assembly), "GetManifestResourceStream", new Type[] { typeof(string) })] public class GetManifestResourceStreamPatch { public static void Postfix(Assembly __instance, string name, ref Stream __result) { if (AzuDevModPlugin.LogAssetBundleIssues.Value != AzuDevModPlugin.Toggle.Off && __result == null && name.Substring(name.LastIndexOf('.') + 1) != "png") { AzuDevModPlugin.AzuDevModLogger.LogError((object)("Assembly '" + __instance.GetName().Name + "' failed to load resource/assetbundle '" + name.Substring(name.LastIndexOf('.') + 1) + "'.")); } } } public static class ExceptionStackGetterPatch { private static bool LockRoot; [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] internal static void ExceptionStackGot(Exception __instance) { if (!LockRoot) { LockRoot = true; AzuDevModExceptionProcessor.Run(__instance); LockRoot = false; } } } [HarmonyPatch] public static class ExceptionConstructorPatch { private static IEnumerable<MethodBase> TargetMethods() { return typeof(Exception).GetConstructors(BindingFlags.Instance | BindingFlags.Public); } private static void Postfix(Exception __instance) { if (__instance is MissingFieldException) { AzuDevModPlugin.AzuDevModLogger.LogError((object)"Intercepted MissingFieldException during construction:"); } AzuDevModExceptionProcessor.PushException(__instance); } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class MissingFieldExceptionMessagePatch { private static void Postfix(MissingMemberException __instance) { Extensions.LogDetailed((Exception)__instance, (string)null); } } [HarmonyPatch(typeof(Harmony), "UnpatchAll", new Type[] { })] public class DumpStacktrace { private static bool Prefix(Harmony __instance) { if (AzuDevModPlugin.LogUnpatchAll.Value == AzuDevModPlugin.Toggle.Off) { return true; } try { List<string> list = ExtractModNames(Environment.StackTrace); if (list.Count <= 0) { return true; } foreach (string item in list) { if (!string.IsNullOrEmpty(item)) { Chainloader.PluginInfos.TryGetValue(__instance.Id, out var value); string text = string.Empty; if (value != null) { text = " Mod: [" + value.Metadata.Name + "] GUID: [" + value.Metadata.GUID + "]"; } AzuDevModPlugin.AzuDevModLogger.LogError((object)("Mod/Class causing the UnpatchAll, the UnpatchAll was prevented: " + item + text)); return false; } AzuDevModPlugin.AzuDevModLogger.LogWarning((object)("Unable to determine the mod causing the UnpatchAll from the stack trace being parsed. Printing everything " + Environment.NewLine + Environment.StackTrace)); } } catch (Exception arg) { AzuDevModPlugin.AzuDevModLogger.LogError((object)$"Error while processing the UnpatchAll Prefix: {arg}"); } return true; } private static List<string> ExtractModNames(string stackTrace) { return (from Match match in Regex.Matches(stackTrace, "at (?<modName>[\\w\\.]+)\\.OnDestroy") where match.Success select match.Groups["modName"].Value).ToList(); } } [HarmonyPatch(typeof(Harmony), "UnpatchSelf")] public class DumpStacktrace2 { private static bool Prefix(Harmony __instance) { if (AzuDevModPlugin.LogUnpatchAll.Value == AzuDevModPlugin.Toggle.Off) { return true; } try { List<string> list = ExtractModNames(Environment.StackTrace); if (list.Count <= 0) { return true; } foreach (string item in list) { if (!string.IsNullOrEmpty(item)) { Chainloader.PluginInfos.TryGetValue(__instance.Id, out var value); string text = string.Empty; if (value != null) { text = " Mod: [" + value.Metadata.Name + "] GUID: [" + value.Metadata.GUID + "]"; } AzuDevModPlugin.AzuDevModLogger.LogError((object)("UnpatchSelf prevented: " + item + text)); return false; } AzuDevModPlugin.AzuDevModLogger.LogWarning((object)("Unable to determine the mod causing the UnpatchSelf from the stack trace being parsed. Printing everything " + Environment.NewLine + Environment.StackTrace)); } } catch (Exception arg) { AzuDevModPlugin.AzuDevModLogger.LogError((object)$"Error while processing the UnpatchSelf Prefix: {arg}"); } return true; } private static List<string> ExtractModNames(string stackTrace) { return (from Match match in Regex.Matches(stackTrace, "at (?<modName>[\\w\\.]+)\\.OnDestroy") where match.Success select match.Groups["modName"].Value).ToList(); } } [HarmonyPatch(typeof(List<GameObject>), "Add")] public class CheckDuplicatePatch { [HarmonyPriority(0)] private static void Prefix(List<GameObject> __instance, GameObject item) { if (AzuDevModPlugin.LogDuplicateGameObjectAdditions.Value != AzuDevModPlugin.Toggle.Off && !((Object)(object)DungeonDB.instance == (Object)null) && !ZoneSystemCheck.HasInit && !((Object)(object)item == (Object)null) && __instance.Contains(item)) { _ = ((Object)item).name; string text = ((Object)item).name.ToLower(); AssetLoadTracker.GetAssemblyForPrefab(text); AssetLoadTracker.GetBundleForPrefab(text); LoggingMethods.LogWithPrefabInfo("Attempting to add duplicate GameObject to a list of GameObjects", text); } } } [HarmonyPatch(typeof(ZoneSystem), "Start")] internal static class ZoneSystemCheck { internal static bool HasInit; private static void Postfix(ZoneSystem __instance) { HasInit = true; } } [HarmonyPatch(typeof(ZNetScene), "Update")] public class WatchForDestroyedZNetViewsInScene { internal static readonly ConditionalWeakTable<ZNetView, string> DestroyedZNetViews = new ConditionalWeakTable<ZNetView, string>(); private static void Postfix(ZNetScene __instance) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) if (AzuDevModPlugin.LogDestroyedZNetViews.Value == AzuDevModPlugin.Toggle.Off) { return; } List<ZDO> list = new List<ZDO>(); foreach (KeyValuePair<ZDO, ZNetView> instance in __instance.m_instances) { ZNetView value = instance.Value; if (Object.op_Implicit((Object)(object)value)) { continue; } list.Add(instance.Key); if (DestroyedZNetViews.TryGetValue(value, out string _)) { string identifier; try { GameObject prefab = ZNetScene.instance.GetPrefab(instance.Key.GetPrefab()); identifier = ((Object.op_Implicit((Object)(object)prefab) && prefab != null) ? ((Object)prefab).name : instance.Key.GetPrefab().ToString()); } catch (Exception) { identifier = "Couldn't get prefab name"; } string text = string.Empty; if (instance.Key != null) { text = $"ZDO: {instance.Key.m_uid} | Owner: {instance.Key.GetOwner()} | Sector: {instance.Key.GetSector()} | Position: {instance.Key.GetPosition()}{Environment.NewLine}"; } LoggingMethods.LogWithPrefabInfo("Potential for ZNetScene.RemoveObjects error spam. ZNetView destroyed without being destroyed through the ZNetScene", identifier, Environment.NewLine + text); } } foreach (ZDO item in list) { __instance.m_instances.Remove(item); } } } [HarmonyPatch(typeof(ZNetView), "Awake")] public class TrackUnregisteredZNetViews { private static void Postfix(ZNetView __instance) { if (AzuDevModPlugin.LogUnregisteredZNetViews.Value == AzuDevModPlugin.Toggle.Off) { return; } ZDO zDO = __instance.GetZDO(); int? num = ((zDO != null) ? new int?(zDO.GetPrefab()) : ((int?)null)); int num2; if (num.HasValue) { int valueOrDefault = num.GetValueOrDefault(); if (valueOrDefault != 0) { num2 = (((Object)(object)ZNetScene.instance.GetPrefab(valueOrDefault) == (Object)null) ? 1 : 0); goto IL_005e; } } num2 = 0; goto IL_005e; IL_005e: if (num2 != 0) { string text; try { text = __instance.GetPrefabName(); } catch (Exception) { text = ((Object)((Component)__instance).gameObject).name; } string identifier = text.ToLower(); LoggingMethods.LogWithPrefabInfo("ZNetView for '" + text + "' has not been registered in ZNetScene. This can cause the ZNetScene.RemoveObjects error spam.", identifier); } } } [HarmonyPatch(typeof(ZNetView), "OnDestroy")] public class TrackZNetViewDestruction { private static void Postfix(ZNetView __instance) { WatchForDestroyedZNetViewsInScene.DestroyedZNetViews.Add(__instance, ((Object)__instance).name + "\n" + Environment.StackTrace + Environment.NewLine); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class CatchInvalidItemdrops { [HarmonyPriority(int.MinValue)] private static void Postfix(ObjectDB __instance) { foreach (GameObject item in __instance.m_items) { if ((Object)(object)item.GetComponent<ItemDrop>() == (Object)null) { AzuDevModPlugin.AzuDevModLogger.LogError((object)("Found null item drop component on " + ((Object)item).name + " when it shouldn't be.")); } } } } } namespace AzuDevMod.Filtering { internal static class AzuDevModBlacklist { internal static string[] ExceptionBlackList = Array.Empty<string>(); internal static string[] FrameBlackList = Array.Empty<string>(); internal static bool IsOnExceptionBlacklist(Assembly assembly) { if (AzuDevModPlugin.EnableBlacklistPerException.Value.IsOff()) { return false; } return IsOnList_Internal(assembly.GetName().Name, in ExceptionBlackList); } internal static bool IsOnFrameBlacklist(Assembly assembly) { if (AzuDevModPlugin.EnableBlacklistPerException.Value.IsOff()) { return false; } return IsOnList_Internal(assembly.GetName().Name, in FrameBlackList); } internal static bool IsOnList_Internal(string name, in string[] blacklist) { string[] array = blacklist; foreach (string value in array) { if (name.StartsWith(value)) { return true; } } return false; } internal static void UpdateLists_Internal() { ExceptionBlackList = AzuDevModPlugin.ExceptionBlacklist.Value.Split(new char[1] { ',' }); FrameBlackList = AzuDevModPlugin.FrameBlacklist.Value.Split(new char[1] { ',' }); for (int i = 0; i < ExceptionBlackList.Length; i++) { ExceptionBlackList[i] = ExceptionBlackList[i].Trim(); } for (int j = 0; j < FrameBlackList.Length; j++) { FrameBlackList[j] = FrameBlackList[j].Trim(); } } } internal static class AzuDevModWhitelist { internal static readonly List<Assembly> AssemblyWhiteList = new List<Assembly>(2) { Helpers.GetAssemblyByName("Assembly-CSharp"), Helpers.GetAssemblyByName("MMHOOK_Assembly-CSharp") }; internal static bool IsOnExceptionWhitelist(Assembly assembly) { return Whitelist(AzuDevModPlugin.EnableWhitelistPerException.Value, assembly); } internal static bool IsOnFrameWhitelist(Assembly assembly) { return Whitelist(AzuDevModPlugin.EnableWhitelistPerFrame.Value, assembly); } private static bool Whitelist(AzuDevModPlugin.Toggle Setting, Assembly assembly) { if (AzuDevModPluginMarshal.InfoCache.ContainsKey(assembly)) { return true; } if (!Setting.IsOff()) { if (Setting.IsOn()) { return AssemblyWhiteList.Contains(assembly); } return false; } return true; } } } namespace AzuDevMod.Components { public class AzuDevModExceptionBroker : MonoBehaviour { private readonly Exception[] popped = new Exception[3]; private void Awake() { AzuDevModPlugin.AzuDevModLogger.LogInfo((object)"AzuDevModExceptionBroker active"); } private void Update() { int num = AzuDevModExceptionProcessor.Storage.TryPopRange(popped); if (num > 0) { for (int i = 0; i < num; i++) { AzuDevModExceptionProcessor.Run(popped[i]); } } } } public class AzuDevModPostChainloadRunner : MonoBehaviour { private void Start() { AzuDevModPlugin.AzuDevModLogger.LogInfo((object)"AzuDevModPostChainloadRunner Starting"); AzuDevModPluginMarshal.Run(); } } }