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 Error Fix v0.0.5
V81ErrorFix.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Dissonance.Integrations.Unity_NFGO; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.AI.Navigation; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace V81ErrorFix { internal enum PatchEnableMode { Auto, Enabled, Disabled } internal static class ErrorFixConfig { internal static ConfigEntry<PatchEnableMode> RuntimePatchMode; internal static ConfigEntry<bool> EnableGlobalDestroyGuard; internal static ConfigEntry<PatchEnableMode> GlobalDestroyGuardMode; internal static ConfigEntry<bool> AllowDestroyDuringSceneUnload; internal static ConfigEntry<float> LifecycleDestroyWindowSeconds; internal static ConfigEntry<bool> LogBlockedDestroyStackTraceOnce; internal static ConfigEntry<PatchEnableMode> AudioSourcePlaybackGuardMode; internal static ConfigEntry<PatchEnableMode> KnownUnityWarningFilterMode; internal static ConfigEntry<PatchEnableMode> KnownBepInExLogNoiseFilterMode; internal static ConfigEntry<PatchEnableMode> PlayerRagdollGlobalTagGuardMode; internal static ConfigEntry<PatchEnableMode> ParticleMeshShapeGuardMode; internal static ConfigEntry<bool> ParticleMeshShapeGuardDryRun; internal static ConfigEntry<PatchEnableMode> EntranceTeleportUpdateGuardMode; internal static ConfigEntry<PatchEnableMode> FindMainEntrancePositionFallbackMode; internal static ConfigEntry<PatchEnableMode> PlayerNearOtherPlayersGuardMode; internal static ConfigEntry<PatchEnableMode> TerminalAccessibleObjectUpdateGuardMode; internal static ConfigEntry<PatchEnableMode> GameplayEnemyUpdateGuardMode; internal static ConfigEntry<PatchEnableMode> ThrowObjectClientRpcGuardMode; internal static ConfigEntry<PatchEnableMode> VoiceRefreshFallbackMode; internal static ConfigEntry<PatchEnableMode> UnlockableSuitGuardMode; internal static ConfigEntry<PatchEnableMode> UnlockableSuitUpdateGuardMode; internal static ConfigEntry<PatchEnableMode> NetworkObjectParentGuardMode; internal static ConfigEntry<PatchEnableMode> SteamValveDamageTriggerSpawnGuardMode; internal static ConfigEntry<PatchEnableMode> EnemyHealthBarsLateUpdateGuardMode; internal static ConfigEntry<PatchEnableMode> ShipLootPlusUiHelperGuardMode; internal static ConfigEntry<PatchEnableMode> NightVisionInsideLightingPostfixGuardMode; internal static ConfigEntry<PatchEnableMode> ChatCommandsStartHostPostfixGuardMode; internal static ConfigEntry<PatchEnableMode> EnemyAINavMeshGuardMode; internal static ConfigEntry<bool> EnableEnemyAINavMeshGuard; internal static ConfigEntry<bool> AllowEnemyAIWarp; internal static ConfigEntry<float> EnemyAINavMeshMaxWarpRadius; internal static ConfigEntry<bool> EnemyAINavMeshHostServerOnly; internal static ConfigEntry<PatchEnableMode> BindRuntimePatchMode(ConfigFile config) { RuntimePatchMode = config.Bind<PatchEnableMode>("Performance", "RuntimePatchMode", PatchEnableMode.Enabled, "Master switch for all Error Fix runtime Harmony patches and scene lifecycle hooks. Disabled leaves the DLL loaded but installs no runtime patches, binds no other ErrorFix settings, and skips Assembly-CSharp verification; use this for FPS baseline testing against an installed-but-passive ErrorFix. Requires restart."); return RuntimePatchMode; } internal static void Bind(ConfigFile config) { BindRuntimePatchMode(config); EnableGlobalDestroyGuard = config.Bind<bool>("NetworkObjectDestroy", "EnableGlobalDestroyGuard", true, "Legacy switch for the global Destroy guard. GlobalDestroyGuardMode can also disable this patch."); GlobalDestroyGuardMode = config.Bind<PatchEnableMode>("NetworkObjectDestroy", "GlobalDestroyGuardMode", PatchEnableMode.Disabled, "Only Enabled installs the global UnityEngine.Object.Destroy hook. Auto is treated as disabled for this global hook to avoid upgrade-time performance surprises. Requires restart."); AllowDestroyDuringSceneUnload = config.Bind<bool>("NetworkObjectDestroy", "AllowDestroyDuringSceneUnload", true, "Allows Destroy during network shutdown, ship scene unload, or lobby transitions."); LifecycleDestroyWindowSeconds = config.Bind<float>("NetworkObjectDestroy", "LifecycleDestroyWindowSeconds", 3f, "Seconds after scene load/unload/active-scene changes where lifecycle Destroy calls are allowed. Values are clamped from 0 to 15."); LogBlockedDestroyStackTraceOnce = config.Bind<bool>("NetworkObjectDestroy", "LogBlockedDestroyStackTraceOnce", true, "Logs one stack trace for the first blocked spawned ragdoll Destroy call."); AudioSourcePlaybackGuardMode = config.Bind<PatchEnableMode>("Performance", "AudioSourcePlaybackGuardMode", PatchEnableMode.Disabled, "Only Enabled installs global AudioSource.Play* hooks. Auto is treated as disabled for this global hook. Requires restart."); KnownUnityWarningFilterMode = config.Bind<PatchEnableMode>("Performance", "KnownUnityWarningFilterMode", PatchEnableMode.Enabled, "Enabled by default to suppress high-frequency Unity log spam for missing audio spatializer plugin setup, BoxCollider negative scale/size asset warnings, disabled AudioSource playback, the SteamValve(Clone) custom-filter AudioSource warning, and duplicate Static Lighting Sky baking warnings. This is log-only and does not repair the underlying audio plugin, collider geometry, AudioSource setup, or lighting setup. It filters only those exact warning prefixes, does not filter Netcode NetworkVariable lifecycle warnings, and requires restart."); KnownBepInExLogNoiseFilterMode = config.Bind<PatchEnableMode>("Performance", "KnownBepInExLogNoiseFilterMode", PatchEnableMode.Enabled, "Enabled by default to suppress exact known low-value BepInEx log noise from RuntimeIcons, PathfindingLib, and LethalPerformance. Error and exception logs are not filtered. Requires restart."); PlayerRagdollGlobalTagGuardMode = config.Bind<PatchEnableMode>("Performance", "PlayerRagdollGlobalTagGuardMode", PatchEnableMode.Disabled, "Only Enabled installs global GameObject/Component tag lookup, CompareTag, and tag setter guards. Auto is treated as disabled for these global hooks. The targeted DeadBodyInfo ragdoll guard remains active. Requires restart."); ParticleMeshShapeGuardMode = config.Bind<PatchEnableMode>("Performance", "ParticleMeshShapeGuardMode", PatchEnableMode.Disabled, "Only Enabled starts ParticleSystem mesh shape scans after scene load. Auto is treated as disabled for this scene scan. Requires restart."); ParticleMeshShapeGuardDryRun = config.Bind<bool>("Performance", "ParticleMeshShapeGuardDryRun", false, "When true, ParticleMeshShapeGuard logs invalid particle mesh shapes without disabling them. Requires restart."); EntranceTeleportUpdateGuardMode = config.Bind<PatchEnableMode>("EntranceTeleport", "EntranceTeleportUpdateGuardMode", PatchEnableMode.Disabled, "Only Enabled installs the EntranceTeleport.Update hot-path replacement. Auto is treated as disabled to avoid per-frame entrance overhead. Requires restart."); FindMainEntrancePositionFallbackMode = config.Bind<PatchEnableMode>("EntranceTeleport", "FindMainEntrancePositionFallbackMode", PatchEnableMode.Auto, "Auto enables the guarded RoundManager.FindMainEntrancePosition fallback only for the verified game assembly. Disabled preserves vanilla origin fallback."); PlayerNearOtherPlayersGuardMode = config.Bind<PatchEnableMode>("PlayerControllerB", "PlayerNearOtherPlayersGuardMode", PatchEnableMode.Disabled, "Only Enabled installs the PlayerControllerB.NearOtherPlayers hot-path guard. Auto is treated as disabled to avoid per-frame local-player proximity overhead. Requires restart."); TerminalAccessibleObjectUpdateGuardMode = config.Bind<PatchEnableMode>("TerminalAccessibleObject", "TerminalAccessibleObjectUpdateGuardMode", PatchEnableMode.Disabled, "Only Enabled installs the TerminalAccessibleObject.Update hot-path guard. Auto is treated as disabled to avoid per-frame terminal object initialization checks. Requires restart."); GameplayEnemyUpdateGuardMode = config.Bind<PatchEnableMode>("Performance", "GameplayEnemyUpdateGuardMode", PatchEnableMode.Disabled, "Only Enabled installs enemy Update/LateUpdate hot-path guards for BushWolf, Crawler, DocileLocustBees, and RedLocustBees. Auto is treated as disabled to avoid per-enemy frame overhead. Requires restart."); ThrowObjectClientRpcGuardMode = config.Bind<PatchEnableMode>("PlayerControllerB", "ThrowObjectClientRpcGuardMode", PatchEnableMode.Auto, "Auto enables the ThrowObjectClientRpc guard only for the verified game assembly. Enabled forces it on; Disabled turns it off."); VoiceRefreshFallbackMode = config.Bind<PatchEnableMode>("Voice", "VoiceRefreshFallbackMode", PatchEnableMode.Auto, "Auto enables the voice refresh fallback only for the verified game assembly. Enabled forces it on; Disabled turns it off."); UnlockableSuitGuardMode = config.Bind<PatchEnableMode>("UnlockableSuit", "UnlockableSuitGuardMode", PatchEnableMode.Auto, "Auto enables suit/unlockable sync guards only for the verified game assembly. Enabled forces them on; Disabled turns them off."); UnlockableSuitUpdateGuardMode = config.Bind<PatchEnableMode>("UnlockableSuit", "UnlockableSuitUpdateGuardMode", PatchEnableMode.Disabled, "Only Enabled installs the UnlockableSuit.Update hot-path guard. Auto is treated as disabled to avoid per-suit frame overhead. Requires restart."); NetworkObjectParentGuardMode = config.Bind<PatchEnableMode>("NetworkObjectParent", "NetworkObjectParentGuardMode", PatchEnableMode.Auto, "Auto suppresses only the known Netcode unspawned reparent SpawnStateException on the verified game assembly. Enabled forces it on; Disabled turns it off."); SteamValveDamageTriggerSpawnGuardMode = config.Bind<PatchEnableMode>("SteamValve", "SteamValveDamageTriggerSpawnGuardMode", PatchEnableMode.Disabled, "Disabled by default because the Netcode warning \"damageTrigger is disabled! Netcode for GameObjects does not support spawning disabled NetworkBehaviours\" is usually a one-time spawn lifecycle warning, not a performance issue. Only Enabled installs the experimental SteamValveHazard damageTrigger spawn guard, which temporarily activates the inactive damageTrigger InteractTrigger during Netcode spawn and then restores it inactive. Auto is treated as disabled for this guard to avoid changing object activation unless a SteamValve damageTrigger gameplay issue is confirmed. Requires restart."); EnemyHealthBarsLateUpdateGuardMode = config.Bind<PatchEnableMode>("OptionalCompatibility", "EnemyHealthBarsLateUpdateGuardMode", PatchEnableMode.Disabled, "Only Enabled installs the EnemyHealthBars HealthBar.LateUpdate compatibility guard. Auto is treated as disabled to avoid optional-mod per-frame overhead."); ShipLootPlusUiHelperGuardMode = config.Bind<PatchEnableMode>("OptionalCompatibility", "ShipLootPlusUiHelperGuardMode", PatchEnableMode.Disabled, "Only Enabled installs the ShipLootPlus UiHelper compatibility guard. Auto is treated as disabled to avoid optional-mod UI refresh overhead."); NightVisionInsideLightingPostfixGuardMode = config.Bind<PatchEnableMode>("OptionalCompatibility", "NightVisionInsideLightingPostfixGuardMode", PatchEnableMode.Disabled, "Only Enabled installs the ToggleableNightVision InsideLightingPostfix compatibility guard. Auto is treated as disabled to avoid optional-mod lighting update overhead."); ChatCommandsStartHostPostfixGuardMode = config.Bind<PatchEnableMode>("OptionalCompatibility", "ChatCommandsStartHostPostfixGuardMode", PatchEnableMode.Auto, "Auto enables the ChatCommands StartHost postfix compatibility guard only on a verified game assembly when the expected target signature is present. Enabled forces it on; Disabled turns it off."); EnemyAINavMeshGuardMode = config.Bind<PatchEnableMode>("EnemyAI.NavMesh", "EnemyAINavMeshGuardMode", PatchEnableMode.Disabled, "Only Enabled installs the EnemyAI.DoAIInterval hot-path guard. Auto is treated as disabled to avoid per-enemy AI tick overhead. Requires restart."); EnableEnemyAINavMeshGuard = config.Bind<bool>("EnemyAI.NavMesh", "EnableEnemyAINavMeshGuard", true, "Legacy switch for the EnemyAI NavMesh guard. EnemyAINavMeshGuardMode can also disable this patch. Requires restart."); AllowEnemyAIWarp = config.Bind<bool>("EnemyAI.NavMesh", "AllowEnemyAIWarp", false, "Allows the guard to warp an off-mesh enemy back to a nearby valid NavMesh point."); EnemyAINavMeshMaxWarpRadius = config.Bind<float>("EnemyAI.NavMesh", "EnemyAINavMeshMaxWarpRadius", 16f, "Maximum radius used when looking for a nearby NavMesh recovery point."); EnemyAINavMeshHostServerOnly = config.Bind<bool>("EnemyAI.NavMesh", "EnemyAINavMeshHostServerOnly", true, "Only allows active EnemyAI NavMesh recovery on host/server. Non-authority clients only suppress the unsafe tick."); } } internal static class GameAssemblyIdentity { internal const string VerifiedAssemblySha256 = "5f7db5538b78dc408845a3002907619785ac9f9c6b6059d13dc9a602d9b65731"; internal const string VerifiedAssemblyMvid = "aca1e98d-6f84-4d3f-85cd-22b6f7be2f9b"; internal static string CurrentAssemblySha256 { get; private set; } internal static string CurrentAssemblyMvid { get; private set; } internal static bool IsVerified { get; private set; } internal static void Initialize() { Assembly assembly = typeof(StartOfRound).Assembly; CurrentAssemblyMvid = assembly.ManifestModule.ModuleVersionId.ToString(); CurrentAssemblySha256 = TryComputeSha256(ResolveAssemblyPath(assembly.Location, assembly.GetName().Name + ".dll", GetManagedPath(), File.Exists)); IsVerified = string.Equals(CurrentAssemblyMvid, "aca1e98d-6f84-4d3f-85cd-22b6f7be2f9b", StringComparison.OrdinalIgnoreCase) && string.Equals(CurrentAssemblySha256, "5f7db5538b78dc408845a3002907619785ac9f9c6b6059d13dc9a602d9b65731", StringComparison.OrdinalIgnoreCase); } internal static string ResolveAssemblyPath(string assemblyLocation, string assemblyFileName, string managedPath, Func<string, bool> fileExists) { if (!string.IsNullOrEmpty(assemblyLocation) && fileExists(assemblyLocation)) { return assemblyLocation; } if (!string.IsNullOrEmpty(managedPath) && !string.IsNullOrEmpty(assemblyFileName)) { string text = Path.Combine(managedPath, assemblyFileName); if (fileExists(text)) { return text; } } return null; } private static string GetManagedPath() { try { if (!string.IsNullOrEmpty(Paths.ManagedPath)) { return Paths.ManagedPath; } } catch { } try { if (!string.IsNullOrEmpty(Paths.GameRootPath)) { return Path.Combine(Paths.GameRootPath, "Lethal Company_Data", "Managed"); } } catch { } return null; } private static string TryComputeSha256(string path) { try { if (string.IsNullOrEmpty(path) || !File.Exists(path)) { return null; } using SHA256 sHA = SHA256.Create(); using FileStream inputStream = File.OpenRead(path); return BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", string.Empty).ToLowerInvariant(); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Could not compute Assembly-CSharp SHA256: " + ex.GetType().Name + ".")); } return null; } } } internal static class NullRefGuard { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static readonly HashSet<string> LoggedExceptionDetails = new HashSet<string>(); internal static Exception Suppress(Exception exception, string key, Func<bool> isKnownSafeCase) { if (exception == null) { return null; } if (!(exception is NullReferenceException)) { return exception; } if (!IsKnownSafeCase(key, isKnownSafeCase)) { return exception; } Warnings.Warn(key, () => LoggedExceptionDetails.Add(key) ? $"Suppressed known NullReferenceException in {key}. First exception detail: {exception}" : ("Suppressed known NullReferenceException in " + key + "; the object will safely retry on later updates.")); return null; } internal static void Clear() { Warnings.Clear(); LoggedExceptionDetails.Clear(); } private static bool IsKnownSafeCase(string key, Func<bool> isKnownSafeCase) { if (isKnownSafeCase == null) { return false; } try { return isKnownSafeCase(); } catch (Exception ex) { Warnings.Warn("classifier-failed|" + key, "NullRefGuard classifier failed for " + key + "; returning original exception: " + ex.GetType().Name + "."); return false; } } } internal sealed class ParticleMeshShapeGuard : MonoBehaviour { private sealed class ParticleMeshWarningBatch { internal readonly string MeshName; internal readonly string Reason; internal readonly bool DryRun; internal readonly HashSet<string> ParticleSystemNames = new HashSet<string>(); internal int OmittedParticleSystemCount; internal ParticleMeshWarningBatch(string meshName, string reason, bool dryRun) { MeshName = meshName; Reason = reason; DryRun = dryRun; } internal void AddParticleSystemName(string particleSystemName) { if (ParticleSystemNames.Count < 16) { ParticleSystemNames.Add(particleSystemName); } else if (!ParticleSystemNames.Contains(particleSystemName)) { OmittedParticleSystemCount++; } } } private sealed class CachedMeshInspection { internal readonly Mesh Mesh; internal readonly MeshInspectionStatus Status; internal readonly string InvalidReason; internal CachedMeshInspection(Mesh mesh, MeshInspectionStatus status, string invalidReason) { Mesh = mesh; Status = status; InvalidReason = invalidReason; } } private sealed class MeshInspectionProgress { internal readonly Mesh Mesh; internal readonly int VertexCount; internal readonly int SubMeshCount; internal readonly int SubMeshIndex; internal readonly int TriangleIndex; internal readonly double Area; internal MeshInspectionProgress(Mesh mesh, int vertexCount, int subMeshCount, int subMeshIndex, int triangleIndex, double area) { Mesh = mesh; VertexCount = vertexCount; SubMeshCount = subMeshCount; SubMeshIndex = subMeshIndex; TriangleIndex = triangleIndex; Area = area; } } private enum MeshInspectionStatus { Valid, Invalid, Incomplete } private const int ScanBatchSize = 64; private const float ScanFrameBudgetSeconds = 0.0025f; private const int MeshInspectionCacheCleanupThreshold = 256; private const int MaxFullAreaScanVertices = 32768; private const int MaxFullAreaScanIndices = 98304; private const int MaxPendingWarningBatches = 64; private const int MaxParticleSystemNamesPerWarning = 16; private static ParticleMeshShapeGuard _instance; private bool _scanRequested; private ParticleSystem[] _scanQueue; private int _scanIndex; private readonly Dictionary<int, ParticleSystem> _patchedParticleSystems = new Dictionary<int, ParticleSystem>(); private readonly WarningLimiter _warnings = new WarningLimiter(); private readonly Dictionary<string, ParticleMeshWarningBatch> _pendingWarnings = new Dictionary<string, ParticleMeshWarningBatch>(); private readonly Dictionary<int, CachedMeshInspection> _meshInspectionCache = new Dictionary<int, CachedMeshInspection>(); private readonly Dictionary<int, MeshInspectionProgress> _meshInspectionProgress = new Dictionary<int, MeshInspectionProgress>(); private List<Vector3> _meshVertices = new List<Vector3>(); private List<int> _meshTriangles = new List<int>(); internal static void EnsureCreated() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0030: Unknown result type (might be due to invalid IL or missing references) ParticleMeshShapeGuard particleMeshShapeGuard = Object.FindObjectOfType<ParticleMeshShapeGuard>(); if ((Object)(object)particleMeshShapeGuard != (Object)null) { _instance = particleMeshShapeGuard; _instance.RequestSceneScan(); return; } GameObject val = new GameObject("V81ErrorFix.ParticleMeshShapeGuard"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; _instance = val.AddComponent<ParticleMeshShapeGuard>(); _instance.RequestSceneScan(); } internal static void NotifySceneLoaded() { if (!((Object)(object)_instance == (Object)null)) { _instance.RequestSceneScan(); } } internal static void NotifySceneUnloaded() { if (!((Object)(object)_instance == (Object)null)) { _instance.ClearSceneCaches(); } } private void Awake() { _instance = this; } private void OnDestroy() { if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } } private void Update() { if (_scanQueue != null) { ContinueParticleSystemScan(); } else if (_scanRequested) { _scanRequested = false; BeginParticleSystemScan(); ContinueParticleSystemScan(); } } private void RequestSceneScan() { _scanRequested = true; } private void BeginParticleSystemScan() { _pendingWarnings.Clear(); _scanQueue = Object.FindObjectsOfType<ParticleSystem>(true); _scanIndex = 0; } private void ContinueParticleSystemScan() { if (_scanQueue != null) { int num = Math.Min(_scanIndex + 64, _scanQueue.Length); float num2 = Time.realtimeSinceStartup + 0.0025f; while (_scanIndex < num && Time.realtimeSinceStartup < num2) { TryPatchParticleSystem(_scanQueue[_scanIndex], num2); _scanIndex++; } if (_scanIndex >= _scanQueue.Length) { _scanQueue = null; _scanIndex = 0; FlushParticleMeshWarnings(); } } } private void TryPatchParticleSystem(ParticleSystem particleSystem, float scanDeadline) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)particleSystem == (Object)null) { return; } int instanceID = ((Object)particleSystem).GetInstanceID(); if (_patchedParticleSystems.TryGetValue(instanceID, out var value) && (Object)(object)value == (Object)(object)particleSystem) { return; } try { ShapeModule shape = particleSystem.shape; if (!((ShapeModule)(ref shape)).enabled || !TryGetShapeMesh(shape, out var mesh) || (Object)(object)mesh == (Object)null) { return; } string invalidReason; switch (GetInvalidMeshReasonCached(mesh, scanDeadline, out invalidReason)) { case MeshInspectionStatus.Incomplete: _scanIndex--; return; case MeshInspectionStatus.Valid: return; } if (!IsDryRun()) { ((ShapeModule)(ref shape)).enabled = false; } _patchedParticleSystems[instanceID] = particleSystem; QueueParticleMeshWarning(((Object)mesh).name, invalidReason, ((Object)particleSystem).name, IsDryRun()); } catch (Exception ex) { _patchedParticleSystems[instanceID] = particleSystem; QueueParticleMeshWarning("inspection", "could not be inspected safely: " + ex.GetType().Name, ((Object)particleSystem).name); } } private void ClearSceneCaches() { _scanQueue = null; _scanIndex = 0; _scanRequested = false; _pendingWarnings.Clear(); _patchedParticleSystems.Clear(); _meshInspectionCache.Clear(); _meshInspectionProgress.Clear(); _warnings.Clear(); } private void QueueParticleMeshWarning(string meshName, string reason, string particleSystemName, bool dryRun = false) { string key = $"{meshName}|{reason}|{dryRun}"; if (!_warnings.CanWarn(key)) { return; } if (!_pendingWarnings.TryGetValue(key, out var value)) { if (_pendingWarnings.Count >= 64) { return; } value = new ParticleMeshWarningBatch(meshName, reason, dryRun); _pendingWarnings[key] = value; } value.AddParticleSystemName(particleSystemName); } private void FlushParticleMeshWarnings() { foreach (KeyValuePair<string, ParticleMeshWarningBatch> pendingWarning in _pendingWarnings) { ParticleMeshWarningBatch warningBatch = pendingWarning.Value; _warnings.Warn(pendingWarning.Key, delegate { string text = string.Join(", ", warningBatch.ParticleSystemNames); if (warningBatch.OmittedParticleSystemCount > 0) { text = $"{text}, and {warningBatch.OmittedParticleSystemCount} more"; } string text2 = (warningBatch.DryRun ? "Would disable" : "Disabled"); return text2 + " mesh shape because mesh '" + warningBatch.MeshName + "' " + warningBatch.Reason + " on particle systems: " + text + "."; }); } } private MeshInspectionStatus GetInvalidMeshReasonCached(Mesh mesh, float scanDeadline, out string invalidReason) { invalidReason = null; int instanceID = ((Object)mesh).GetInstanceID(); if (_meshInspectionCache.TryGetValue(instanceID, out var value) && (Object)(object)value.Mesh == (Object)(object)mesh) { invalidReason = value.InvalidReason; return value.Status; } MeshInspectionStatus invalidMeshReason = GetInvalidMeshReason(mesh, instanceID, scanDeadline, out invalidReason); if (invalidMeshReason == MeshInspectionStatus.Incomplete) { return invalidMeshReason; } _meshInspectionProgress.Remove(instanceID); _meshInspectionCache[instanceID] = new CachedMeshInspection(mesh, invalidMeshReason, invalidReason); CleanupMeshInspectionCacheIfNeeded(); return invalidMeshReason; } private void CleanupMeshInspectionCacheIfNeeded() { if (_meshInspectionCache.Count < 256) { return; } List<int> list = null; foreach (KeyValuePair<int, CachedMeshInspection> item in _meshInspectionCache) { if (!((Object)(object)item.Value.Mesh != (Object)null)) { if (list == null) { list = new List<int>(); } list.Add(item.Key); } } if (list != null) { for (int i = 0; i < list.Count; i++) { _meshInspectionCache.Remove(list[i]); } } } private static bool TryGetShapeMesh(ShapeModule shape, out Mesh mesh) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_0012: Invalid comparison between Unknown and I4 //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 mesh = null; ParticleSystemShapeType shapeType = ((ShapeModule)(ref shape)).shapeType; if ((int)shapeType != 6) { if ((int)shapeType != 13) { if ((int)shapeType == 14) { mesh = (((Object)(object)((ShapeModule)(ref shape)).skinnedMeshRenderer != (Object)null) ? ((ShapeModule)(ref shape)).skinnedMeshRenderer.sharedMesh : null); return (Object)(object)mesh != (Object)null; } return false; } object obj; if (!((Object)(object)((ShapeModule)(ref shape)).meshRenderer != (Object)null)) { obj = null; } else { MeshFilter component = ((Component)((ShapeModule)(ref shape)).meshRenderer).GetComponent<MeshFilter>(); obj = ((component != null) ? component.sharedMesh : null); } mesh = (Mesh)obj; return (Object)(object)mesh != (Object)null; } mesh = ((ShapeModule)(ref shape)).mesh; return (Object)(object)mesh != (Object)null; } private MeshInspectionStatus GetInvalidMeshReason(Mesh mesh, int meshId, float scanDeadline, out string invalidReason) { //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //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_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) invalidReason = null; if (!mesh.isReadable) { invalidReason = "is not readable"; return MeshInspectionStatus.Invalid; } int vertexCount = mesh.vertexCount; int subMeshCount = mesh.subMeshCount; if (vertexCount == 0 || subMeshCount <= 0) { invalidReason = "has zero surface area"; return MeshInspectionStatus.Invalid; } try { bool flag = false; ulong num = 0uL; for (int i = 0; i < subMeshCount; i++) { uint indexCount = mesh.GetIndexCount(i); if (indexCount >= 3) { flag = true; } num += indexCount; } if (!flag) { invalidReason = "has zero surface area"; return MeshInspectionStatus.Invalid; } if (vertexCount > 32768 || num > 98304) { return MeshInspectionStatus.Valid; } MeshInspectionProgress meshInspectionProgress = null; if (_meshInspectionProgress.TryGetValue(meshId, out var value) && (Object)(object)value.Mesh == (Object)(object)mesh && value.VertexCount == vertexCount && value.SubMeshCount == subMeshCount) { meshInspectionProgress = value; } _meshVertices.Clear(); _meshTriangles.Clear(); mesh.GetVertices(_meshVertices); int num2 = Math.Min(meshInspectionProgress?.SubMeshIndex ?? 0, subMeshCount - 1); for (int j = num2; j < subMeshCount; j++) { _meshTriangles.Clear(); mesh.GetTriangles(_meshTriangles, j); if (_meshTriangles.Count < 3) { continue; } double num3 = ((j != num2) ? 0.0 : (meshInspectionProgress?.Area ?? 0.0)); int num4 = ((j == num2) ? (meshInspectionProgress?.TriangleIndex ?? 0) : 0); for (int k = num4 - num4 % 3; k + 2 < _meshTriangles.Count; k += 3) { Vector3 val = _meshVertices[_meshTriangles[k]]; Vector3 val2 = _meshVertices[_meshTriangles[k + 1]]; Vector3 val3 = _meshVertices[_meshTriangles[k + 2]]; double num5 = num3; Vector3 val4 = Vector3.Cross(val2 - val, val3 - val); num3 = num5 + (double)((Vector3)(ref val4)).magnitude * 0.5; if (num3 > 0.0001) { return MeshInspectionStatus.Valid; } if (Time.realtimeSinceStartup >= scanDeadline) { _meshInspectionProgress[meshId] = new MeshInspectionProgress(mesh, vertexCount, subMeshCount, j, Math.Min(k + 3, _meshTriangles.Count), num3); return MeshInspectionStatus.Incomplete; } } } } catch { invalidReason = "could not be inspected safely"; return MeshInspectionStatus.Invalid; } finally { TrimMeshScratchLists(); } invalidReason = "has zero surface area"; return MeshInspectionStatus.Invalid; } private void TrimMeshScratchLists() { if (_meshVertices.Capacity > 65536) { _meshVertices = new List<Vector3>(); } else { _meshVertices.Clear(); } if (_meshTriangles.Capacity > 65536) { _meshTriangles = new List<int>(); } else { _meshTriangles.Clear(); } } private static bool IsDryRun() { if (ErrorFixConfig.ParticleMeshShapeGuardDryRun != null) { return ErrorFixConfig.ParticleMeshShapeGuardDryRun.Value; } return false; } } internal sealed class WarningLimiter { private const string OverflowKey = "__overflow__"; private static readonly List<WarningLimiter> SceneScopedLimiters = new List<WarningLimiter>(); private readonly int _maxWarnings; private readonly int _maxKeyCount; private readonly Dictionary<string, int> _warningCounts = new Dictionary<string, int>(); internal WarningLimiter(int maxWarnings = 5, int maxKeyCount = 512, bool clearOnSceneChange = true) { _maxWarnings = maxWarnings; _maxKeyCount = Math.Max(1, maxKeyCount); if (clearOnSceneChange) { SceneScopedLimiters.Add(this); } } internal void Warn(string key, string message) { if (TryIncrement(key, out var warningCount)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"{message} ({warningCount}/{_maxWarnings})"); } } } internal void Warn(string key, Func<string> messageFactory) { if (TryIncrement(key, out var warningCount)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"{messageFactory()} ({warningCount}/{_maxWarnings})"); } } } internal bool CanWarn(string key) { _warningCounts.TryGetValue(GetEffectiveKeyForRead(NormalizeKey(key)), out var value); return value < _maxWarnings; } internal int KeyCount() { return _warningCounts.Count; } internal void Clear() { _warningCounts.Clear(); } internal void ClearPrefix(string prefix) { if (string.IsNullOrEmpty(prefix) || _warningCounts.Count == 0) { return; } List<string> list = null; foreach (string key in _warningCounts.Keys) { if (key.StartsWith(prefix, StringComparison.Ordinal)) { if (list == null) { list = new List<string>(); } list.Add(key); } } if (list != null) { for (int i = 0; i < list.Count; i++) { _warningCounts.Remove(list[i]); } } } internal static void ClearSceneScopedLimiters() { for (int i = 0; i < SceneScopedLimiters.Count; i++) { SceneScopedLimiters[i]?.Clear(); } } private bool TryIncrement(string key, out int warningCount) { key = NormalizeKey(key); key = ApplyKeyLimit(key); _warningCounts.TryGetValue(key, out warningCount); if (warningCount >= _maxWarnings) { return false; } warningCount++; _warningCounts[key] = warningCount; return true; } private string ApplyKeyLimit(string incomingKey) { if (_warningCounts.ContainsKey(incomingKey) || _warningCounts.Count < _maxKeyCount) { return incomingKey; } if (_warningCounts.ContainsKey("__overflow__") || _warningCounts.Count < _maxKeyCount + 1) { return "__overflow__"; } string text = null; foreach (string key in _warningCounts.Keys) { if (!(key == "__overflow__")) { text = key; break; } } if (text != null) { _warningCounts.Remove(text); } return "__overflow__"; } private string GetEffectiveKeyForRead(string incomingKey) { if (_warningCounts.ContainsKey(incomingKey) || _warningCounts.Count < _maxKeyCount) { return incomingKey; } return "__overflow__"; } private static string NormalizeKey(string key) { if (!string.IsNullOrEmpty(key)) { return key; } return "unknown"; } } [HarmonyPatch] internal static class DisabledAudioSourcePlayGuardPatch { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static readonly WarningLimiter GuardFailureWarnings = new WarningLimiter(); [HarmonyPrepare] private static bool Prepare() { return PatchModeUtility.IsExplicitlyEnabled(ErrorFixConfig.AudioSourcePlaybackGuardMode); } [HarmonyTargetMethods] private static IEnumerable<MethodBase> TargetMethods() { MethodInfo[] methods = typeof(AudioSource).GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (methodInfo.DeclaringType == typeof(AudioSource) && IsPlaybackMethod(methodInfo)) { yield return methodInfo; } } } private static bool Prefix(AudioSource __instance, MethodBase __originalMethod) { try { return ShouldAllowPlayback(__instance, __originalMethod); } catch (Exception ex) { GuardFailureWarnings.Warn("guard-failure", "Disabled AudioSource guard failed safely and allowed original playback: " + ex.GetType().Name + "."); return true; } } private static bool ShouldAllowPlayback(AudioSource audioSource, MethodBase originalMethod) { if ((Object)(object)audioSource == (Object)null || ((Behaviour)audioSource).isActiveAndEnabled) { return true; } string methodName = originalMethod?.Name ?? "Play"; string key = $"{((Object)audioSource).GetInstanceID()}|{methodName}"; Warnings.Warn(key, delegate { string transformPath = GetTransformPath(((Component)audioSource).transform); return $"Suppressed disabled AudioSource {methodName} on '{transformPath}' because AudioSource.enabled={((Behaviour)audioSource).enabled}, activeInHierarchy={(Object)(object)((Component)audioSource).gameObject != (Object)null && ((Component)audioSource).gameObject.activeInHierarchy}."; }); return false; } private static bool IsPlaybackMethod(MethodInfo method) { if (method.ReturnType == typeof(void)) { if (!(method.Name == "Play") && !(method.Name == "PlayDelayed") && !(method.Name == "PlayScheduled")) { return method.Name == "PlayOneShot"; } return true; } return false; } internal static string GetTransformPath(Transform transform) { if ((Object)(object)transform == (Object)null) { return "unknown"; } string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } } [HarmonyPatch] internal static class AudioSourcePlayOneShotNullClipGuardPatch { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static readonly WarningLimiter GuardFailureWarnings = new WarningLimiter(); [HarmonyPrepare] private static bool Prepare() { return PatchModeUtility.IsExplicitlyEnabled(ErrorFixConfig.AudioSourcePlaybackGuardMode); } [HarmonyTargetMethods] private static IEnumerable<MethodBase> TargetMethods() { MethodInfo methodInfo = AccessTools.Method(typeof(AudioSource), "PlayOneShot", new Type[1] { typeof(AudioClip) }, (Type[])null); if (methodInfo != null) { yield return methodInfo; } MethodInfo methodInfo2 = AccessTools.Method(typeof(AudioSource), "PlayOneShot", new Type[2] { typeof(AudioClip), typeof(float) }, (Type[])null); if (methodInfo2 != null) { yield return methodInfo2; } } private static bool Prefix(AudioSource __instance, AudioClip __0) { try { if ((Object)(object)__0 != (Object)null) { return true; } WarnNullClip(__instance); return false; } catch (Exception ex) { GuardFailureWarnings.Warn("guard-failure", "AudioSource null AudioClip guard failed safely and allowed original PlayOneShot: " + ex.GetType().Name + "."); return true; } } private static void WarnNullClip(AudioSource audioSource) { string key = (((Object)(object)audioSource != (Object)null) ? $"null-clip|{((Object)audioSource).GetInstanceID()}" : "null-clip|unknown"); Warnings.Warn(key, delegate { string text = (((Object)(object)audioSource != (Object)null) ? DisabledAudioSourcePlayGuardPatch.GetTransformPath(((Component)audioSource).transform) : "unknown"); return "Suppressed AudioSource.PlayOneShot on '" + text + "' because AudioClip was null."; }); } } [HarmonyPatch] internal static class BepInExKnownLogNoiseFilterPatch { private enum KnownBepInExLogNoise { None, RuntimeIcons, PathfindingLib, LethalPerformanceSave, LethalPerformanceInactiveSearch } private const string RuntimeIconsBetterRotationsPrefix = "[debit_card_debit-RuntimeIcons_BetterRotations] Overriding "; private const string RuntimeIconsReadPrefix = "[LethalCompanyModding-RuntimeIcons] Reading "; private const string RuntimeIconsOverridePrefix = "[LethalCompanyModding-RuntimeIcons] Overriding RuntimeIcons/"; private const string PathfindingLibAreaMaskPrefix = "Changed prefabbed NavMeshAgent "; private const string LethalPerformanceInactiveSearchSuffix = " search called with inactive objects, probably will cause incompatibility!"; private static int runtimeIconsFilteredCount; private static int pathfindingLibFilteredCount; private static int lethalPerformanceSaveFilteredCount; private static int lethalPerformanceInactiveSearchFilteredCount; [HarmonyPrepare] private static bool Prepare() { if (!PatchModeUtility.IsExplicitlyEnabled(ErrorFixConfig.KnownBepInExLogNoiseFilterMode)) { return false; } bool num = TargetMethod() != null; if (!num) { ManualLogSource log = Plugin.Log; if (log == null) { return num; } log.LogWarning((object)"Known BepInEx log noise filter disabled because Logger.InternalLogEvent was not found."); } return num; } [HarmonyTargetMethod] private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Logger), "InternalLogEvent", new Type[2] { typeof(object), typeof(LogEventArgs) }, (Type[])null); } private static bool Prefix(object sender, LogEventArgs eventArgs) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (eventArgs == null) { return true; } object sourceName; if (eventArgs.Source == null) { object obj = ((sender is ILogSource) ? sender : null); sourceName = ((obj != null) ? ((ILogSource)obj).SourceName : null); } else { sourceName = eventArgs.Source.SourceName; } if (!ShouldSuppress(message: eventArgs.Data?.ToString(), sourceName: (string)sourceName, level: eventArgs.Level, noise: out var noise)) { return true; } IncrementFilteredCount(noise); return false; } internal static bool ShouldSuppressForTest(string sourceName, string levelName, string message) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!Enum.TryParse<LogLevel>(levelName, ignoreCase: true, out LogLevel result)) { return false; } KnownBepInExLogNoise noise; return ShouldSuppress(sourceName, result, message, out noise); } internal static void FlushSummary() { int num = Interlocked.Exchange(ref runtimeIconsFilteredCount, 0); int num2 = Interlocked.Exchange(ref pathfindingLibFilteredCount, 0); int num3 = Interlocked.Exchange(ref lethalPerformanceSaveFilteredCount, 0); int num4 = Interlocked.Exchange(ref lethalPerformanceInactiveSearchFilteredCount, 0); if (num != 0 || num2 != 0 || num3 != 0 || num4 != 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Known BepInEx log noise filter suppressed logs since the last scene change: " + $"runtimeIcons={num}, " + $"pathfindingLib={num2}, " + $"lethalPerformanceSaves={num3}, " + $"lethalPerformanceInactiveSearch={num4}.")); } } } private static bool ShouldSuppress(string sourceName, LogLevel level, string message, out KnownBepInExLogNoise noise) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Invalid comparison between Unknown and I4 //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Invalid comparison between Unknown and I4 //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Invalid comparison between Unknown and I4 //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Invalid comparison between Unknown and I4 noise = KnownBepInExLogNoise.None; if (string.IsNullOrEmpty(sourceName) || string.IsNullOrEmpty(message)) { return false; } if (sourceName == "RuntimeIcons" && (int)level == 32 && (message.StartsWith("[debit_card_debit-RuntimeIcons_BetterRotations] Overriding ", StringComparison.Ordinal) || message.StartsWith("[LethalCompanyModding-RuntimeIcons] Reading ", StringComparison.Ordinal) || message.StartsWith("[LethalCompanyModding-RuntimeIcons] Overriding RuntimeIcons/", StringComparison.Ordinal))) { noise = KnownBepInExLogNoise.RuntimeIcons; return true; } if (sourceName == "PathfindingLib" && (int)level == 32 && (message.StartsWith("Changed prefabbed NavMeshAgent ", StringComparison.Ordinal) || message.IndexOf(" connects to ", StringComparison.Ordinal) >= 0)) { noise = KnownBepInExLogNoise.PathfindingLib; return true; } if (sourceName == "LethalPerformance.Patcher" && (int)level == 16 && message.StartsWith("Saved ", StringComparison.Ordinal) && message.EndsWith(" config(s)", StringComparison.Ordinal)) { noise = KnownBepInExLogNoise.LethalPerformanceSave; return true; } if (sourceName == "LethalPerformance" && (int)level == 16 && message.StartsWith("Saved ", StringComparison.Ordinal) && message.EndsWith(" save(s)", StringComparison.Ordinal)) { noise = KnownBepInExLogNoise.LethalPerformanceSave; return true; } if (sourceName == "LethalPerformance" && (int)level == 4 && message.EndsWith(" search called with inactive objects, probably will cause incompatibility!", StringComparison.Ordinal)) { noise = KnownBepInExLogNoise.LethalPerformanceInactiveSearch; return true; } return false; } private static void IncrementFilteredCount(KnownBepInExLogNoise noise) { switch (noise) { case KnownBepInExLogNoise.RuntimeIcons: Interlocked.Increment(ref runtimeIconsFilteredCount); break; case KnownBepInExLogNoise.PathfindingLib: Interlocked.Increment(ref pathfindingLibFilteredCount); break; case KnownBepInExLogNoise.LethalPerformanceSave: Interlocked.Increment(ref lethalPerformanceSaveFilteredCount); break; case KnownBepInExLogNoise.LethalPerformanceInactiveSearch: Interlocked.Increment(ref lethalPerformanceInactiveSearchFilteredCount); break; } } } [HarmonyPatch(typeof(BushWolfEnemy), "Update")] internal static class BushWolfEnemyUpdatePatch { [HarmonyPrepare] private static bool Prepare() { return GameplayEnemyUpdatePatchGate.ShouldPatchConfigured(); } private static bool Prefix(BushWolfEnemy __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null) { return false; } if ((Object)(object)((EnemyAI)__instance).agent == (Object)null || (Object)(object)((EnemyAI)__instance).creatureAnimator == (Object)null || (Object)(object)__instance.animationContainer == (Object)null) { return false; } return true; } private static Exception Finalizer(BushWolfEnemy __instance, Exception __exception) { if (__exception == null) { return null; } if (!(__exception is NullReferenceException)) { return __exception; } if ((Object)(object)__instance != (Object)null) { try { ((EnemyAI)__instance).targetPlayer = null; ((EnemyAI)__instance).movingTowardsTargetPlayer = false; } catch { } } return NullRefGuard.Suppress(__exception, "BushWolfEnemy.Update", () => (Object)(object)__instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)((EnemyAI)__instance).agent == (Object)null || (Object)(object)((EnemyAI)__instance).creatureAnimator == (Object)null || (Object)(object)__instance.animationContainer == (Object)null); } } [HarmonyPatch(typeof(BushWolfEnemy), "LateUpdate")] internal static class BushWolfEnemyLateUpdatePatch { [HarmonyPrepare] private static bool Prepare() { return GameplayEnemyUpdatePatchGate.ShouldPatchConfigured(); } private static bool Prefix(BushWolfEnemy __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null) { return false; } if ((Object)(object)__instance.tongue == (Object)null || (Object)(object)__instance.tongueStartPoint == (Object)null || (Object)(object)__instance.animationContainer == (Object)null || (Object)(object)__instance.bendHeadBack == (Object)null || __instance.proceduralBodyTargets == null || __instance.IKTargetContainers == null || __instance.IKTargetContainers.Length < __instance.proceduralBodyTargets.Length) { return false; } return true; } private static Exception Finalizer(BushWolfEnemy __instance, Exception __exception) { if (__exception == null) { return null; } if (!(__exception is NullReferenceException)) { return __exception; } return NullRefGuard.Suppress(__exception, "BushWolfEnemy.LateUpdate", () => (Object)(object)__instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)__instance.tongue == (Object)null || (Object)(object)__instance.tongueStartPoint == (Object)null || (Object)(object)__instance.animationContainer == (Object)null || (Object)(object)__instance.bendHeadBack == (Object)null || __instance.proceduralBodyTargets == null || __instance.IKTargetContainers == null || __instance.IKTargetContainers.Length < __instance.proceduralBodyTargets.Length); } } internal static class GameplayEnemyUpdatePatchGate { internal static bool ShouldPatch(PatchEnableMode mode) { return mode == PatchEnableMode.Enabled; } internal static bool ShouldPatch(PatchEnableMode mode, bool isVerifiedAssembly) { return mode == PatchEnableMode.Enabled; } internal static bool ShouldPatchConfigured() { return ShouldPatch(ErrorFixConfig.GameplayEnemyUpdateGuardMode?.Value ?? PatchEnableMode.Disabled); } } [HarmonyPatch(typeof(DocileLocustBeesAI), "Update")] internal static class DocileLocustBeesAIUpdatePatch { [HarmonyPrepare] private static bool Prepare() { return GameplayEnemyUpdatePatchGate.ShouldPatchConfigured(); } private static bool Prefix(DocileLocustBeesAI __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.activeCamera == (Object)null || (Object)(object)__instance.bugsEffect == (Object)null || (Object)(object)((EnemyAI)__instance).creatureVoice == (Object)null || (Object)(object)__instance.scanNode == (Object)null) { return false; } if (((EnemyAI)__instance).currentBehaviourStateIndex == 1 && ((Object)(object)((EnemyAI)__instance).creatureSFX == (Object)null || (Object)(object)((EnemyAI)__instance).enemyType == (Object)null || ((EnemyAI)__instance).enemyType.audioClips == null || ((EnemyAI)__instance).enemyType.audioClips.Length == 0 || (Object)(object)RoundManager.Instance == (Object)null)) { return false; } return true; } private static Exception Finalizer(DocileLocustBeesAI __instance, Exception __exception) { if (__exception == null) { return null; } if (!(__exception is NullReferenceException)) { return __exception; } return NullRefGuard.Suppress(__exception, "DocileLocustBeesAI.Update", () => (Object)(object)__instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.activeCamera == (Object)null || (Object)(object)__instance.bugsEffect == (Object)null || (Object)(object)((EnemyAI)__instance).creatureVoice == (Object)null || (Object)(object)__instance.scanNode == (Object)null || (((EnemyAI)__instance).currentBehaviourStateIndex == 1 && ((Object)(object)((EnemyAI)__instance).creatureSFX == (Object)null || (Object)(object)((EnemyAI)__instance).enemyType == (Object)null || ((EnemyAI)__instance).enemyType.audioClips == null || ((EnemyAI)__instance).enemyType.audioClips.Length == 0 || (Object)(object)RoundManager.Instance == (Object)null))); } } [HarmonyPatch(typeof(CrawlerAI), "Update")] internal static class CrawlerAIUpdatePatch { [HarmonyPrepare] private static bool Prepare() { return GameplayEnemyUpdatePatchGate.ShouldPatchConfigured(); } private static bool Prefix(CrawlerAI __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)GameNetworkManager.Instance == (Object)null || (Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return false; } if ((Object)(object)((EnemyAI)__instance).agent == (Object)null || (Object)(object)((EnemyAI)__instance).creatureAnimator == (Object)null || (Object)(object)((Component)__instance).transform == (Object)null || __instance.searchForPlayers == null) { return false; } return true; } private static Exception Finalizer(CrawlerAI __instance, Exception __exception) { if (__exception == null) { return null; } if (!(__exception is NullReferenceException)) { return __exception; } if ((Object)(object)__instance != (Object)null) { try { ((EnemyAI)__instance).movingTowardsTargetPlayer = false; } catch { } } return NullRefGuard.Suppress(__exception, "CrawlerAI.Update", () => (Object)(object)__instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)GameNetworkManager.Instance == (Object)null || (Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null || (Object)(object)((EnemyAI)__instance).agent == (Object)null || (Object)(object)((EnemyAI)__instance).creatureAnimator == (Object)null || (Object)(object)((Component)__instance).transform == (Object)null || __instance.searchForPlayers == null); } } [HarmonyPatch(typeof(EntranceTeleport), "Update")] internal static class EntranceTeleportUpdatePatch { private sealed class HoverTipState { internal string DefaultHoverTip; } private const float EnemyNearDistanceSqr = 59.289997f; private static readonly WarningLimiter Warnings = new WarningLimiter(); private static readonly ConditionalWeakTable<EntranceTeleport, HoverTipState> HoverTipStates = new ConditionalWeakTable<EntranceTeleport, HoverTipState>(); [HarmonyPrepare] private static bool Prepare() { return ShouldPatch(ErrorFixConfig.EntranceTeleportUpdateGuardMode?.Value ?? PatchEnableMode.Disabled); } private static bool Prefix(EntranceTeleport __instance) { //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) if (!IsPatchEnabled()) { return true; } if ((Object)(object)__instance == (Object)null || !__instance.isEntranceToBuilding || (Object)(object)RoundManager.Instance == (Object)null) { return false; } InteractTrigger triggerScript = __instance.triggerScript; if ((Object)(object)triggerScript == (Object)null) { Warnings.Warn("missing-trigger", "Skipped EntranceTeleport.Update guard because triggerScript was missing."); return false; } if (__instance.checkForEnemiesInterval > 0f) { __instance.checkForEnemiesInterval -= Time.deltaTime; return false; } if (!__instance.gotExitPoint) { if (__instance.FindExitPoint()) { __instance.gotExitPoint = true; } else { Warnings.Warn("missing-exit|" + GetEntranceName(__instance), "Skipped EntranceTeleport.Update for '" + GetEntranceName(__instance) + "' because no exit point was found."); } return false; } if (((Object)(object)__instance.exitScript == (Object)null || (Object)(object)__instance.exitScript.entrancePoint == (Object)null) && !__instance.FindExitPoint()) { Warnings.Warn("missing-exit-point|" + GetEntranceName(__instance), "Skipped EntranceTeleport.Update for '" + GetEntranceName(__instance) + "' because exitScript or entrancePoint was missing."); return false; } __instance.checkForEnemiesInterval = 1f; bool flag = false; if (RoundManager.Instance.SpawnedEnemies != null) { foreach (EnemyAI spawnedEnemy in RoundManager.Instance.SpawnedEnemies) { if (!((Object)(object)spawnedEnemy == (Object)null) && !((Object)(object)((Component)spawnedEnemy).transform == (Object)null) && !spawnedEnemy.isEnemyDead) { if ((Object)(object)__instance.exitScript == (Object)null || (Object)(object)__instance.exitScript.entrancePoint == (Object)null) { break; } Vector3 val = ((Component)spawnedEnemy).transform.position - __instance.exitScript.entrancePoint.position; if (((Vector3)(ref val)).sqrMagnitude < 59.289997f) { flag = true; break; } } } } if (flag && !__instance.enemyNearLastCheck) { __instance.enemyNearLastCheck = true; SaveDefaultHoverTip(__instance, triggerScript); triggerScript.hoverTip = "[Near activity detected!]"; } else if (!flag && __instance.enemyNearLastCheck) { __instance.enemyNearLastCheck = false; triggerScript.hoverTip = GetDefaultHoverTip(__instance, triggerScript); } return false; } private static Exception Finalizer(EntranceTeleport __instance, Exception __exception) { if (__exception == null) { return null; } if (!(__exception is NullReferenceException)) { return __exception; } return NullRefGuard.Suppress(__exception, "EntranceTeleport.Update", () => IsPatchEnabled() && ((Object)(object)__instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null || (Object)(object)__instance.triggerScript == (Object)null || (Object)(object)__instance.exitScript == (Object)null || (Object)(object)__instance.exitScript.entrancePoint == (Object)null)); } private static bool IsPatchEnabled() { return ShouldPatch(ErrorFixConfig.EntranceTeleportUpdateGuardMode?.Value ?? PatchEnableMode.Disabled); } internal static bool ShouldPatch(PatchEnableMode mode) { return mode == PatchEnableMode.Enabled; } internal static bool ShouldPatch(PatchEnableMode mode, bool isVerifiedAssembly) { return mode == PatchEnableMode.Enabled; } private static void SaveDefaultHoverTip(EntranceTeleport entrance, InteractTrigger trigger) { if (!((Object)(object)entrance == (Object)null) && !((Object)(object)trigger == (Object)null)) { HoverTipState orCreateValue = HoverTipStates.GetOrCreateValue(entrance); if (!string.Equals(trigger.hoverTip, "[Near activity detected!]", StringComparison.Ordinal)) { orCreateValue.DefaultHoverTip = trigger.hoverTip; } } } private static string GetDefaultHoverTip(EntranceTeleport entrance, InteractTrigger trigger) { if ((Object)(object)entrance != (Object)null && HoverTipStates.TryGetValue(entrance, out var value) && !string.IsNullOrEmpty(value.DefaultHoverTip)) { return value.DefaultHoverTip; } if (!((Object)(object)trigger != (Object)null)) { return string.Empty; } return trigger.hoverTip; } private static string GetEntranceName(EntranceTeleport entrance) { if (!((Object)(object)entrance != (Object)null) || !((Object)(object)((Component)entrance).gameObject != (Object)null)) { return "unknown"; } return ((Object)((Component)entrance).gameObject).name; } } [HarmonyPatch(typeof(RoundManager), "FindMainEntrancePosition")] internal static class RoundManagerFindMainEntrancePositionPatch { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static EntranceTeleport[] cachedEntrances; private static int cachedSceneHandle = int.MinValue; [HarmonyPrepare] private static bool Prepare() { return PatchModeUtility.IsEnabled(ErrorFixConfig.FindMainEntrancePositionFallbackMode); } private static bool Prefix(bool getTeleportPosition, bool getOutsideEntrance, ref Vector3 __result) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) EntranceTeleport[] array = GetCachedEntrances(); EntranceTeleport fallbackEntrance = null; EntranceTeleport fallbackMainEntrance = null; if (TryFindEntrance(array, getTeleportPosition, getOutsideEntrance, ref __result, ref fallbackEntrance, ref fallbackMainEntrance)) { return false; } if ((Object)(object)fallbackEntrance == (Object)null && array.Length != 0) { array = RefreshEntranceCache(); if (TryFindEntrance(array, getTeleportPosition, getOutsideEntrance, ref __result, ref fallbackEntrance, ref fallbackMainEntrance)) { return false; } } EntranceTeleport val = fallbackMainEntrance ?? fallbackEntrance; if ((Object)(object)val != (Object)null) { __result = GetEntrancePosition(val, getTeleportPosition); Warn("Main entrance position was missing; using the first available EntranceTeleport instead of origin."); return false; } __result = Vector3.zero; if (!IsCompanyLevel()) { Warn("Main entrance position was missing and no EntranceTeleport fallback existed; returning origin."); } return false; } private static bool TryFindEntrance(EntranceTeleport[] entrances, bool getTeleportPosition, bool getOutsideEntrance, ref Vector3 result, ref EntranceTeleport fallbackEntrance, ref EntranceTeleport fallbackMainEntrance) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) foreach (EntranceTeleport val in entrances) { if (!((Object)(object)val == (Object)null)) { if (fallbackEntrance == null) { fallbackEntrance = val; } if (val.entranceId == 0 && fallbackMainEntrance == null) { fallbackMainEntrance = val; } if (val.entranceId == 0 && val.isEntranceToBuilding == getOutsideEntrance) { result = GetEntrancePosition(val, getTeleportPosition); return true; } } } return false; } private static EntranceTeleport[] GetCachedEntrances() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); int handle = ((Scene)(ref activeScene)).handle; if (cachedEntrances == null || cachedSceneHandle != handle || cachedEntrances.Length == 0) { return RefreshEntranceCache(handle); } return cachedEntrances; } private static EntranceTeleport[] RefreshEntranceCache() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); return RefreshEntranceCache(((Scene)(ref activeScene)).handle); } private static EntranceTeleport[] RefreshEntranceCache(int activeSceneHandle) { cachedEntrances = Object.FindObjectsOfType<EntranceTeleport>(false); cachedSceneHandle = activeSceneHandle; return cachedEntrances; } internal static void ClearCache() { cachedEntrances = null; cachedSceneHandle = int.MinValue; Warnings.Clear(); } private static Vector3 GetEntrancePosition(EntranceTeleport entrance, bool getTeleportPosition) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)entrance == (Object)null) { return Vector3.zero; } if (getTeleportPosition && (Object)(object)entrance.entrancePoint != (Object)null) { return entrance.entrancePoint.position; } if (!((Object)(object)((Component)entrance).transform != (Object)null)) { return Vector3.zero; } return ((Component)entrance).transform.position; } private static bool IsCompanyLevel() { SelectableLevel val = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.currentLevel : null); if ((Object)(object)val == (Object)null) { return false; } if (val.levelID != 3 && !string.Equals(val.sceneName, "CompanyBuilding", StringComparison.OrdinalIgnoreCase)) { if (val.PlanetName != null) { return val.PlanetName.IndexOf("company", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } return true; } private static void Warn(string message) { Warnings.Warn("FindMainEntrancePosition", message); } } [HarmonyPatch(typeof(HUDManager), "AddChatMessage")] internal static class HUDManagerAddChatMessagePatch { private const int MaxWarnings = 5; private static int _warningCount; private static bool Prefix(HUDManager __instance) { if (IsHudChatReady(__instance, out var missingDependency)) { return true; } Warn("Skipped HUDManager.AddChatMessage because chat HUD was not ready: " + missingDependency + "."); return false; } private static Exception Finalizer(HUDManager __instance, Exception __exception) { if (__exception is NullReferenceException && !IsHudChatReady(__instance, out var _)) { Warn("Suppressed HUDManager.AddChatMessage NullReferenceException while chat HUD was not ready."); return null; } return __exception; } internal static bool IsHudChatReady(HUDManager hudManager, out string missingDependency) { if ((Object)(object)hudManager == (Object)null) { missingDependency = "HUDManager"; return false; } if (hudManager.Chat == null) { missingDependency = "HUDManager.Chat"; return false; } if ((Object)(object)hudManager.chatText == (Object)null) { missingDependency = "HUDManager.chatText"; return false; } if (hudManager.ChatMessageHistory == null) { missingDependency = "HUDManager.ChatMessageHistory"; return false; } if ((Object)(object)GameNetworkManager.Instance == (Object)null) { missingDependency = "GameNetworkManager.Instance"; return false; } if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { missingDependency = "GameNetworkManager.Instance.localPlayerController"; return false; } if ((Object)(object)StartOfRound.Instance == (Object)null) { missingDependency = "StartOfRound.Instance"; return false; } if (StartOfRound.Instance.allPlayerScripts == null || StartOfRound.Instance.allPlayerScripts.Length < 4) { missingDependency = "StartOfRound.Instance.allPlayerScripts"; return false; } int num = Math.Min(4, StartOfRound.Instance.allPlayerScripts.Length); for (int i = 0; i < num; i++) { if ((Object)(object)StartOfRound.Instance.allPlayerScripts[i] == (Object)null) { missingDependency = $"StartOfRound.Instance.allPlayerScripts[{i}]"; return false; } } missingDependency = string.Empty; return true; } internal static void Warn(string message) { if (_warningCount < 5) { _warningCount++; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"{message} ({_warningCount}/{5})"); } } } } [HarmonyPatch(typeof(HUDManager), "AddTextMessageClientRpc")] internal static class HUDManagerAddTextMessageClientRpcPatch { private static readonly WarningLimiter FinalizerWarnings = new WarningLimiter(1); private static bool Prefix(HUDManager __instance) { if (!IsExecutingClientRpc(__instance)) { return true; } if (HUDManagerAddChatMessagePatch.IsHudChatReady(__instance, out var missingDependency)) { return true; } HUDManagerAddChatMessagePatch.Warn("Skipped HUDManager.AddTextMessageClientRpc because chat HUD was not ready: " + missingDependency + "."); return false; } private static Exception Finalizer(HUDManager __instance, Exception __exception) { if (!(__exception is NullReferenceException)) { return __exception; } if (!RpcExecStageUtility.ShouldAllowClientRpcSuppression((NetworkBehaviour)(object)__instance, "HUDManager.AddTextMessageClientRpc", __exception, FinalizerWarnings)) { return __exception; } if (!HUDManagerAddChatMessagePatch.IsHudChatReady(__instance, out var _)) { HUDManagerAddChatMessagePatch.Warn("Suppressed HUDManager.AddTextMessageClientRpc NullReferenceException while chat HUD was not ready."); return null; } return __exception; } private static bool IsExecutingClientRpc(HUDManager hudManager) { bool isExecuting; return RpcExecStageUtility.TryIsExecuting((NetworkBehaviour)(object)hudManager, out isExecuting) && isExecuting; } } [HarmonyPatch(typeof(HUDManager), "SyncAllPlayerLevelsServerRpc", new Type[] { })] internal static class HUDManagerSyncAllPlayerLevelsServerRpcPatch { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static bool Prefix(HUDManager __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)((NetworkBehaviour)__instance).NetworkManager == (Object)null || !((NetworkBehaviour)__instance).NetworkManager.IsListening) { return false; } if ((((NetworkBehaviour)__instance).NetworkManager.IsClient || ((NetworkBehaviour)__instance).NetworkManager.IsHost) && ((NetworkBehaviour)__instance).OwnerClientId != ((NetworkBehaviour)__instance).NetworkManager.LocalClientId) { Warnings.Warn("not-owner", "Skipped HUDManager.SyncAllPlayerLevelsServerRpc because the local client does not own HUDManager."); return false; } return true; } } [HarmonyPatch(typeof(InteractTrigger), "UpdateUsedByPlayerClientRpc")] internal static class InteractTriggerUpdateUsedByPlayerClientRpcPatch { private const int MaxWarnings = 5; private static int _warningCount; private static readonly WarningLimiter FinalizerWarnings = new WarningLimiter(1); private static bool Prefix(InteractTrigger __instance, int playerNum) { if (!IsExecutingClientRpc(__instance)) { return true; } if (IsInteractRpcReady(__instance, playerNum, out var missingDependency)) { return true; } Warn($"Skipped InteractTrigger.UpdateUsedByPlayerClientRpc for player {playerNum} because dependencies were not ready: {missingDependency}."); return false; } private static Exception Finalizer(InteractTrigger __instance, int playerNum, Exception __exception) { if (!(__exception is NullReferenceException)) { return __exception; } if (!RpcExecStageUtility.ShouldAllowClientRpcSuppression((NetworkBehaviour)(object)__instance, "InteractTrigger.UpdateUsedByPlayerClientRpc", __exception, FinalizerWarnings)) { return __exception; } if (!IsInteractRpcReady(__instance, playerNum, out var _)) { Warn("Suppressed InteractTrigger.UpdateUsedByPlayerClientRpc NullReferenceException while trigger dependencies were not ready."); return null; } return __exception; } private static bool IsInteractRpcReady(InteractTrigger trigger, int playerNum, out string missingDependency) { if ((Object)(object)trigger == (Object)null) { missingDependency = "InteractTrigger"; return false; } if (trigger.onInteractEarlyOtherClients == null) { missingDependency = GetTriggerName(trigger) + ".onInteractEarlyOtherClients"; return false; } if (!TryGetPlayer(playerNum, out var player, out missingDependency)) { return false; } if ((Object)(object)GameNetworkManager.Instance == (Object)null) { missingDependency = "GameNetworkManager.Instance"; return false; } PlayerControllerB localPlayerController = GameNetworkManager.Instance.localPlayerController; if ((Object)(object)localPlayerController == (Object)null || (Object)(object)((Component)localPlayerController).transform == (Object)null) { missingDependency = "GameNetworkManager.Instance.localPlayerController"; return false; } if (trigger.specialCharacterAnimation && trigger.setVehicleAnimation && (Object)(object)player.gameplayCamera == (Object)null) { missingDependency = $"player {playerNum} gameplayCamera"; return false; } missingDependency = string.Empty; return true; } private static bool TryGetPlayer(int playerNum, out PlayerControllerB player, out string missingDependency) { player = null; if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null || playerNum < 0 || playerNum >= StartOfRound.Instance.allPlayerScripts.Length) { missingDependency = "StartOfRound.Instance.allPlayerScripts"; return false; } player = StartOfRound.Instance.allPlayerScripts[playerNum]; if ((Object)(object)player == (Object)null || (Object)(object)((Component)player).transform == (Object)null) { missingDependency = $"StartOfRound.Instance.allPlayerScripts[{playerNum}]"; return false; } missingDependency = string.Empty; return true; } private static string GetTriggerName(InteractTrigger trigger) { if (!((Object)(object)trigger != (Object)null) || !((Object)(object)((Component)trigger).gameObject != (Object)null)) { return "unknown trigger"; } return ((Object)((Component)trigger).gameObject).name; } private static void Warn(string message) { if (_warningCount < 5) { _warningCount++; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"{message} ({_warningCount}/{5})"); } } } private static bool IsExecutingClientRpc(InteractTrigger trigger) { bool isExecuting; return RpcExecStageUtility.TryIsExecuting((NetworkBehaviour)(object)trigger, out isExecuting) && isExecuting; } } [HarmonyPatch(typeof(RadMechAI), "SetExplosion")] internal static class RadMechAISetExplosionPatch { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static Exception Finalizer(RadMechAI __instance, Exception __exception) { return SuppressKnownException(__exception, "RadMechAI.SetExplosion", Warnings, () => HasKnownMissingExplosionDependency(__instance)); } internal static Exception SuppressKnownException(Exception exception, string key, WarningLimiter warnings, Func<bool> isKnownSafeCase) { if (!(exception is NullReferenceException) || isKnownSafeCase == null) { return exception; } bool flag; try { flag = isKnownSafeCase(); } catch (Exception ex) { warnings.Warn(key + "|classifier-failed", "Known dependency classifier failed for " + key + "; returning original NullReferenceException: " + ex.GetType().Name + "."); return exception; } if (flag) { warnings.Warn(key, "Suppressed " + key + " NullReferenceException for a known missing dependency."); return null; } return exception; } internal static bool HasKnownMissingExplosionDependency(RadMechAI mech) { if (!((Object)(object)mech == (Object)null) && !((Object)(object)GameNetworkManager.Instance == (Object)null) && !((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) && !((Object)(object)((Component)GameNetworkManager.Instance.localPlayerController).transform == (Object)null) && !((Object)(object)StartOfRound.Instance == (Object)null) && !((Object)(object)mech.explosionAudio == (Object)null)) { return mech.largeExplosionSFX == null; } return true; } } [HarmonyPatch(typeof(RadMechAI), "SetExplosionClientRpc")] internal static class RadMechAISetExplosionClientRpcPatch { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static readonly WarningLimiter NonExecuteStageWarnings = new WarningLimiter(1); private static Exception Finalizer(RadMechAI __instance, Exception __exception) { if (__exception is NullReferenceException && !RpcExecStageUtility.ShouldAllowClientRpcSuppression((NetworkBehaviour)(object)__instance, "RadMechAI.SetExplosionClientRpc", __exception, NonExecuteStageWarnings)) { return __exception; } return RadMechAISetExplosionPatch.SuppressKnownException(__exception, "RadMechAI.SetExplosionClientRpc", Warnings, () => RadMechAISetExplosionPatch.HasKnownMissingExplosionDependency(__instance)); } } [HarmonyPatch(typeof(JetpackItem), "DeactivateJetpack")] internal static class JetpackItemDeactivateJetpackPatch { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static Exception Finalizer(JetpackItem __instance, Exception __exception) { return RadMechAISetExplosionPatch.SuppressKnownException(__exception, "JetpackItem.DeactivateJetpack", Warnings, () => JetpackItemKnownDependencyGuard.HasKnownMissingDeactivateDependency(__instance)); } } [HarmonyPatch(typeof(JetpackItem), "ItemActivate")] internal static class JetpackItemItemActivatePatch { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static Exception Finalizer(JetpackItem __instance, Exception __exception) { return RadMechAISetExplosionPatch.SuppressKnownException(__exception, "JetpackItem.ItemActivate", Warnings, () => JetpackItemKnownDependencyGuard.HasKnownMissingActivateDependency(__instance)); } } [HarmonyPatch(typeof(GrabbableObject), "ActivateItemRpc")] internal static class GrabbableObjectActivateItemRpcPatch { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static Exception Finalizer(GrabbableObject __instance, Exception __exception) { JetpackItem jetpack = (JetpackItem)(object)((__instance is JetpackItem) ? __instance : null); if (jetpack == null) { return __exception; } return RadMechAISetExplosionPatch.SuppressKnownException(__exception, "GrabbableObject.ActivateItemRpc", Warnings, () => JetpackItemKnownDependencyGuard.HasKnownMissingActivateDependency(jetpack)); } } internal static class JetpackItemKnownDependencyGuard { internal static bool HasKnownMissingDeactivateDependency(JetpackItem jetpack) { if (!((Object)(object)jetpack == (Object)null) && !((Object)(object)jetpack.previousPlayerHeldBy == (Object)null) && !((Object)(object)jetpack.jetpackBeepsAudio == (Object)null) && !((Object)(object)jetpack.jetpackAudio == (Object)null)) { return (Object)(object)jetpack.smokeTrailParticle == (Object)null; } return true; } internal static bool HasKnownMissingActivateDependency(JetpackItem jetpack) { if (!((Object)(object)jetpack == (Object)null) && !((Object)(object)((GrabbableObject)jetpack).playerHeldBy == (Object)null) && !((Object)(object)jetpack.jetpackAudio == (Object)null) && !((Object)(object)jetpack.smokeTrailParticle == (Object)null)) { if (jetpack.streamlineJetpack) { if (!((Object)(object)StartOfRound.Instance == (Object)null) && !((Object)(object)GameNetworkManager.Instance == (Object)null)) { return (Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null; } return true; } return false; } return true; } } [HarmonyPatch(typeof(LobbySlot), "SetModdedIcon")] internal static class LobbySlotSetModdedIconPatch { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static bool Prefix(LobbySlot __instance, ModdedState moddedState) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (IsKnownMissingIcon(__instance, moddedState, out var reason)) { Warnings.Warn($"missing-icon|{moddedState}|{reason}", "Skipped LobbySlot.SetModdedIcon because " + reason + "."); return false; } return true; } private static Exception Finalizer(LobbySlot __instance, ModdedState moddedState, Exception __exception) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (__exception == null) { return null; } if (!(__exception is NullReferenceException)) { return __exception; } string reason; return NullRefGuard.Suppress(__exception, "LobbySlot.SetModdedIcon", () => IsKnownMissingIcon(__instance, moddedState, out reason)); } private static bool IsKnownMissingIcon(LobbySlot slot, ModdedState moddedState, out string reason) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 if ((Object)(object)slot == (Object)null) { reason = "slot was null"; return true; } if ((int)moddedState == 0 && (Object)(object)slot.modStateUnknownIcon == (Object)null) { reason = "modStateUnknownIcon was missing"; return true; } if ((int)moddedState == 2 && (Object)(object)slot.modStateTrueIcon == (Object)null) { reason = "modStateTrueIcon was missing"; return true; } reason = string.Empty; return false; } } [HarmonyPatch(typeof(DisplayPlayerMicVolume), "InitMic")] internal static class DisplayPlayerMicVolumeInitMicPatch { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static bool Prefix(DisplayPlayerMicVolume __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)IngamePlayerSettings.Instance == (Object)null || IngamePlayerSettings.Instance.unsavedSettings == null) { return false; } IngamePlayerSettings.Instance.RefreshAndDisplayCurrentMicrophone(false); string micDevice = IngamePlayerSettings.Instance.unsavedSettings.micDevice; if (!IsValidMicrophoneDevice(micDevice)) { return false; } string device = __instance._device; if (IsValidMicrophoneDevice(device) && Microphone.IsRecording(device)) { Microphone.End(device); } try { int num = default(int); int num2 = default(int); Microphone.GetDeviceCaps(micDevice, ref num, ref num2); int num3 = ((num2 <= 0) ? 44100 : Mathf.Clamp(44100, Mathf.Max(num, 1), num2)); __instance._device = micDevice; __instance._clipRecord = Microphone.Start(micDevice, true, 1, num3); } catch (Exception ex) when (ex is ArgumentException || ex is InvalidOperationException) { Warn("Skipping microphone preview for unavailable device '" + micDevice + "': " + ex.Message); __instance._clipRecord = null; } return false; } internal static void Warn(string message) { Warnings.Warn("DisplayPlayerMicVolume", message); } internal static bool IsValidMicrophoneDevice(string device) { if (string.IsNullOrWhiteSpace(device) || device == "none" || device == "LCNoMic" || Microphone.devices == null) { return false; } return Array.IndexOf(Microphone.devices, device) >= 0; } } [HarmonyPatch(typeof(DisplayPlayerMicVolume), "StopMicrophone")] internal static class DisplayPlayerMicVolumeStopMicrophonePatch { private static bool Prefix(DisplayPlayerMicVolume __instance) { if ((Object)(object)__instance == (Object)null) { return false; } try { string device = __instance._device; if (DisplayPlayerMicVolumeInitMicPatch.IsValidMicrophoneDevice(device) && Microphone.IsRecording(device)) { Microphone.End(device); } } catch (Exception ex) { DisplayPlayerMicVolumeInitMicPatch.Warn("Skipping microphone stop because it failed safely: " + ex.GetType().Name); } return false; } } [HarmonyPatch(typeof(DisplayPlayerMicVolume), "LevelMax")] internal static class DisplayPlayerMicVolumeLevelMaxPatch { private static bool Prefix(DisplayPlayerMicVolume __instance, ref float __result) { __result = 0f; if ((Object)(object)__instance == (Object)null || (Object)(object)IngamePlayerSettings.Instance == (Object)null || IngamePlayerSettings.Instance.unsavedSettings == null) { return false; } string micDevice = IngamePlayerSettings.Instance.unsavedSettings.micDevice; if (!DisplayPlayerMicVolumeInitMicPatch.IsValidMicrophoneDevice(micDevice) || !Microphone.IsRecording(micDevice)) { return false; } try { return (Object)(object)__instance._clipRecord != (Object)null; } catch (Exception ex) { DisplayPlayerMicVolumeInitMicPatch.Warn("Skipping microphone level preview because it failed safely: " + ex.GetType().Name); return false; } } } [HarmonyPatch(typeof(NavMeshSurface), "CollectSources")] internal static class NavMeshSurfaceCollectSourcesPatch { private static readonly WarningLimiter Warnings = new WarningLimiter(); private static readonly WarningLimiter GuardFailureWarnings = new WarningLimiter(); private static void Postfix(NavMeshSurface __instance, ref List<NavMeshBuildSource> __result) { try { FilterUnreadableMeshSources(__instance, __result); } catch (Exception ex) { GuardFailureWarnings.Warn("guard-failure", "NavMeshSurface source filter failed safely and left sources unchanged: " + ex.GetType().Name + "."); } } private static void FilterUnreadableMeshSources(NavMeshSurface surface, List<NavMeshBuildSource> sources) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (sources == null || sources.Count == 0) { return; } int num = 0; string surfaceName = GetSurfaceName(surface); bool flag = ShouldLog(surfaceName); HashSet<string> hashSet = (flag ? new HashSet<string>() : null); for (int num2 = sources.Count - 1; num2 >= 0; num2--) { if (TryGetUnreadableMesh(sources[num2], out var mesh)) { sources.RemoveAt(num2); num++; hashSet?.Add(GetMeshName(mesh)); } } if (num > 0 && flag) { Warn(surfaceName, hashSet, num); } } private static bool TryGetUnreadableMesh(NavMeshBuildSource source, out Mesh mesh) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) mesh = null; if ((int)((NavMeshBuildSource)(ref source)).shape == 0) { Object sourceObject = ((NavMeshBuildSource)(ref source)).sourceObject; Mesh val = (Mesh)(object)((sourceObject is Mesh) ? sourceObject : null); if (val != null && !((Object)(object)val == (Object)null)) { mesh = val; return !val.isReadable; } } return false; } private static bool ShouldLog(string surfaceName) { string key = "unreadable-navmesh-sources|" + surfaceName; return Warnings.CanWarn(key); } private static void Warn(string surfaceName, HashSet<string> meshNames, int removedCount) { string key = "unreadable-navmesh-sources|" + surfaceName; Warnings.Warn(key, delegate { string arg = ((meshNames != null && meshNames.Count > 0) ? string.Join(", ", meshNames) : "unknown"); return $"Filtered {removedCount} unreadable mesh source(s) from NavMeshSurface '{surfaceName}' before runtime NavMesh build: {arg}."; }); } private static string GetSurfaceName(NavMeshSurface surface) { if (!((Object)(object)surface != (Object)null) || !((Object)(object)((Component)surface).gameObject != (Object)null)) { return "unknown"; } return ((Object)((Component)surface).gameObject).name; } private static string GetMeshName(Mesh mesh) { if (!((Object)(object)mesh != (Object)null) || string.IsNullOrEmpty(((Object)mesh).name)) { return "unnamed mesh"; } return ((Object)mesh).name; } } [HarmonyPatch(typeof(EnemyAI), "DoAIInterval")] internal static class EnemyAINavMeshGuardPatch { private const float RecoveryAttemptCooldown = 0.5f; private const float RecoveryCacheCleanupInterval = 30f; private static readonly float[] SampleRadii = new float[5] { 2f, 4f, 8f, 16f, 32f }; private static readonly WarningLimiter Warnings = new WarningLimiter(); private static readonly Dictionary<int, float> NextRecoveryAttemptTimes = new Dictionary<int, float>(); private static float _nextRecoveryCacheCleanupTime; [HarmonyPrepare] private static bool Prepare() { return ShouldPatch(ErrorFixConfig.EnemyAINavMeshGuardMode?.Value ?? PatchEnableMode.Disabled, ErrorFixConfig.EnableEnemyAINavMeshGuard?.Value ?? false); } private static bool Prefix(EnemyAI __instance) { try { return GuardEnemyAIInterval(__instance); } catch (Exception ex) { string enemyName = GetEnemyName(__instance); Warn(enemyName, "NavMesh guard failed safely for " + enemyName + ": " + ex.GetType().Name + "."); return true; } } private static bool GuardEnemyAIInterval(EnemyAI enemy) { if (!ShouldPatch(ErrorFixConfig.EnemyAINavMeshGuardMode?.Value ?? PatchEnableMode.Disabled, ErrorFixConfig.EnableEnemyAINavMeshGuard?.Value ?? false)) { return true; } if ((Object)(object)enemy == (Object)null || enemy.inSpecialAnimation || (Object)(object)enemy.agent == (Object)null || !enemy.moveTowardsDestination || !((Behaviour)enemy.agent).enabled || enemy.agent.isOnNavMesh) { return true; } string enemyName = GetEnemyName(enemy); if (!CanAttemptRecovery(enemy)) { return false; } if (ShouldRestrictRecoveryToHostServer() && !IsHostOrServer()) { Warn(enemyName, "Suppressed non-host/client " + enemyName + " SetDestination while its NavMeshAgent is off the NavMesh."); return false; } if (!((NetworkBehaviour)enemy).IsOwner) { Warn(enemyName, "Suppressed non-owner " + enemyName + " SetDestination while its NavMeshAgent is off the NavMesh."); return false; } if (enemy.isEnemyDead) { enemy.moveTowardsDestination = false; TrySyncPositionToClients(enemy); Warn(enemyName, "Suppressed " + enemyName + " SetDestination while it is dead and its NavMeshAgent is off the NavMesh."); return false; } if (ShouldAllowWarp() && TryWarpToNearbyNavMesh(enemy)) { Warn(enemyName, "Recovered " + enemyName + " NavMeshAgent by warping it back onto the NavMesh."); return true; } enemy.moveTowardsDestination = false; TrySyncPositionToClients(enemy); Warn(enemyName, "Suppressed " + enemyName + " SetDestination while its NavMeshAgent is off the NavMesh and no nearby NavMesh point was found."); return false; } private static bool TryWarpToNearbyNavMesh(EnemyAI enemy) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) int areaMask = enemy.agent.areaMask; Vector3 position = ((Component)enemy).transform.position; if (TryWarpToNearbyNavMesh(enemy, position, areaMask)) { return true; } if (areaMask != -1) { return TryWarpToNearbyNavMesh(enemy, position, -1); } return false; } private static bool TryWarpToNearbyNavMesh(EnemyAI enemy, Vector3 position, int areaMask) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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) NavMeshHit val = default(NavMeshHit); for (int i = 0; i < SampleRadii.Length; i++) { float num = SampleRadii[i]; if (num > GetMaxWarpRadius()) { break; } if (NavMesh.SamplePosition(position, ref val, num, areaMask) && IsUsableRecoveryPoint(enemy, ((NavMeshHit)(ref val)).position, areaMask) && enemy.agent.Warp(((NavMeshHit)(ref val)).position)) { if (enemy.agent.isOnNavMesh) { enemy.agent.ResetPath(); } return true; } } return false; } private static bool ShouldAllowWarp() { if (ErrorFixConfig.AllowEnemyAIWarp != null) { return ErrorFixConfig.AllowEnemyAIWarp.Value; } return true; } private static bool ShouldRestrictRecoveryToHostServer() { if (ErrorFixConfig.EnemyAINavMeshHostServerOnly != null) { return ErrorFixConfig.EnemyAINavMeshHostServerOnly.Value; } return true; } private static bool IsHostOrServer() { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && !singleton.IsHost) { return singleton.IsServer; } return true; } private static float GetMaxWarpRadius() { return Mathf.Clamp(ErrorFixConfig.EnemyAINavMeshMaxWarpRadius?.Value ?? 32f, 0f, 64f); } private static bool IsUsableRecoveryPoint(EnemyAI enemy, Vector3 position, int areaMask) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 if (enemy.destination == Vector3.zero) { return true; } NavMeshPath val = enemy.path1; if (val == null) { val = (enemy.path1 = new NavMeshPath()); } if (NavMesh.CalculatePath(position, enemy.destination, areaMask, val)) { return (int)val.status != 2; } return false; } private static bool CanAttemptRecovery(EnemyAI enemy) { int instanceID = ((Object)enemy).GetInstanceID(); float realtimeSinceStartup = Time.realtimeSinceStartup; CleanupRecoveryCacheIfNeeded(realtimeSinceStartup); if (NextRecoveryAttemptTimes.TryGetValue(instanceID, out var value) && realtimeSinceStartup < value) { return false; } NextRecoveryAttemptTimes[instanceID] = realtimeSinceStartup + 0.5f; return true; } private static void Warn(string enemyName, string message) { Warnings.Warn(enemyName, message); } private static void CleanupRecoveryCacheIfNeeded(float now) { if (now < _nextRecoveryCacheCleanupTime || NextRecoveryAttemptTimes.Count < 128) { return; } _nextRecoveryCacheCleanupTime = now + 30f; List<int> list = null; foreach (KeyValuePair<int, float> nextRecoveryAttemptTime in NextRecoveryAttemptTimes) { if (!(nextRecoveryAttemptTime.Value >= now)) { if (list == null) { list = new List<int>(); } list.Add(nextRecoveryAttemptTime.Key); } } if (list != null) { for (int i = 0; i < list.Count; i++) { NextRecoveryAttemptTimes.Remove(list[i]); } } } private static string GetEnemyName(EnemyAI enemy) { if ((Object)(object)enemy == (Object)null) { return "Unknown Enemy"; } if ((Object)(object)enemy.enemyType != (Object)null && !string.IsNullOrEmpty(enemy.enemyType.enemyName)) { return enemy.enemyType.enemyName; } return ((Object)enemy).name; } private static void TrySyncPositionToClients(EnemyAI enemy) { try { enemy.SyncPositionToClients(); } catch (Exception ex) { Warn(GetEnemyName(enemy), "Skipped EnemyAI position sync after NavMesh guard because it failed safely: " + ex.GetType().Name + "."); } } internal static bool ShouldPatch(PatchEnableMode mode, bool legacySwitchEnabled) { if (legacySwitchEnabled) { return mode == PatchEnableMode.Enabled; } return false; } } [HarmonyPatch] internal static class NetworkObjectDestroyGuardPatch { private const float WarningInterval = 5f; private const float WarningCacheCleanupInterval = 30f; private static readonly Dictionary<ulong, float> LastWarningTimes = new Dictionary<ulong, float>(); private static readonly WarningLimiter Warnings = new WarningLimiter(); private static readonly WarningLimiter GuardFailureWarnings = new WarningLimiter(); private static bool _loggedBlockedDestroyStackTrace; private static float _nextWarningCacheCleanupTime; [HarmonyPrepare] private static bool Prepare() { return ShouldPatch(ErrorFixConfig.GlobalDestroyGuardMode?.Value ?? PatchEnableMode.Disabled, ErrorFixConfig.EnableGlobalDestroyGuard?.Value ?? false); } [HarmonyTargetMethods] private static IEnumerable<MethodBase> TargetMethods() { MethodInfo methodInfo = AccessTools.Method(typeof(Object), "Destroy", new Type[1] { typeof(Object) }, (Type[])null); if (methodInfo != null) { yield return methodInfo; } MethodInfo methodInfo2 = AccessTools.Method(typeof(Object), "Destroy", new Type[2] { typeof(Object), typeof(float) }, (Type[])null); if (methodInfo2 != null) { yield return methodInfo2; } } private static bool Prefix(Object obj) { try { return ShouldAllowDestroy(obj); } catch (Exception ex) { GuardFailureWarnings.Warn("guard-failure", "NetworkObject destroy guard failed safely and allowed original Destroy: " + ex.GetType().Name + "."); return true; } } private static bool ShouldAllowDestroy(Object obj) { if (obj == (Object)null || (Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.IsServer) { return true; } if (ShouldAllowLifecycleDestroy()) { return true; } bool flag = obj is GameObject; GameObject val = (GameObject)(object)((obj is GameObject) ? obj : null); if ((Object)(object)val == (Object)null) { Component val2 = (Component)(object)((obj is Component) ? obj : null); if (val2 != null) { val = val2.gameObject; } } if ((Object)(object)val == (Object)null) { return true; } RagdollGrabbableObject val3 = val.GetComponent<RagdollGrabbableObject>() ?? val.GetComponentInParent<RagdollGrabbableObject>(); if ((Object)(object)val3 == (Object)null && flag) { val3 = val.GetComponentInChildren<RagdollGrabbableObject>(); } if ((Object)(object)val3 == (Object)null) { return true; } NetworkObject component = ((Component)val3).GetComponent<NetworkObject>(); if ((Object)(object)component == (Object)null || !component.IsSpawned) { return true; } WarnBlockedDestroy(component); return false; } internal static bool ShouldPatch(PatchEnableMode mode, bool legacySwitchEnabled) { if (legacySwitchEnabled) { return mode == PatchEnableMode.Enabled; } return false; } internal static void ClearCaches() { LastWarningTimes.Clear(); Warnings.Clear(); GuardFailureWarnings.Clear(); _loggedBlockedDestroyStackTrace = false; } private static bool ShouldAllowLifecycleDestroy() { if (ErrorFixConfig.AllowDestroyDuringSceneUnload != null && !ErrorFixConfig.AllowDestroyDuringSceneUnload.Value) { return false; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.ShutdownInProgress || !singleton.IsListening) { return tr