Decompiled source of NullReferenceFix v1.0.44

NullReferenceFix.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using NullReferenceFix.Patches;
using TMPro;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("NullReferenceFix")]
[assembly: AssemblyDescription("Fixes a vanilla Valheim NullReferenceException loop in ZNetScene.RemoveObjects.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("KorCaptain")]
[assembly: AssemblyProduct("NullReferenceFix")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("B3C1F0F1-6E8C-4B7A-9B2E-1E7F5C9A2D41")]
[assembly: AssemblyFileVersion("1.0.44")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.44.0")]
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;
		}
	}
}
namespace NullReferenceFix
{
	[BepInPlugin("KorCaptain.NullReferenceFix", "NullReferenceFix", "1.0.44")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class NullReferenceFixPlugin : BaseUnityPlugin
	{
		internal const string ModVersion = "1.0.44";

		internal const string ModName = "NullReferenceFix";

		internal const string Author = "KorCaptain";

		private const string ModGUID = "KorCaptain.NullReferenceFix";

		private readonly Harmony _harmony = new Harmony("KorCaptain.NullReferenceFix");

		public static ManualLogSource NullReferenceFixLogger;

		public void Awake()
		{
			NullReferenceFixLogger = ((BaseUnityPlugin)this).Logger;
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			TMPLocalizationFallbackFontRegistrar.Initialize();
			AzuCraftyBoxesStaleContainerPruner.Initialize((MonoBehaviour)(object)this);
			if (((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Enabled", false, "Every 60s log frame time, hitches, memory, ping and object counts, and warn about any metric that keeps rising, to find slow-lag leaks.").Value)
			{
				ObjectCountDiagnostics.Initialize((MonoBehaviour)(object)this);
			}
			NullReferenceFixLogger.LogInfo((object)"NullReferenceFix v1.0.44 loaded.");
		}
	}
}
namespace NullReferenceFix.Patches
{
	[HarmonyPatch(typeof(ZNetScene), "RemoveObjects")]
	public static class ZNetScene_RemoveObjects_NullGuard_Patch
	{
		private static readonly FieldInfo? InstancesField = typeof(ZNetScene).GetField("m_instances", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static Exception? Finalizer(ZNetScene __instance, Exception __exception)
		{
			if (!(__exception is NullReferenceException))
			{
				return __exception;
			}
			try
			{
				if (!(InstancesField?.GetValue(__instance) is Dictionary<ZDO, ZNetView> dictionary))
				{
					return null;
				}
				List<ZDO> list = new List<ZDO>();
				foreach (KeyValuePair<ZDO, ZNetView> item in dictionary)
				{
					if ((Object)(object)item.Value == (Object)null)
					{
						list.Add(item.Key);
					}
				}
				foreach (ZDO item2 in list)
				{
					dictionary.Remove(item2);
				}
				if (list.Count > 0)
				{
					NullReferenceFixPlugin.NullReferenceFixLogger.LogWarning((object)string.Format("[NullFix] Cleared {0} stale ZNetView entr{1} that caused a ZNetScene.RemoveObjects NullReferenceException.", list.Count, (list.Count == 1) ? "y" : "ies"));
				}
			}
			catch (Exception arg)
			{
				NullReferenceFixPlugin.NullReferenceFixLogger.LogError((object)$"Failed to clean up stale ZNetView entries: {arg}");
			}
			return null;
		}
	}
	[HarmonyPatch]
	public static class AzuCraftyBoxes_HaveRequirements_NullGuard_Patch
	{
		private const string AzuCraftyBoxesPluginGuid = "Azumatt.AzuCraftyBoxes";

		private static readonly FieldInfo? ContainersField = ResolveContainersField();

		private static bool Prepare()
		{
			return TargetMethod() != null;
		}

		private static MethodBase? TargetMethod()
		{
			Type type = ResolveAzuCraftyBoxesType("AzuCraftyBoxes.Patches.PlayerHaveRequirementsPatchRBoolInt");
			return (type == null) ? null : AccessTools.DeclaredMethod(type, "Postfix", (Type[])null, (Type[])null);
		}

		private static FieldInfo? ResolveContainersField()
		{
			Type type = ResolveAzuCraftyBoxesType("AzuCraftyBoxes.Util.Functions.Boxes");
			return (type == null) ? null : AccessTools.Field(type, "Containers");
		}

		private static Type? ResolveAzuCraftyBoxesType(string typeName)
		{
			if (!Chainloader.PluginInfos.TryGetValue("Azumatt.AzuCraftyBoxes", out var value))
			{
				return null;
			}
			return ((object)value.Instance)?.GetType().Assembly.GetType(typeName);
		}

		private static Exception? Finalizer(Exception __exception)
		{
			if (!(__exception is NullReferenceException))
			{
				return __exception;
			}
			int num = RemoveDestroyedContainers();
			NullReferenceFixPlugin.NullReferenceFixLogger.LogWarning((object)((num > 0) ? string.Format("[NullFix] Cleared {0} stale (destroyed) container entr{1} that caused an AzuCraftyBoxes NullReferenceException during a recipe check.", num, (num == 1) ? "y" : "ies") : "[NullFix] Swallowed a NullReferenceException from AzuCraftyBoxes' recipe container check."));
			return null;
		}

		private static int RemoveDestroyedContainers()
		{
			try
			{
				if (!(ContainersField?.GetValue(null) is HashSet<Container> hashSet))
				{
					return 0;
				}
				List<Container> list = new List<Container>();
				foreach (Container item in hashSet)
				{
					if ((Object)(object)item == (Object)null)
					{
						list.Add(item);
					}
				}
				foreach (Container item2 in list)
				{
					hashSet.Remove(item2);
				}
				return list.Count;
			}
			catch (Exception arg)
			{
				NullReferenceFixPlugin.NullReferenceFixLogger.LogError((object)$"Failed to clean up stale AzuCraftyBoxes container entries: {arg}");
				return 0;
			}
		}
	}
	[HarmonyPatch(typeof(EnemyHud), "ShowHud")]
	public static class EnemyHud_ShowHud_NullGuard_Patch
	{
		private static readonly FieldInfo? HudsField = typeof(EnemyHud).GetField("m_huds", BindingFlags.Instance | BindingFlags.NonPublic);

		private static FieldInfo? _guiField;

		private static Exception? Finalizer(EnemyHud __instance, Character c, Exception __exception)
		{
			if (!(__exception is NullReferenceException))
			{
				return __exception;
			}
			try
			{
				if (RemoveStaleHud(__instance, c))
				{
					NullReferenceFixPlugin.NullReferenceFixLogger.LogWarning((object)"[NullFix] Cleared a stale EnemyHud entry that caused a NullReferenceException in a mod's EnemyHud.ShowHud patch (seen with EpicMMOSystem's PvP name-color patch when another mod force-refreshes EnemyHud.ShowHud).");
				}
				else
				{
					NullReferenceFixPlugin.NullReferenceFixLogger.LogWarning((object)"[NullFix] Swallowed a NullReferenceException thrown from a mod's EnemyHud.ShowHud patch.");
				}
			}
			catch (Exception arg)
			{
				NullReferenceFixPlugin.NullReferenceFixLogger.LogError((object)$"Failed to clean up stale EnemyHud entry: {arg}");
			}
			return null;
		}

		private static bool RemoveStaleHud(EnemyHud instance, Character c)
		{
			if (!(HudsField?.GetValue(instance) is IDictionary dictionary) || !dictionary.Contains(c))
			{
				return false;
			}
			object obj = dictionary[c];
			if (obj != null)
			{
				if ((object)_guiField == null)
				{
					_guiField = obj.GetType().GetField("m_gui", BindingFlags.Instance | BindingFlags.Public);
				}
				object? obj2 = _guiField?.GetValue(obj);
				GameObject val = (GameObject)((obj2 is GameObject) ? obj2 : null);
				if (val != null && (Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
			}
			dictionary.Remove(c);
			return true;
		}
	}
	[HarmonyPatch]
	public static class TMP_Text_LoadFontAsset_MissingFont_Patch
	{
		private static TMP_FontAsset? _fallbackFont;

		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(TextMeshProUGUI), "LoadFontAsset", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(TextMeshPro), "LoadFontAsset", (Type[])null, (Type[])null);
		}

		private static void Prefix(TMP_Text __instance)
		{
			if (!((Object)(object)__instance.font != (Object)null))
			{
				TMP_FontAsset val = _fallbackFont ?? (_fallbackFont = ResolveFallbackFont());
				if (!((Object)(object)val == (Object)null))
				{
					__instance.font = val;
				}
			}
		}

		private static TMP_FontAsset? ResolveFallbackFont()
		{
			if ((Object)(object)TMP_Settings.defaultFontAsset != (Object)null)
			{
				return TMP_Settings.defaultFontAsset;
			}
			TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			return (array.Length != 0) ? array[0] : null;
		}
	}
	public static class TMPLocalizationFallbackFontRegistrar
	{
		private static readonly string[] TargetFontNames = new string[12]
		{
			"NotoSansArabic-Regular SDF", "NotoSansArmenian-Regular SDF", "NotoSansBengali-Regular SDF", "NotoSansDevanagari-Regular SDF", "NotoSansGeorgian-Regular SDF", "NotoSansHebrew-Regular SDF", "NotoSansJP-Regular SDF", "NotoSansKR-Regular SDF", "NotoSansMalayalam-Regular SDF", "NotoSansSC-Regular SDF",
			"NotoSansThai-Regular SDF", "NotoEmoji-Regular SDF"
		};

		private const string WarningSubstring = "font asset or any potential fallbacks";

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

		private static bool _subscribed;

		public static void Initialize()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			if (!_subscribed)
			{
				RegisterAndReport(FindLocalizationFontAssets());
				if (_pending.Count != 0)
				{
					_subscribed = true;
					Application.logMessageReceived += new LogCallback(OnLogMessageReceived);
				}
			}
		}

		private static void OnLogMessageReceived(string message, string stackTrace, LogType type)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			if (_pending.Count == 0)
			{
				Unsubscribe();
			}
			else if ((int)type == 2 && message.Contains("font asset or any potential fallbacks"))
			{
				RegisterAndReport(FindLocalizationFontAssets());
				if (_pending.Count == 0)
				{
					Unsubscribe();
				}
			}
		}

		private static void Unsubscribe()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			Application.logMessageReceived -= new LogCallback(OnLogMessageReceived);
			_subscribed = false;
		}

		private static void RegisterAndReport(List<TMP_FontAsset> found)
		{
			if (found.Count == 0)
			{
				return;
			}
			List<string> list = new List<string>();
			foreach (TMP_FontAsset item in found)
			{
				_pending.Remove(((Object)item).name);
				if (!TMP_Settings.fallbackFontAssets.Contains(item))
				{
					TMP_Settings.fallbackFontAssets.Add(item);
					list.Add(((Object)item).name);
				}
			}
			if (list.Count > 0)
			{
				StringBuilder stringBuilder = new StringBuilder("[NullFix] Added ");
				stringBuilder.Append(list.Count);
				stringBuilder.Append(" localization fallback font(s) to TMP's global fallback list: ");
				stringBuilder.Append(string.Join(", ", list));
				stringBuilder.Append(" - so text in those scripts (e.g. player nicknames, guild names, info panels) renders instead of showing blank spaces or missing-glyph boxes.");
				NullReferenceFixPlugin.NullReferenceFixLogger.LogWarning((object)stringBuilder.ToString());
			}
		}

		private static List<TMP_FontAsset> FindLocalizationFontAssets()
		{
			List<TMP_FontAsset> list = new List<TMP_FontAsset>();
			TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			TMP_FontAsset[] array2 = array;
			foreach (TMP_FontAsset val in array2)
			{
				if (_pending.Contains(((Object)val).name))
				{
					list.Add(val);
				}
			}
			foreach (string name in _pending)
			{
				if (!list.Exists((TMP_FontAsset f) => ((Object)f).name == name))
				{
					TMP_FontAsset val2 = Resources.Load<TMP_FontAsset>("Fonts & Materials/" + name);
					if ((Object)(object)val2 != (Object)null)
					{
						list.Add(val2);
					}
				}
			}
			return list;
		}
	}
	[HarmonyPatch]
	public static class KeenCombat_SkillTooltip_NonWeaponStrip_Patch
	{
		private const string KeenCombatPluginGuid = "Bhimas.KeenCombat";

		private const string SkillBlockMarker = "<link=\"kc_skill\"></link>";

		private static bool Prepare()
		{
			return Chainloader.PluginInfos.ContainsKey("Bhimas.KeenCombat");
		}

		private static IEnumerable<MethodBase> TargetMethods()
		{
			return (from m in typeof(ItemData).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
				where m.Name == "GetTooltip" && m.ReturnType == typeof(string) && m.GetParameters().Any((ParameterInfo p) => p.ParameterType == typeof(ItemData))
				select m).Cast<MethodBase>();
		}

		[HarmonyPriority(0)]
		private static void Postfix(ref string __result, object[] __args)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(__result))
			{
				return;
			}
			int num = __result.IndexOf("<link=\"kc_skill\"></link>", StringComparison.Ordinal);
			if (num < 0)
			{
				return;
			}
			ItemData val = __args.OfType<ItemData>().FirstOrDefault();
			if (val?.m_shared != null && !IsWeapon(val.m_shared.m_itemType))
			{
				int num2 = num;
				while (num2 > 0 && __result[num2 - 1] == '\n')
				{
					num2--;
				}
				__result = __result.Substring(0, num2);
			}
		}

		private static bool IsWeapon(ItemType type)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			return (int)type == 3 || (int)type == 14 || (int)type == 22 || (int)type == 4;
		}
	}
	[HarmonyPatch(typeof(VisEquipment), "UpdateLodgroup")]
	public static class VisEquipment_UpdateLodgroup_DuplicateRenderer_Patch
	{
		private static bool _logged;

		private static bool Prefix(LODGroup ___m_lodGroup, GameObject ___m_visual)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)___m_lodGroup == (Object)null || (Object)(object)___m_visual == (Object)null)
			{
				return true;
			}
			HashSet<Renderer> hashSet = new HashSet<Renderer>();
			LODGroup[] componentsInChildren = ___m_visual.GetComponentsInChildren<LODGroup>(true);
			foreach (LODGroup val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)(object)___m_lodGroup)
				{
					continue;
				}
				LOD[] lODs = val.GetLODs();
				foreach (LOD val2 in lODs)
				{
					Renderer[] renderers = val2.renderers;
					foreach (Renderer val3 in renderers)
					{
						if ((Object)(object)val3 != (Object)null)
						{
							hashSet.Add(val3);
						}
					}
				}
			}
			if (hashSet.Count == 0)
			{
				return true;
			}
			List<Renderer> list = new List<Renderer>(___m_visual.GetComponentsInChildren<Renderer>());
			bool flag = false;
			foreach (Renderer item in list)
			{
				if (hashSet.Contains(item) && !VanillaSkips(item, ___m_lodGroup))
				{
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				return true;
			}
			list.RemoveAll(hashSet.Contains);
			LOD[] lODs2 = ___m_lodGroup.GetLODs();
			lODs2[0].renderers = list.ToArray();
			___m_lodGroup.SetLODs(lODs2);
			if (!_logged)
			{
				_logged = true;
				NullReferenceFixPlugin.NullReferenceFixLogger.LogInfo((object)"[NullFix] Excluded renderers already owned by another LODGroup from the player LODGroup (VisEquipment.UpdateLodgroup).");
			}
			return false;
		}

		private static bool VanillaSkips(Renderer renderer, LODGroup playerGroup)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			LODGroup componentInParent = ((Component)renderer).GetComponentInParent<LODGroup>();
			if ((Object)(object)componentInParent == (Object)null || (Object)(object)componentInParent == (Object)(object)playerGroup)
			{
				return false;
			}
			LOD[] lODs = componentInParent.GetLODs();
			foreach (LOD val in lODs)
			{
				if (Array.IndexOf(val.renderers, renderer) >= 0)
				{
					return true;
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(UITooltip), "LateUpdate")]
	public static class UITooltip_LateUpdate_TallTooltip_Patch
	{
		private static readonly FieldInfo? CurrentField = AccessTools.Field(typeof(UITooltip), "m_current");

		private static readonly FieldInfo? TooltipField = AccessTools.Field(typeof(UITooltip), "m_tooltip");

		private static int _cachedFrame = -1;

		private static UITooltip? _current;

		private static GameObject? _tooltip;

		private const float ColumnGap = 6f;

		private const string Column2Name = "NullFix_TallTooltipColumn2";

		private static readonly Regex TagRegex = new Regex("<(/?)(color|b|i|u|s|size|mark|sup|sub|alpha)(=[^>]*)?>", RegexOptions.IgnoreCase | RegexOptions.Compiled);

		private static bool _errorLogged;

		private static readonly Vector3[] Corners = (Vector3[])(object)new Vector3[4];

		private static readonly Vector3[] Corners2 = (Vector3[])(object)new Vector3[4];

		private static void Postfix(UITooltip __instance)
		{
			try
			{
				if (_cachedFrame != Time.frameCount)
				{
					_cachedFrame = Time.frameCount;
					object? obj = CurrentField?.GetValue(null);
					_current = (UITooltip?)((obj is UITooltip) ? obj : null);
					object? obj2 = TooltipField?.GetValue(null);
					_tooltip = (GameObject?)((obj2 is GameObject) ? obj2 : null);
				}
				if ((Object)(object)_current != (Object)(object)__instance)
				{
					return;
				}
				GameObject tooltip = _tooltip;
				if ((Object)(object)tooltip == (Object)null || !tooltip.activeSelf || tooltip.transform.childCount == 0)
				{
					return;
				}
				Transform child = tooltip.transform.GetChild(0);
				RectTransform val = (RectTransform)(object)((child is RectTransform) ? child : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				TallTooltipState tallTooltipState = tooltip.GetComponent<TallTooltipState>();
				if ((Object)(object)tallTooltipState == (Object)null)
				{
					tallTooltipState = tooltip.AddComponent<TallTooltipState>();
				}
				if ((Object)(object)tallTooltipState.Text == (Object)null)
				{
					Transform val2 = FindChildByName((Transform)(object)val, "Text");
					tallTooltipState.Text = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<TMP_Text>() : null);
				}
				TMP_Text text = tallTooltipState.Text;
				if ((Object)(object)text == (Object)null)
				{
					return;
				}
				string text2 = text.text;
				if (text2 == tallTooltipState.Seen && tallTooltipState.ScreenHeight == Screen.height)
				{
					if (tallTooltipState.Split)
					{
						Place(tooltip.transform, val, tallTooltipState);
					}
					return;
				}
				tallTooltipState.Seen = text2;
				tallTooltipState.ScreenHeight = Screen.height;
				if (!TrySplit(tooltip.transform, val, text, text2, tallTooltipState))
				{
					tallTooltipState.Split = false;
					if ((Object)(object)tallTooltipState.Column2 != (Object)null)
					{
						((Component)tallTooltipState.Column2).gameObject.SetActive(false);
					}
				}
				else
				{
					tallTooltipState.Split = true;
					Place(tooltip.transform, val, tallTooltipState);
				}
			}
			catch (Exception ex)
			{
				if (!_errorLogged)
				{
					_errorLogged = true;
					NullReferenceFixPlugin.NullReferenceFixLogger.LogWarning((object)("[NullFix] Tall tooltip split skipped: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		private static bool TrySplit(Transform root, RectTransform panel, TMP_Text text, string full, TallTooltipState state)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			float y = ((Transform)panel).lossyScale.y;
			if (y <= 0f || string.IsNullOrEmpty(full))
			{
				return false;
			}
			LayoutRebuilder.ForceRebuildLayoutImmediate(panel);
			RectTransform textBlock = GetTextBlock(text);
			Rect rect = panel.rect;
			float height = ((Rect)(ref rect)).height;
			rect = textBlock.rect;
			float num = Mathf.Max(0f, height - ((Rect)(ref rect)).height);
			rect = text.rectTransform.rect;
			float width = ((Rect)(ref rect)).width;
			float y2 = text.GetPreferredValues(full, width, 0f).y;
			float num2 = (float)Screen.height / y - num;
			if (num + y2 <= (float)Screen.height / y + 0.5f || num2 <= 0f)
			{
				return false;
			}
			TMP_TextInfo textInfo = text.GetTextInfo(full);
			if (textInfo == null || textInfo.lineCount < 2)
			{
				return false;
			}
			float num3 = 0f;
			int num4 = textInfo.lineCount;
			for (int i = 0; i < textInfo.lineCount; i++)
			{
				num3 += textInfo.lineInfo[i].lineHeight;
				if (num3 > num2)
				{
					num4 = Mathf.Max(i, 1);
					break;
				}
			}
			if (num4 >= textInfo.lineCount)
			{
				return false;
			}
			int index = textInfo.characterInfo[textInfo.lineInfo[num4].firstCharacterIndex].index;
			if (index <= 0 || index >= full.Length)
			{
				return false;
			}
			string text2 = full.Substring(0, index);
			string text3 = OpenTags(text2) + full.Substring(index);
			RectTransform val = EnsureColumn2(root, panel, state);
			TMP_Text text4 = state.Text2;
			text4.text = text3;
			text.text = text2;
			state.Seen = text2;
			SetBlockHeight(text, Mathf.Min(text.GetPreferredValues(text2, width, 0f).y, num2));
			rect = text4.rectTransform.rect;
			SetBlockHeight(text4, text4.GetPreferredValues(text3, ((Rect)(ref rect)).width, 0f).y);
			LayoutRebuilder.ForceRebuildLayoutImmediate(panel);
			LayoutRebuilder.ForceRebuildLayoutImmediate(val);
			((Component)val).gameObject.SetActive(true);
			return true;
		}

		private static RectTransform EnsureColumn2(Transform root, RectTransform panel, TallTooltipState state)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			if ((Object)(object)state.Column2 != (Object)null && (Object)(object)state.Text2 != (Object)null)
			{
				return state.Column2;
			}
			GameObject val = Object.Instantiate<GameObject>(((Component)panel).gameObject, root, false);
			((Object)val).name = "NullFix_TallTooltipColumn2";
			Transform val2 = FindChildByName(val.transform, "Topic");
			if ((Object)(object)val2 != (Object)null)
			{
				((Component)val2).gameObject.SetActive(false);
			}
			Transform val3 = FindChildByName(val.transform, "Text");
			state.Column2 = (RectTransform)val.transform;
			state.Text2 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).GetComponent<TMP_Text>() : null);
			if ((Object)(object)state.Text2 == (Object)null)
			{
				throw new InvalidOperationException("tooltip panel has no 'Text' TMP_Text to clone");
			}
			return state.Column2;
		}

		private static Transform? FindChildByName(Transform parent, string name)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			foreach (Transform item in parent)
			{
				Transform val = item;
				if (((Object)val).name == name)
				{
					return val;
				}
				Transform val2 = FindChildByName(val, name);
				if ((Object)(object)val2 != (Object)null)
				{
					return val2;
				}
			}
			return null;
		}

		private static RectTransform GetTextBlock(TMP_Text text)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			ScrollRect componentInParent = ((Component)text).GetComponentInParent<ScrollRect>();
			return (RectTransform)((!((Object)(object)componentInParent != (Object)null)) ? ((object)text.rectTransform) : ((object)(RectTransform)((Component)componentInParent).transform));
		}

		private static void SetBlockHeight(TMP_Text text, float height)
		{
			ScrollRect componentInParent = ((Component)text).GetComponentInParent<ScrollRect>();
			if (!((Object)(object)componentInParent == (Object)null))
			{
				LayoutElement component = ((Component)componentInParent).GetComponent<LayoutElement>();
				if (!((Object)(object)component == (Object)null))
				{
					component.minHeight = height;
					component.preferredHeight = height;
				}
			}
		}

		private static void Place(Transform root, RectTransform panel, TallTooltipState state)
		{
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			RectTransform column = state.Column2;
			if (!((Object)(object)column == (Object)null))
			{
				panel.GetWorldCorners(Corners);
				column.GetWorldCorners(Corners2);
				float num = Corners[2].x - Corners[0].x;
				float num2 = Corners2[2].x - Corners2[0].x;
				float num3 = num + 6f + num2;
				float num4 = 0f;
				if (Corners[0].x + num3 > (float)Screen.width)
				{
					num4 = (float)Screen.width - num3 - Corners[0].x;
				}
				if (Corners[0].x + num4 < 0f)
				{
					num4 = 0f - Corners[0].x;
				}
				float num5 = (float)Screen.height - Corners[1].y;
				root.position += new Vector3(num4, num5, 0f);
				float num6 = Corners[2].x + num4 + 6f;
				float num7 = Corners[0].y + num5;
				((Transform)column).position = ((Transform)column).position + new Vector3(num6 - Corners2[0].x, num7 - Corners2[0].y, 0f);
			}
		}

		private static string OpenTags(string text)
		{
			List<(string, string)> list = new List<(string, string)>();
			foreach (Match item in TagRegex.Matches(text))
			{
				string name = item.Groups[2].Value.ToLowerInvariant();
				if (item.Groups[1].Value.Length == 0)
				{
					list.Add((name, item.Value));
					continue;
				}
				int num = list.FindLastIndex(((string name, string tag) t) => t.name == name);
				if (num >= 0)
				{
					list.RemoveAt(num);
				}
			}
			return string.Concat(list.ConvertAll(((string name, string tag) t) => t.tag));
		}
	}
	public class TallTooltipState : MonoBehaviour
	{
		public TMP_Text? Text;

		public TMP_Text? Text2;

		public RectTransform? Column2;

		public string? Seen;

		public int ScreenHeight;

		public bool Split;
	}
	public static class AzuCraftyBoxesStaleContainerPruner
	{
		private const string AzuCraftyBoxesPluginGuid = "Azumatt.AzuCraftyBoxes";

		private const float IntervalSeconds = 30f;

		internal static int LastSetSize = -1;

		public static void Initialize(MonoBehaviour host)
		{
			host.StartCoroutine(Run());
		}

		private static IEnumerator Run()
		{
			WaitForSeconds wait = new WaitForSeconds(30f);
			yield return wait;
			FieldInfo containersField = ResolveContainersField();
			if (containersField == null)
			{
				yield break;
			}
			while (true)
			{
				try
				{
					Prune(containersField);
				}
				catch (Exception ex)
				{
					Exception exception = ex;
					NullReferenceFixPlugin.NullReferenceFixLogger.LogError((object)$"[NullFix] Failed to prune stale AzuCraftyBoxes container entries: {exception}");
				}
				yield return wait;
			}
		}

		private static FieldInfo? ResolveContainersField()
		{
			if (!Chainloader.PluginInfos.TryGetValue("Azumatt.AzuCraftyBoxes", out var value))
			{
				return null;
			}
			Type type = ((object)value.Instance)?.GetType().Assembly.GetType("AzuCraftyBoxes.Util.Functions.Boxes");
			return (type == null) ? null : AccessTools.Field(type, "Containers");
		}

		private static void Prune(FieldInfo containersField)
		{
			if (containersField.GetValue(null) is HashSet<Container> hashSet)
			{
				int num = hashSet.RemoveWhere((Container container) => (Object)(object)container == (Object)null);
				LastSetSize = hashSet.Count;
				if (num > 0)
				{
					NullReferenceFixPlugin.NullReferenceFixLogger.LogInfo((object)string.Format("[NullFix] Pruned {0} destroyed AzuCraftyBoxes container entr{1} ({2} live remain).", num, (num == 1) ? "y" : "ies", hashSet.Count));
				}
			}
		}
	}
	public static class ObjectCountDiagnostics
	{
		private const float IntervalSeconds = 60f;

		private const float HitchSeconds = 0.1f;

		private const int TrendSamples = 4;

		private const int TopCount = 5;

		private static readonly Dictionary<string, List<long>> History = new Dictionary<string, List<long>>();

		private static int _frames;

		private static float _frameTimeSum;

		private static float _worstFrame;

		private static int _hitches;

		public static void Initialize(MonoBehaviour host)
		{
			host.StartCoroutine(TrackFrames());
			host.StartCoroutine(Run());
		}

		private static IEnumerator TrackFrames()
		{
			while (true)
			{
				yield return null;
				float dt = Time.unscaledDeltaTime;
				_frames++;
				_frameTimeSum += dt;
				if (dt > _worstFrame)
				{
					_worstFrame = dt;
				}
				if (dt > 0.1f)
				{
					_hitches++;
				}
			}
		}

		private static IEnumerator Run()
		{
			WaitForSeconds wait = new WaitForSeconds(60f);
			while (true)
			{
				yield return wait;
				if ((Object)(object)Player.m_localPlayer == (Object)null)
				{
					ResetFrameStats();
					continue;
				}
				try
				{
					Sample();
				}
				catch (Exception ex)
				{
					Exception exception = ex;
					NullReferenceFixPlugin.NullReferenceFixLogger.LogError((object)$"[NullFix][Diag] Sample failed: {exception}");
				}
			}
		}

		private static void ResetFrameStats()
		{
			_frames = 0;
			_frameTimeSum = 0f;
			_worstFrame = 0f;
			_hitches = 0;
		}

		private static void Sample()
		{
			float num = ((_frames > 0) ? (_frameTimeSum / (float)_frames * 1000f) : 0f);
			float num2 = _worstFrame * 1000f;
			int hitches = _hitches;
			ResetFrameStats();
			Material[] array = Resources.FindObjectsOfTypeAll<Material>();
			ParticleSystem[] array2 = Object.FindObjectsOfType<ParticleSystem>();
			Light[] array3 = Object.FindObjectsOfType<Light>();
			AudioSource[] array4 = Object.FindObjectsOfType<AudioSource>();
			Dictionary<string, long> dictionary = new Dictionary<string, long>
			{
				["materials"] = array.Length,
				["textures"] = Resources.FindObjectsOfTypeAll<Texture>().Length,
				["meshes"] = Resources.FindObjectsOfTypeAll<Mesh>().Length,
				["particleSystems"] = array2.Length,
				["lights"] = array3.Length,
				["audioSources"] = array4.Length,
				["rigidbodies"] = Object.FindObjectsOfType<Rigidbody>().Length,
				["colliders"] = Object.FindObjectsOfType<Collider>().Length,
				["containers"] = Object.FindObjectsOfType<Container>().Length,
				["characters"] = Character.GetAllCharacters().Count,
				["zdos"] = ((ZDOMan.instance != null) ? ZDOMan.instance.NrOfObjects() : (-1)),
				["sceneObjects"] = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.NrOfInstances() : (-1)),
				["azuContainerSet"] = AzuCraftyBoxesStaleContainerPruner.LastSetSize,
				["managedHeapMB"] = GC.GetTotalMemory(forceFullCollection: false) / 1048576,
				["unityMemoryMB"] = Profiler.GetTotalAllocatedMemoryLong() / 1048576
			};
			double num3 = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetServerPing() : (-1f));
			NullReferenceFixPlugin.NullReferenceFixLogger.LogInfo((object)($"[NullFix][Diag] frame avg={num:0.0}ms worst={num2:0}ms hitches(>{100f:0}ms)={hitches} ping={num3 * 1000.0:0}ms | " + string.Join(" ", dictionary.Select((KeyValuePair<string, long> pair) => $"{pair.Key}={pair.Value}").ToArray())));
			foreach (KeyValuePair<string, long> item in dictionary)
			{
				Record(item.Key, item.Value, array, array2, array3, array4);
			}
			Record("avgFrameMs", (long)num, array, array2, array3, array4);
		}

		private static void Record(string name, long value, Material[] materials, ParticleSystem[] particleSystems, Light[] lights, AudioSource[] audioSources)
		{
			if (value < 0)
			{
				return;
			}
			if (!History.TryGetValue(name, out List<long> value2))
			{
				value2 = (History[name] = new List<long>());
			}
			value2.Add(value);
			if (value2.Count > 5)
			{
				value2.RemoveAt(0);
			}
			if (value2.Count < 5)
			{
				return;
			}
			for (int i = 1; i < value2.Count; i++)
			{
				if (value2[i] <= value2[i - 1])
				{
					return;
				}
			}
			long num = value2[value2.Count - 1] - value2[0];
			long num2 = (name.EndsWith("MB", StringComparison.Ordinal) ? 50 : ((name == "avgFrameMs") ? 2 : 20));
			if (num >= num2)
			{
				string text = DescribeTop(name, materials, particleSystems, lights, audioSources);
				NullReferenceFixPlugin.NullReferenceFixLogger.LogWarning((object)$"[NullFix][Diag] SUSPECT {name} rose every sample: {value2[0]} -> {value2[value2.Count - 1]} (+{num} over {4} min).{text}");
			}
		}

		private static string DescribeTop(string name, Material[] materials, ParticleSystem[] particleSystems, Light[] lights, AudioSource[] audioSources)
		{
			return name switch
			{
				"materials" => Top(" Top instances: ", from material in materials
					where (Object)(object)material != (Object)null && ((Object)material).name.EndsWith("(Instance)", StringComparison.Ordinal)
					select ((Object)material).name), 
				"particleSystems" => Top(" Top roots: ", from item in particleSystems
					where (Object)(object)item != (Object)null
					select ((Object)((Component)item).transform.root).name), 
				"lights" => Top(" Top roots: ", from item in lights
					where (Object)(object)item != (Object)null
					select ((Object)((Component)item).transform.root).name), 
				"audioSources" => Top(" Top roots: ", from item in audioSources
					where (Object)(object)item != (Object)null
					select ((Object)((Component)item).transform.root).name), 
				_ => string.Empty, 
			};
		}

		private static string Top(string prefix, IEnumerable<string> names)
		{
			string text = string.Join(", ", (from @group in (from item in names
					group item by item into @group
					orderby @group.Count() descending
					select @group).Take(5)
				select $"{@group.Key} x{@group.Count()}").ToArray());
			return (text.Length == 0) ? string.Empty : (prefix + text);
		}
	}
}