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 NullReferenceFix v1.0.44
NullReferenceFix.dll
Decompiled a day agousing 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); } } }