Decompiled source of AzuDevMod v1.0.6

AzuDevMod.dll

Decompiled 3 months ago
using System;
using System.Collections.Generic;
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.Util;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
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.6")]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.6.0")]
[module: UnverifiableCode]
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;
		}
	}
}
namespace AzuDevMod
{
	[BepInPlugin("Azumatt.AzuDevMod", "AzuDevMod", "1.0.6")]
	public class AzuDevModPlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			Off,
			On
		}

		internal const string ModName = "AzuDevMod";

		internal const string ModVersion = "1.0.6";

		internal const string Author = "Azumatt";

		private const string ModGUID = "Azumatt.AzuDevMod";

		private static string ConfigFileName = "Azumatt.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;

		private void Awake()
		{
			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.");
			_harmony.PatchAll();
			SetupWatcher();
		}

		private void Start()
		{
			AssetLoadTracker.MapPrefabsToBundles();
			AssetLoadTracker.MapBundlesToAssemblies();
		}

		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("Azumatt.AzuDevMod");
			AzuDevModLogger = Logger.CreateLogSource("AzuDevMod");
			LogDuplicateGameObjectAdditions = null;
			LogDestroyedZNetViews = null;
			LogUnregisteredZNetViews = null;
			LogUnpatchAll = null;
			LogAssetBundleIssues = null;
		}
	}
}
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;
		}
	}
	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());
		}
	}
}
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 != 0)
			{
				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 != 0 && __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) + "'."));
			}
		}
	}
	[HarmonyPatch(typeof(Harmony), "UnpatchAll", new Type[] { })]
	public class DumpStacktrace
	{
		private static bool Prefix()
		{
			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))
					{
						AzuDevModPlugin.AzuDevModLogger.LogError((object)("Mod/Class causing the UnpatchAll, the UnpatchAll was prevented: " + item));
						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(List<GameObject>), "Add")]
	public class CheckDuplicatePatch
	{
		[HarmonyPriority(0)]
		private static void Prefix(List<GameObject> __instance, GameObject item)
		{
			if (AzuDevModPlugin.LogDuplicateGameObjectAdditions.Value != 0 && !((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()) : 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."));
				}
			}
		}
	}
}