Decompiled source of VOIDWARD v1.0.0

VOIDWARD.dll

Decompiled 10 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BoneLib;
using BoneLib.BoneMenu;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Marrow;
using LabFusion.Network;
using LabFusion.Player;
using LabFusion.Representation;
using LabFusion.Senders;
using LabFusion.Utilities;
using MelonLoader;
using MelonLoader.Preferences;
using UnityEngine;
using VOIDWARD;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(VOIDWARDMod), "VOIDWARD", "1.0.0", "VoidIndustries", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("VOIDWARD")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("VOIDWARD")]
[assembly: AssemblyTitle("VOIDWARD")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace VOIDWARD;

internal sealed class FusionHostStabilityGuard
{
	private enum GuardRoleRequirement
	{
		DEFAULT,
		OPERATOR,
		OWNER
	}

	private MelonPreferences_Entry<bool> prefEnableGuard;

	private MelonPreferences_Entry<bool> prefEnableJoinThrottle;

	private MelonPreferences_Entry<float> prefReconnectCooldown;

	private MelonPreferences_Entry<float> prefMaxJoinAttemptsPerMinute;

	private MelonPreferences_Entry<bool> prefEnableTrafficSpikeGuard;

	private MelonPreferences_Entry<float> prefTrafficSpikeKbps;

	private MelonPreferences_Entry<float> prefTrafficStabilizeSeconds;

	private MelonPreferences_Entry<bool> prefEnableIdValidationGuard;

	private MelonPreferences_Entry<bool> prefEnableRoleProtection;

	private MelonPreferences_Entry<float> prefMinimumBypassRole;

	private MelonPreferences_Entry<bool> prefEnableActionCooldowns;

	private MelonPreferences_Entry<float> prefActionWindowSeconds;

	private MelonPreferences_Entry<float> prefMaxActionEventsInWindow;

	private MelonPreferences_Entry<float> prefActionPenaltySeconds;

	private MelonPreferences_Entry<bool> prefEnableDynamicSafetyLists;

	private MelonPreferences_Entry<float> prefSafetyListRefreshSeconds;

	private MelonPreferences_Entry<bool> prefEnableIncidentLog;

	private readonly Queue<float> globalJoinAttempts = new Queue<float>();

	private readonly Dictionary<ulong, float> lastAttemptByPlatform = new Dictionary<ulong, float>();

	private readonly Dictionary<ulong, Queue<float>> playerActionTimes = new Dictionary<ulong, Queue<float>>();

	private readonly HashSet<ulong> blockedPlatformIds = new HashSet<ulong>();

	private readonly HashSet<string> blockedNameTokens = new HashSet<string>();

	private bool subscribed = false;

	private float trafficSampleTimer = 0f;

	private float trafficBytesAccumulator = 0f;

	private float stabilizingUntil = -1f;

	private float lastSpikeLogTime = -100f;

	private float safetyListRefreshTimer = 0f;

	private string safetyFolderPath;

	private string blockedIdsPath;

	private string blockedNamesPath;

	private string incidentLogPath;

	private bool listsLoaded = false;

	public void Initialize(MelonPreferences_Category prefCategory)
	{
		if (prefCategory != null)
		{
			prefEnableGuard = prefCategory.CreateEntry<bool>("EnableHostStabilityGuard", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefEnableJoinThrottle = prefCategory.CreateEntry<bool>("EnableJoinThrottle", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefReconnectCooldown = prefCategory.CreateEntry<float>("ReconnectCooldownSeconds", 8f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefMaxJoinAttemptsPerMinute = prefCategory.CreateEntry<float>("MaxJoinAttemptsPerMinute", 18f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefEnableTrafficSpikeGuard = prefCategory.CreateEntry<bool>("EnableTrafficSpikeGuard", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefTrafficSpikeKbps = prefCategory.CreateEntry<float>("TrafficSpikeKBps", 700f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefTrafficStabilizeSeconds = prefCategory.CreateEntry<float>("TrafficStabilizeSeconds", 12f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefEnableIdValidationGuard = prefCategory.CreateEntry<bool>("EnableIDValidationGuard", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefEnableRoleProtection = prefCategory.CreateEntry<bool>("EnableRoleProtection", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefMinimumBypassRole = prefCategory.CreateEntry<float>("GuardBypassMinRole", 1f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefEnableActionCooldowns = prefCategory.CreateEntry<bool>("EnableActionMessageCooldowns", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefActionWindowSeconds = prefCategory.CreateEntry<float>("ActionWindowSeconds", 8f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefMaxActionEventsInWindow = prefCategory.CreateEntry<float>("MaxActionEventsInWindow", 25f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefActionPenaltySeconds = prefCategory.CreateEntry<float>("ActionPenaltySeconds", 12f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefEnableDynamicSafetyLists = prefCategory.CreateEntry<bool>("EnableDynamicSafetyLists", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefSafetyListRefreshSeconds = prefCategory.CreateEntry<float>("SafetyListRefreshSeconds", 15f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			prefEnableIncidentLog = prefCategory.CreateEntry<bool>("EnablePersistentIncidentLog", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			safetyFolderPath = Path.Combine(Environment.CurrentDirectory, "UserData", "VOIDWARD");
			blockedIdsPath = Path.Combine(safetyFolderPath, "blocked_platform_ids.txt");
			blockedNamesPath = Path.Combine(safetyFolderPath, "blocked_name_tokens.txt");
			incidentLogPath = Path.Combine(safetyFolderPath, "incident_log.txt");
			EnsureSafetyFiles();
			RefreshDynamicSafetyLists(verbose: true);
			Subscribe();
		}
	}

	public void AttachMenu(Page menuPage)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_018c: 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_01dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_020f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		//IL_026e: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0304: Unknown result type (might be due to invalid IL or missing references)
		//IL_033b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0363: Unknown result type (might be due to invalid IL or missing references)
		if (menuPage != null)
		{
			Page val = menuPage.CreatePage("Fusion Guard", new Color(0.85f, 0.95f, 1f), 32, true);
			val.CreateBool("Enable host guard", Color.white, prefEnableGuard.Value, (Action<bool>)delegate(bool value)
			{
				prefEnableGuard.Value = value;
				SavePrefs();
			});
			val.CreateBool("Join throttle", Color.white, prefEnableJoinThrottle.Value, (Action<bool>)delegate(bool value)
			{
				prefEnableJoinThrottle.Value = value;
				SavePrefs();
			});
			val.CreateFloat("Reconnect cooldown (s)", Color.white, prefReconnectCooldown.Value, 1f, 1f, 30f, (Action<float>)delegate(float value)
			{
				prefReconnectCooldown.Value = value;
				SavePrefs();
			});
			val.CreateFloat("Max joins per minute", Color.white, prefMaxJoinAttemptsPerMinute.Value, 1f, 5f, 120f, (Action<float>)delegate(float value)
			{
				prefMaxJoinAttemptsPerMinute.Value = value;
				SavePrefs();
			});
			val.CreateBool("Traffic spike guard", Color.white, prefEnableTrafficSpikeGuard.Value, (Action<bool>)delegate(bool value)
			{
				prefEnableTrafficSpikeGuard.Value = value;
				SavePrefs();
			});
			val.CreateFloat("Traffic spike KB/s", Color.white, prefTrafficSpikeKbps.Value, 10f, 64f, 4096f, (Action<float>)delegate(float value)
			{
				prefTrafficSpikeKbps.Value = value;
				SavePrefs();
			});
			val.CreateFloat("Stabilize time (s)", Color.white, prefTrafficStabilizeSeconds.Value, 1f, 1f, 45f, (Action<float>)delegate(float value)
			{
				prefTrafficStabilizeSeconds.Value = value;
				SavePrefs();
			});
			val.CreateBool("ID validation guard", Color.white, prefEnableIdValidationGuard.Value, (Action<bool>)delegate(bool value)
			{
				prefEnableIdValidationGuard.Value = value;
				SavePrefs();
			});
			val.CreateBool("Role-based protection", Color.white, prefEnableRoleProtection.Value, (Action<bool>)delegate(bool value)
			{
				prefEnableRoleProtection.Value = value;
				SavePrefs();
			});
			val.CreateEnum("Bypass role", Color.white, (Enum)ToRoleRequirement(prefMinimumBypassRole.Value), (Action<Enum>)delegate(Enum value)
			{
				prefMinimumBypassRole.Value = Convert.ToInt32(value);
				SavePrefs();
			});
			val.CreateBool("Action message cooldown", Color.white, prefEnableActionCooldowns.Value, (Action<bool>)delegate(bool value)
			{
				prefEnableActionCooldowns.Value = value;
				SavePrefs();
			});
			val.CreateFloat("Action window (s)", Color.white, prefActionWindowSeconds.Value, 1f, 2f, 30f, (Action<float>)delegate(float value)
			{
				prefActionWindowSeconds.Value = value;
				SavePrefs();
			});
			val.CreateFloat("Max actions in window", Color.white, prefMaxActionEventsInWindow.Value, 1f, 5f, 100f, (Action<float>)delegate(float value)
			{
				prefMaxActionEventsInWindow.Value = value;
				SavePrefs();
			});
			val.CreateFloat("Action penalty (s)", Color.white, prefActionPenaltySeconds.Value, 1f, 1f, 60f, (Action<float>)delegate(float value)
			{
				prefActionPenaltySeconds.Value = value;
				SavePrefs();
			});
			val.CreateBool("Dynamic safety lists", Color.white, prefEnableDynamicSafetyLists.Value, (Action<bool>)delegate(bool value)
			{
				prefEnableDynamicSafetyLists.Value = value;
				SavePrefs();
			});
			val.CreateFloat("List refresh (s)", Color.white, prefSafetyListRefreshSeconds.Value, 1f, 5f, 120f, (Action<float>)delegate(float value)
			{
				prefSafetyListRefreshSeconds.Value = value;
				SavePrefs();
			});
			val.CreateBool("Persistent incident log", Color.white, prefEnableIncidentLog.Value, (Action<bool>)delegate(bool value)
			{
				prefEnableIncidentLog.Value = value;
				SavePrefs();
			});
			val.CreateFunction("Reload safety lists now", Color.cyan, (Action)delegate
			{
				RefreshDynamicSafetyLists(verbose: true);
				MelonLogger.Msg("[VOIDWARD] Dynamic safety lists reloaded.");
			});
		}
	}

	public void Tick(float deltaTime)
	{
		if (!prefEnableGuard.Value || !NetworkInfo.IsHost)
		{
			trafficSampleTimer = 0f;
			trafficBytesAccumulator = 0f;
			safetyListRefreshTimer = 0f;
			return;
		}
		TryRefreshSafetyLists(deltaTime);
		if (!prefEnableTrafficSpikeGuard.Value)
		{
			return;
		}
		trafficSampleTimer += deltaTime;
		trafficBytesAccumulator += NetworkInfo.BytesDown + NetworkInfo.BytesUp;
		if (trafficSampleTimer < 1f)
		{
			return;
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		float num = trafficBytesAccumulator / Mathf.Max(0.01f, trafficSampleTimer);
		float num2 = Mathf.Max(64f, prefTrafficSpikeKbps.Value) * 1024f;
		if (num >= num2)
		{
			stabilizingUntil = Mathf.Max(stabilizingUntil, realtimeSinceStartup + Mathf.Max(1f, prefTrafficStabilizeSeconds.Value));
			if (realtimeSinceStartup - lastSpikeLogTime > 2f)
			{
				lastSpikeLogTime = realtimeSinceStartup;
				MelonLogger.Warning($"[VOIDWARD] Traffic spike detected ({num / 1024f:0} KB/s). Join throttling enabled for stabilization.");
				LogIncident("traffic-spike", null, $"kbps={num / 1024f:0}");
			}
		}
		trafficSampleTimer = 0f;
		trafficBytesAccumulator = 0f;
	}

	public void Dispose()
	{
		Unsubscribe();
		globalJoinAttempts.Clear();
		lastAttemptByPlatform.Clear();
		playerActionTimes.Clear();
		blockedPlatformIds.Clear();
		blockedNameTokens.Clear();
	}

	private void Subscribe()
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Expected O, but got Unknown
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Expected O, but got Unknown
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Expected O, but got Unknown
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Expected O, but got Unknown
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Expected O, but got Unknown
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Expected O, but got Unknown
		if (!subscribed)
		{
			MultiplayerHooking.OnShouldAllowConnection += new UserAccessEvent(OnShouldAllowConnection);
			MultiplayerHooking.OnStartedServer += new ServerEvent(OnStartedServer);
			MultiplayerHooking.OnPlayerJoined += new PlayerUpdate(OnPlayerJoined);
			MultiplayerHooking.OnPlayerLeft += new PlayerUpdate(OnPlayerLeft);
			MultiplayerHooking.OnPlayerAction += new PlayerAction(OnPlayerAction);
			MultiplayerHooking.OnDisconnected += new ServerEvent(OnDisconnected);
			subscribed = true;
		}
	}

	private void Unsubscribe()
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Expected O, but got Unknown
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Expected O, but got Unknown
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Expected O, but got Unknown
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Expected O, but got Unknown
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Expected O, but got Unknown
		if (subscribed)
		{
			MultiplayerHooking.OnShouldAllowConnection -= new UserAccessEvent(OnShouldAllowConnection);
			MultiplayerHooking.OnStartedServer -= new ServerEvent(OnStartedServer);
			MultiplayerHooking.OnPlayerJoined -= new PlayerUpdate(OnPlayerJoined);
			MultiplayerHooking.OnPlayerLeft -= new PlayerUpdate(OnPlayerLeft);
			MultiplayerHooking.OnPlayerAction -= new PlayerAction(OnPlayerAction);
			MultiplayerHooking.OnDisconnected -= new ServerEvent(OnDisconnected);
			subscribed = false;
		}
	}

	private bool OnShouldAllowConnection(PlayerID playerId, out string reason)
	{
		reason = "";
		if (!prefEnableGuard.Value || !NetworkInfo.IsHost)
		{
			return true;
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		PruneAttempts(realtimeSinceStartup);
		if (prefEnableIdValidationGuard.Value && !ValidatePlayerID(playerId, out reason))
		{
			LogIncident("id-validation", playerId, reason);
			return false;
		}
		if (prefEnableDynamicSafetyLists.Value && IsBlockedByDynamicLists(playerId, out reason))
		{
			LogIncident("safety-list", playerId, reason);
			return false;
		}
		if (prefEnableTrafficSpikeGuard.Value && realtimeSinceStartup < stabilizingUntil)
		{
			reason = "Server is stabilizing from high network load. Try reconnecting in a few seconds.";
			LogIncident("traffic-stabilization-reject", playerId, reason);
			return false;
		}
		if (!prefEnableJoinThrottle.Value)
		{
			return true;
		}
		globalJoinAttempts.Enqueue(realtimeSinceStartup);
		int num = Mathf.Max(5, Mathf.RoundToInt(prefMaxJoinAttemptsPerMinute.Value));
		if (globalJoinAttempts.Count > num)
		{
			reason = "Server is handling too many join attempts right now. Please retry shortly.";
			LogIncident("join-throttle", playerId, reason);
			return false;
		}
		ulong platformID = playerId.PlatformID;
		if (lastAttemptByPlatform.TryGetValue(platformID, out var value))
		{
			float num2 = Mathf.Max(1f, prefReconnectCooldown.Value);
			if (realtimeSinceStartup - value < num2)
			{
				reason = $"Reconnect too fast. Wait {num2 - (realtimeSinceStartup - value):0.0}s.";
				LogIncident("reconnect-cooldown", playerId, reason);
				return false;
			}
		}
		lastAttemptByPlatform[platformID] = realtimeSinceStartup;
		return true;
	}

	private void OnStartedServer()
	{
		globalJoinAttempts.Clear();
		lastAttemptByPlatform.Clear();
		playerActionTimes.Clear();
		stabilizingUntil = -1f;
		trafficSampleTimer = 0f;
		trafficBytesAccumulator = 0f;
	}

	private void OnPlayerJoined(PlayerID playerId)
	{
		if (!prefEnableGuard.Value || !NetworkInfo.IsHost || playerId == null || playerId.IsMe)
		{
			return;
		}
		string reason = "";
		bool flag = false;
		if (prefEnableIdValidationGuard.Value && !ValidatePlayerID(playerId, out reason))
		{
			flag = true;
			LogIncident("post-join-id-validation", playerId, reason);
		}
		else if (prefEnableDynamicSafetyLists.Value && IsBlockedByDynamicLists(playerId, out reason))
		{
			flag = true;
			LogIncident("post-join-safety-list", playerId, reason);
		}
		else if (prefEnableJoinThrottle.Value)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			PruneAttempts(realtimeSinceStartup);
			globalJoinAttempts.Enqueue(realtimeSinceStartup);
			int num = Mathf.Max(5, Mathf.RoundToInt(prefMaxJoinAttemptsPerMinute.Value));
			if (globalJoinAttempts.Count > num)
			{
				flag = true;
				reason = "Join burst limit exceeded.";
				LogIncident("post-join-throttle", playerId, reason);
			}
		}
		if (flag)
		{
			NetworkConnectionManager.TimeoutDisconnect(playerId.PlatformID, 0.15f);
			MelonLogger.Warning("[VOIDWARD] Disconnected player after join: " + reason);
		}
		else
		{
			lastAttemptByPlatform[playerId.PlatformID] = Time.realtimeSinceStartup;
		}
	}

	private void OnPlayerLeft(PlayerID playerId)
	{
		if (playerId != null)
		{
			lastAttemptByPlatform[playerId.PlatformID] = Time.realtimeSinceStartup;
		}
	}

	private void OnPlayerAction(PlayerID playerId, PlayerActionType type, PlayerID otherPlayer = null)
	{
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		if (prefEnableGuard.Value && NetworkInfo.IsHost && prefEnableActionCooldowns.Value && playerId != null && !CanBypassProtection(playerId))
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			float num = Mathf.Max(2f, prefActionWindowSeconds.Value);
			int num2 = Mathf.Max(5, Mathf.RoundToInt(prefMaxActionEventsInWindow.Value));
			ulong platformID = playerId.PlatformID;
			if (!playerActionTimes.TryGetValue(platformID, out var value))
			{
				value = new Queue<float>();
				playerActionTimes[platformID] = value;
			}
			value.Enqueue(realtimeSinceStartup);
			while (value.Count > 0 && realtimeSinceStartup - value.Peek() > num)
			{
				value.Dequeue();
			}
			if (value.Count > num2)
			{
				float num3 = Mathf.Max(1f, prefActionPenaltySeconds.Value);
				NetworkConnectionManager.TimeoutDisconnect(platformID, num3);
				value.Clear();
				LogIncident("action-cooldown", playerId, $"type={type}, penalty={num3:0.0}s, count>{num2}/{num:0.0}s");
			}
		}
	}

	private void OnDisconnected()
	{
		globalJoinAttempts.Clear();
		lastAttemptByPlatform.Clear();
		playerActionTimes.Clear();
		trafficSampleTimer = 0f;
		trafficBytesAccumulator = 0f;
		stabilizingUntil = -1f;
	}

	private void PruneAttempts(float now)
	{
		while (globalJoinAttempts.Count > 0 && now - globalJoinAttempts.Peek() > 60f)
		{
			globalJoinAttempts.Dequeue();
		}
		if (lastAttemptByPlatform.Count == 0)
		{
			return;
		}
		float num = Mathf.Max(60f, prefReconnectCooldown.Value * 8f);
		List<ulong> list = null;
		foreach (KeyValuePair<ulong, float> item in lastAttemptByPlatform)
		{
			if (!(now - item.Value <= num))
			{
				if (list == null)
				{
					list = new List<ulong>();
				}
				list.Add(item.Key);
			}
		}
		if (list == null)
		{
			return;
		}
		foreach (ulong item2 in list)
		{
			lastAttemptByPlatform.Remove(item2);
		}
	}

	private bool ValidatePlayerID(PlayerID playerId, out string reason)
	{
		reason = "";
		if (playerId == null || !playerId.IsValid)
		{
			reason = "Invalid player id payload.";
			return false;
		}
		if (playerId.PlatformID == 0L || playerId.PlatformID == ulong.MaxValue)
		{
			reason = "Invalid platform id.";
			return false;
		}
		if (playerId.SmallID == byte.MaxValue)
		{
			reason = "Invalid small id.";
			return false;
		}
		PlayerID playerID = PlayerIDManager.GetPlayerID(playerId.SmallID);
		if (playerID != null && playerID.PlatformID != playerId.PlatformID)
		{
			reason = "SmallID collision detected.";
			return false;
		}
		PlayerID playerID2 = PlayerIDManager.GetPlayerID(playerId.PlatformID);
		if (playerID2 != null && playerID2.SmallID != playerId.SmallID)
		{
			reason = "PlatformID collision detected.";
			return false;
		}
		return true;
	}

	private bool IsBlockedByDynamicLists(PlayerID playerId, out string reason)
	{
		reason = "";
		if (!listsLoaded)
		{
			RefreshDynamicSafetyLists(verbose: false);
		}
		if (blockedPlatformIds.Contains(playerId.PlatformID))
		{
			reason = "Blocked by safety list (platform id).";
			return true;
		}
		if (blockedNameTokens.Count == 0)
		{
			return false;
		}
		string text = "";
		string text2 = default(string);
		if (MetadataHelper.TryGetDisplayName(playerId, ref text2))
		{
			text = text2 ?? "";
		}
		if (string.IsNullOrWhiteSpace(text))
		{
			return false;
		}
		string text3 = text.ToLowerInvariant();
		foreach (string blockedNameToken in blockedNameTokens)
		{
			if (text3.Contains(blockedNameToken))
			{
				reason = "Blocked by safety list token '" + blockedNameToken + "'.";
				return true;
			}
		}
		return false;
	}

	private bool CanBypassProtection(PlayerID playerId)
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Invalid comparison between Unknown and I4
		if (!prefEnableRoleProtection.Value || playerId == null)
		{
			return false;
		}
		PermissionLevel val = default(PermissionLevel);
		if (!MetadataHelper.TryGetPermissionLevel(playerId, ref val))
		{
			return false;
		}
		GuardRoleRequirement guardRoleRequirement = ToRoleRequirement(prefMinimumBypassRole.Value);
		return (int)val >= (int)guardRoleRequirement;
	}

	private GuardRoleRequirement ToRoleRequirement(float rawValue)
	{
		return (GuardRoleRequirement)Mathf.Clamp(Mathf.RoundToInt(rawValue), 0, 2);
	}

	private void TryRefreshSafetyLists(float deltaTime)
	{
		if (prefEnableDynamicSafetyLists.Value)
		{
			safetyListRefreshTimer += deltaTime;
			if (!(safetyListRefreshTimer < Mathf.Max(5f, prefSafetyListRefreshSeconds.Value)))
			{
				safetyListRefreshTimer = 0f;
				RefreshDynamicSafetyLists(verbose: false);
			}
		}
	}

	private void RefreshDynamicSafetyLists(bool verbose)
	{
		EnsureSafetyFiles();
		blockedPlatformIds.Clear();
		blockedNameTokens.Clear();
		string[] array = File.ReadAllLines(blockedIdsPath);
		foreach (string raw in array)
		{
			string text = NormalizeLine(raw);
			if (!string.IsNullOrWhiteSpace(text) && ulong.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				blockedPlatformIds.Add(result);
			}
		}
		string[] array2 = File.ReadAllLines(blockedNamesPath);
		foreach (string raw2 in array2)
		{
			string text2 = NormalizeLine(raw2);
			if (!string.IsNullOrWhiteSpace(text2) && text2.Length >= 2)
			{
				blockedNameTokens.Add(text2.ToLowerInvariant());
			}
		}
		listsLoaded = true;
		if (verbose)
		{
			MelonLogger.Msg($"[VOIDWARD] Safety lists loaded: ids={blockedPlatformIds.Count}, nameTokens={blockedNameTokens.Count}");
		}
	}

	private void EnsureSafetyFiles()
	{
		if (!Directory.Exists(safetyFolderPath))
		{
			Directory.CreateDirectory(safetyFolderPath);
		}
		if (!File.Exists(blockedIdsPath))
		{
			File.WriteAllText(blockedIdsPath, "# one platform id per line" + Environment.NewLine);
		}
		if (!File.Exists(blockedNamesPath))
		{
			File.WriteAllText(blockedNamesPath, "# one lowercase token per line" + Environment.NewLine);
		}
		if (!File.Exists(incidentLogPath))
		{
			File.WriteAllText(incidentLogPath, "# VOIDWARD incident log" + Environment.NewLine);
		}
	}

	private static string NormalizeLine(string raw)
	{
		if (raw == null)
		{
			return "";
		}
		int num = raw.IndexOf('#');
		string text = ((num >= 0) ? raw.Substring(0, num) : raw);
		return text.Trim();
	}

	private void LogIncident(string tag, PlayerID playerId, string detail)
	{
		if (prefEnableIncidentLog.Value)
		{
			EnsureSafetyFiles();
			ulong value = ((playerId != null) ? playerId.PlatformID : 0);
			byte value2 = (byte)((playerId != null) ? playerId.SmallID : 0);
			string value3 = "unknown";
			string text = default(string);
			if (playerId != null && MetadataHelper.TryGetDisplayName(playerId, ref text) && !string.IsNullOrWhiteSpace(text))
			{
				value3 = text;
			}
			string text2 = $"{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}Z | {tag} | platform={value} | small={value2} | name={value3} | {detail}";
			File.AppendAllText(incidentLogPath, text2 + Environment.NewLine);
		}
	}

	private void SavePrefs()
	{
		MelonPreferences.Save();
	}
}
public class VOIDWARDMod : MelonMod
{
	private enum AaLevel
	{
		Off = 0,
		X2 = 2,
		X4 = 4,
		X8 = 8
	}

	private float deltaTime = 0f;

	private float checkTimer = 0f;

	private float cacheTimer = 0f;

	private bool isOptimizedMode = false;

	private bool showFps = true;

	private bool settingsDirty = false;

	private float settingsSaveTimer = 0f;

	private float runtimeCheckInterval = 0f;

	private float runtimeCacheInterval = 0f;

	private float runtimeCullDistance = 0f;

	private float lastFps = 0f;

	private int lastCulledCount = 0;

	private Rigidbody[] cachedBodies = Array.Empty<Rigidbody>();

	private float originalShadowDistance;

	private int originalAntiAliasing;

	private float originalLodBias;

	private int originalPixelLightCount;

	private int originalShadowCascades;

	private ShadowQuality originalShadowQuality;

	private ShadowResolution originalShadowResolution;

	private AnisotropicFiltering originalAniso;

	private int originalTextureLimit;

	private int originalVSyncCount;

	private bool originalSoftParticles;

	private bool originalRealtimeReflectionProbes;

	private int originalSolverIterations;

	private int originalSolverVelocityIterations;

	private Page menuPage;

	private bool menuInitialized = false;

	private Type boneFpsType;

	private object boneFpsInstance;

	private bool boneFpsChecked = false;

	private FusionHostStabilityGuard hostStabilityGuard;

	private static MelonPreferences_Category prefCategory;

	private static MelonPreferences_Entry<float> prefLowFps;

	private static MelonPreferences_Entry<float> prefHighFps;

	private static MelonPreferences_Entry<float> prefCheckInterval;

	private static MelonPreferences_Entry<float> prefCullDistance;

	private static MelonPreferences_Entry<float> prefCacheInterval;

	private static MelonPreferences_Entry<float> prefShadowDistance;

	private static MelonPreferences_Entry<int> prefAntiAliasing;

	private static MelonPreferences_Entry<float> prefLodBias;

	private static MelonPreferences_Entry<bool> prefEnableAuto;

	private static MelonPreferences_Entry<bool> prefEnableCulling;

	private static MelonPreferences_Entry<bool> prefShowFps;

	private static MelonPreferences_Entry<float> prefPixelLightCount;

	private static MelonPreferences_Entry<float> prefShadowCascades;

	private static MelonPreferences_Entry<int> prefShadowQuality;

	private static MelonPreferences_Entry<int> prefShadowResolution;

	private static MelonPreferences_Entry<int> prefAniso;

	private static MelonPreferences_Entry<float> prefTextureLimit;

	private static MelonPreferences_Entry<bool> prefCloseQualityGuard;

	private static MelonPreferences_Entry<float> prefMinCloseLodBias;

	private static MelonPreferences_Entry<float> prefMaxCloseTextureLimit;

	private static MelonPreferences_Entry<float> prefVSyncCount;

	private static MelonPreferences_Entry<bool> prefSoftParticles;

	private static MelonPreferences_Entry<bool> prefRealtimeReflectionProbes;

	private static MelonPreferences_Entry<float> prefSolverIterations;

	private static MelonPreferences_Entry<float> prefSolverVelocityIterations;

	private static MelonPreferences_Entry<bool> prefEnableBoneFps;

	private static MelonPreferences_Entry<float> prefBoneFpsNear;

	private static MelonPreferences_Entry<float> prefBoneFpsMid;

	private static MelonPreferences_Entry<float> prefBoneFpsFar;

	private static MelonPreferences_Entry<bool> prefAutoSave;

	private static MelonPreferences_Entry<float> prefTargetFrameRate;

	private static MelonPreferences_Entry<bool> prefEnableAdaptiveTick;

	private static MelonPreferences_Entry<float> prefAdaptiveMaxIntervalMultiplier;

	private static MelonPreferences_Entry<bool> prefEnableDynamicCullScaling;

	private static MelonPreferences_Entry<float> prefDynamicCullMinFactor;

	public override void OnInitializeMelon()
	{
		MelonLogger.Msg("VOIDWARD 1.0.0 loaded and ready.");
		prefCategory = MelonPreferences.CreateCategory("VOIDWARD");
		prefLowFps = prefCategory.CreateEntry<float>("LowFPS", 50f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefHighFps = prefCategory.CreateEntry<float>("HighFPS", 75f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefCheckInterval = prefCategory.CreateEntry<float>("CheckInterval", 3f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefCullDistance = prefCategory.CreateEntry<float>("CullDistance", 40f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefCacheInterval = prefCategory.CreateEntry<float>("CacheInterval", 5f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefShadowDistance = prefCategory.CreateEntry<float>("ShadowDistance", 15f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefAntiAliasing = prefCategory.CreateEntry<int>("AntiAliasing", 0, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefLodBias = prefCategory.CreateEntry<float>("LodBias", 0.5f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefEnableAuto = prefCategory.CreateEntry<bool>("EnableAutoOptimization", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefEnableCulling = prefCategory.CreateEntry<bool>("EnableDistanceCulling", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefShowFps = prefCategory.CreateEntry<bool>("ShowFpsOverlay", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefPixelLightCount = prefCategory.CreateEntry<float>("PixelLightCount", 0f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefShadowCascades = prefCategory.CreateEntry<float>("ShadowCascades", 0f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefShadowQuality = prefCategory.CreateEntry<int>("ShadowQuality", 1, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefShadowResolution = prefCategory.CreateEntry<int>("ShadowResolution", 0, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefAniso = prefCategory.CreateEntry<int>("AnisotropicFiltering", 0, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefTextureLimit = prefCategory.CreateEntry<float>("TextureLimit", 1f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefCloseQualityGuard = prefCategory.CreateEntry<bool>("EnableCloseQualityGuard", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefMinCloseLodBias = prefCategory.CreateEntry<float>("MinCloseLodBias", 1f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefMaxCloseTextureLimit = prefCategory.CreateEntry<float>("MaxCloseTextureLimit", 1f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefVSyncCount = prefCategory.CreateEntry<float>("VSyncCount", 0f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefSoftParticles = prefCategory.CreateEntry<bool>("SoftParticles", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefRealtimeReflectionProbes = prefCategory.CreateEntry<bool>("RealtimeReflectionProbes", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefSolverIterations = prefCategory.CreateEntry<float>("SolverIterations", 4f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefSolverVelocityIterations = prefCategory.CreateEntry<float>("SolverVelocityIterations", 1f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefEnableBoneFps = prefCategory.CreateEntry<bool>("EnableBoneFPSIntegration", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefBoneFpsNear = prefCategory.CreateEntry<float>("BoneFPSNearDist", 15f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefBoneFpsMid = prefCategory.CreateEntry<float>("BoneFPSMidDist", 35f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefBoneFpsFar = prefCategory.CreateEntry<float>("BoneFPSFarDist", 60f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefAutoSave = prefCategory.CreateEntry<bool>("AutoSaveSettings", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefTargetFrameRate = prefCategory.CreateEntry<float>("TargetFrameRate", 0f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefEnableAdaptiveTick = prefCategory.CreateEntry<bool>("EnableAdaptiveTickOptimizer", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefAdaptiveMaxIntervalMultiplier = prefCategory.CreateEntry<float>("AdaptiveMaxIntervalMultiplier", 2f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefEnableDynamicCullScaling = prefCategory.CreateEntry<bool>("EnableDynamicDistanceScaling", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		prefDynamicCullMinFactor = prefCategory.CreateEntry<float>("DynamicCullMinFactor", 0.45f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		showFps = prefShowFps.Value;
		runtimeCheckInterval = Mathf.Max(0.1f, prefCheckInterval.Value);
		runtimeCacheInterval = Mathf.Max(0.1f, prefCacheInterval.Value);
		runtimeCullDistance = Mathf.Max(5f, prefCullDistance.Value);
		ApplyFrameRateCap();
		SavePreferences(verbose: false);
		hostStabilityGuard = new FusionHostStabilityGuard();
		hostStabilityGuard.Initialize(prefCategory);
		Hooking.OnUIRigCreated += SetupBoneMenu;
	}

	public override void OnSceneWasLoaded(int buildIndex, string sceneName)
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: 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)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		originalShadowDistance = QualitySettings.shadowDistance;
		originalAntiAliasing = QualitySettings.antiAliasing;
		originalLodBias = QualitySettings.lodBias;
		originalPixelLightCount = QualitySettings.pixelLightCount;
		originalShadowCascades = QualitySettings.shadowCascades;
		originalShadowQuality = QualitySettings.shadows;
		originalShadowResolution = QualitySettings.shadowResolution;
		originalAniso = QualitySettings.anisotropicFiltering;
		originalTextureLimit = QualitySettings.masterTextureLimit;
		originalVSyncCount = QualitySettings.vSyncCount;
		originalSoftParticles = QualitySettings.softParticles;
		originalRealtimeReflectionProbes = QualitySettings.realtimeReflectionProbes;
		originalSolverIterations = Physics.defaultSolverIterations;
		originalSolverVelocityIterations = Physics.defaultSolverVelocityIterations;
		isOptimizedMode = false;
		cacheTimer = 0f;
		runtimeCheckInterval = Mathf.Max(0.1f, prefCheckInterval.Value);
		runtimeCacheInterval = Mathf.Max(0.1f, prefCacheInterval.Value);
		runtimeCullDistance = Mathf.Max(5f, prefCullDistance.Value);
		cachedBodies = Array.Empty<Rigidbody>();
		ApplyBoneFpsSettings();
	}

	public override void OnUpdate()
	{
		deltaTime += (Time.unscaledDeltaTime - deltaTime) * 0.1f;
		float num = (lastFps = 1f / deltaTime);
		hostStabilityGuard?.Tick(Time.unscaledDeltaTime);
		UpdateAdaptiveRuntime(num);
		checkTimer += Time.deltaTime;
		cacheTimer += Time.deltaTime;
		if (cacheTimer >= runtimeCacheInterval)
		{
			cacheTimer = 0f;
			RefreshBodyCache();
		}
		RigManager rigManager = Player.RigManager;
		PhysicsRig val = (((Object)(object)rigManager != (Object)null) ? rigManager.physicsRig : null);
		if ((Object)(object)val != (Object)null && checkTimer > runtimeCheckInterval)
		{
			checkTimer = 0f;
			if (prefEnableAuto.Value && num < prefLowFps.Value && !isOptimizedMode)
			{
				EnableOptimization();
			}
			else if (prefEnableAuto.Value && num > prefHighFps.Value && isOptimizedMode)
			{
				DisableOptimization();
			}
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			if (prefEnableCulling.Value)
			{
				DistanceCulling(val);
			}
			ApplyBoneFpsSettings();
		}
		if (settingsDirty && prefAutoSave.Value)
		{
			settingsSaveTimer += Time.unscaledDeltaTime;
			if (settingsSaveTimer >= 0.75f)
			{
				SavePreferences(verbose: false);
			}
		}
	}

	private void EnableOptimization()
	{
		MelonLogger.Msg("[VOIDWARD] FPS dropped! Enabling automatic graphics optimization...");
		originalShadowDistance = QualitySettings.shadowDistance;
		originalAntiAliasing = QualitySettings.antiAliasing;
		originalLodBias = QualitySettings.lodBias;
		ApplyOptimizationSettings();
		isOptimizedMode = true;
	}

	private void DisableOptimization()
	{
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		MelonLogger.Msg("[VOIDWARD] FPS is stable. Restoring original graphics settings...");
		QualitySettings.shadowDistance = originalShadowDistance;
		QualitySettings.antiAliasing = originalAntiAliasing;
		QualitySettings.lodBias = originalLodBias;
		QualitySettings.pixelLightCount = originalPixelLightCount;
		QualitySettings.shadowCascades = originalShadowCascades;
		QualitySettings.shadows = originalShadowQuality;
		QualitySettings.shadowResolution = originalShadowResolution;
		QualitySettings.anisotropicFiltering = originalAniso;
		QualitySettings.masterTextureLimit = originalTextureLimit;
		QualitySettings.vSyncCount = originalVSyncCount;
		QualitySettings.softParticles = originalSoftParticles;
		QualitySettings.realtimeReflectionProbes = originalRealtimeReflectionProbes;
		Physics.defaultSolverIterations = originalSolverIterations;
		Physics.defaultSolverVelocityIterations = originalSolverVelocityIterations;
		isOptimizedMode = false;
	}

	private void ApplyOptimizationSettings()
	{
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		QualitySettings.shadowDistance = prefShadowDistance.Value;
		QualitySettings.antiAliasing = prefAntiAliasing.Value;
		float num = prefLodBias.Value;
		QualitySettings.pixelLightCount = ClampInt((int)prefPixelLightCount.Value, 0, 8);
		QualitySettings.shadowCascades = ClampInt((int)prefShadowCascades.Value, 0, 4);
		QualitySettings.shadows = ToShadowQuality(prefShadowQuality.Value);
		QualitySettings.shadowResolution = ToShadowResolution(prefShadowResolution.Value);
		QualitySettings.anisotropicFiltering = ToAniso(prefAniso.Value);
		int num2 = ClampInt((int)prefTextureLimit.Value, 0, 3);
		if (prefCloseQualityGuard.Value)
		{
			num = Mathf.Max(num, Mathf.Clamp(prefMinCloseLodBias.Value, 0.5f, 2.5f));
			num2 = Mathf.Min(num2, ClampInt((int)prefMaxCloseTextureLimit.Value, 0, 3));
		}
		QualitySettings.lodBias = num;
		QualitySettings.masterTextureLimit = num2;
		QualitySettings.vSyncCount = ClampInt((int)prefVSyncCount.Value, 0, 2);
		QualitySettings.softParticles = prefSoftParticles.Value;
		QualitySettings.realtimeReflectionProbes = prefRealtimeReflectionProbes.Value;
		Physics.defaultSolverIterations = ClampInt((int)prefSolverIterations.Value, 1, 20);
		Physics.defaultSolverVelocityIterations = ClampInt((int)prefSolverVelocityIterations.Value, 1, 20);
	}

	private void RefreshBodyCache()
	{
		cachedBodies = Il2CppArrayBase<Rigidbody>.op_Implicit(Object.FindObjectsOfType<Rigidbody>());
	}

	private void DistanceCulling(PhysicsRig physicsRig)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		Vector3 position = ((Rig)physicsRig).m_pelvis.position;
		if (cachedBodies == null || cachedBodies.Length == 0)
		{
			RefreshBodyCache();
		}
		int num = 0;
		Rigidbody[] array = cachedBodies;
		foreach (Rigidbody val in array)
		{
			if (!((Object)(object)val == (Object)null) && !((Component)val).gameObject.transform.IsChildOf(((Component)physicsRig).transform))
			{
				float num2 = Vector3.Distance(position, val.position);
				if (num2 > runtimeCullDistance && isOptimizedMode && !val.IsSleeping())
				{
					val.Sleep();
					num++;
				}
			}
		}
		lastCulledCount = num;
		if (num > 0)
		{
			MelonLogger.Msg($"[VOIDWARD] Put {num} distant physics objects to sleep to preserve FPS.");
		}
	}

	private void SetupBoneMenu()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_0170: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_021b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0260: Unknown result type (might be due to invalid IL or missing references)
		//IL_0296: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0329: Unknown result type (might be due to invalid IL or missing references)
		//IL_0351: Unknown result type (might be due to invalid IL or missing references)
		//IL_037d: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_042e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0469: Unknown result type (might be due to invalid IL or missing references)
		//IL_04a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_04d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_050b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0546: Unknown result type (might be due to invalid IL or missing references)
		//IL_0581: Unknown result type (might be due to invalid IL or missing references)
		//IL_05bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_05cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_05f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0601: Unknown result type (might be due to invalid IL or missing references)
		//IL_0628: Unknown result type (might be due to invalid IL or missing references)
		//IL_0663: Unknown result type (might be due to invalid IL or missing references)
		//IL_0672: Unknown result type (might be due to invalid IL or missing references)
		//IL_0699: Unknown result type (might be due to invalid IL or missing references)
		//IL_06d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0700: Unknown result type (might be due to invalid IL or missing references)
		//IL_073b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0776: Unknown result type (might be due to invalid IL or missing references)
		//IL_07b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_07dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0809: Unknown result type (might be due to invalid IL or missing references)
		//IL_0844: Unknown result type (might be due to invalid IL or missing references)
		//IL_087f: Unknown result type (might be due to invalid IL or missing references)
		//IL_08ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_08f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0930: Unknown result type (might be due to invalid IL or missing references)
		if (menuInitialized)
		{
			return;
		}
		menuInitialized = true;
		menuPage = Page.Root.CreatePage("VOIDWARD", Color.white, 64, true);
		hostStabilityGuard?.AttachMenu(menuPage);
		menuPage.CreateFunction("Save settings now", Color.white, (Action)delegate
		{
			SavePreferences(verbose: true);
		});
		menuPage.CreateFunction("Apply optimization now", Color.yellow, (Action)delegate
		{
			if (!isOptimizedMode)
			{
				EnableOptimization();
			}
			else
			{
				ApplyOptimizationSettings();
			}
			ApplyFrameRateCap();
			RefreshBodyCache();
			MelonLogger.Msg("[VOIDWARD] Optimization profile applied.");
		});
		menuPage.CreateFunction("Restore quality now", Color.cyan, (Action)delegate
		{
			DisableOptimization();
			WakeAllCachedBodies();
			ApplyFrameRateCap();
			MelonLogger.Msg("[VOIDWARD] Original quality restored.");
		});
		Page val = menuPage.CreatePage("Presets", new Color(0.9f, 0.9f, 1f), 16, true);
		Page val2 = menuPage.CreatePage("Adaptive", new Color(0.85f, 1f, 0.9f), 20, true);
		val.CreateFunction("Potato", new Color(0.85f, 0.35f, 0.35f), (Action)delegate
		{
			ApplyPreset("potato");
		});
		val.CreateFunction("Low", new Color(1f, 0.6f, 0.2f), (Action)delegate
		{
			ApplyPreset("low");
		});
		val.CreateFunction("Balanced", new Color(0.95f, 0.85f, 0.2f), (Action)delegate
		{
			ApplyPreset("balanced");
		});
		val.CreateFunction("High", Color.white, (Action)delegate
		{
			ApplyPreset("high");
		});
		val.CreateFunction("Ultra", new Color(0.45f, 0.75f, 1f), (Action)delegate
		{
			ApplyPreset("ultra");
		});
		val2.CreateBool("Adaptive tick optimizer", new Color(0.8f, 1f, 0.8f), prefEnableAdaptiveTick.Value, (Action<bool>)delegate(bool value)
		{
			prefEnableAdaptiveTick.Value = value;
			MarkSettingsDirty();
		});
		val2.CreateFloat("Max interval multiplier", new Color(0.8f, 1f, 0.8f), prefAdaptiveMaxIntervalMultiplier.Value, 0.1f, 1.1f, 4f, (Action<float>)delegate(float value)
		{
			prefAdaptiveMaxIntervalMultiplier.Value = value;
			MarkSettingsDirty();
		});
		val2.CreateBool("Dynamic distance scaling", new Color(0.8f, 1f, 0.8f), prefEnableDynamicCullScaling.Value, (Action<bool>)delegate(bool value)
		{
			prefEnableDynamicCullScaling.Value = value;
			MarkSettingsDirty();
		});
		val2.CreateFloat("Min cull distance factor", new Color(0.8f, 1f, 0.8f), prefDynamicCullMinFactor.Value, 0.05f, 0.25f, 1f, (Action<float>)delegate(float value)
		{
			prefDynamicCullMinFactor.Value = value;
			MarkSettingsDirty();
		});
		menuPage.CreateBool("Auto optimization", Color.white, prefEnableAuto.Value, (Action<bool>)delegate(bool value)
		{
			prefEnableAuto.Value = value;
			MarkSettingsDirty();
		});
		menuPage.CreateBool("Distance culling", Color.white, prefEnableCulling.Value, (Action<bool>)delegate(bool value)
		{
			prefEnableCulling.Value = value;
			MarkSettingsDirty();
		});
		menuPage.CreateBool("Show FPS overlay", Color.white, showFps, (Action<bool>)delegate(bool value)
		{
			showFps = value;
			prefShowFps.Value = value;
			MarkSettingsDirty();
		});
		menuPage.CreateBool("Auto save settings", Color.white, prefAutoSave.Value, (Action<bool>)delegate(bool value)
		{
			prefAutoSave.Value = value;
			MarkSettingsDirty();
			SavePreferences(verbose: true);
		});
		menuPage.CreateFloat("Low FPS threshold", Color.white, prefLowFps.Value, 1f, 10f, 120f, (Action<float>)delegate(float value)
		{
			prefLowFps.Value = value;
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("High FPS threshold", Color.white, prefHighFps.Value, 1f, 10f, 144f, (Action<float>)delegate(float value)
		{
			if (value < prefLowFps.Value + 5f)
			{
				value = prefLowFps.Value + 5f;
			}
			prefHighFps.Value = value;
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("Check interval (s)", Color.white, prefCheckInterval.Value, 0.5f, 1f, 10f, (Action<float>)delegate(float value)
		{
			prefCheckInterval.Value = value;
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("Cull distance (m)", Color.white, prefCullDistance.Value, 1f, 10f, 100f, (Action<float>)delegate(float value)
		{
			prefCullDistance.Value = value;
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("Target FPS cap (0=off)", Color.white, prefTargetFrameRate.Value, 1f, 0f, 240f, (Action<float>)delegate(float value)
		{
			prefTargetFrameRate.Value = value;
			ApplyFrameRateCap();
			MarkSettingsDirty();
		});
		menuPage.CreateBool("Enable BoneFPS", Color.white, prefEnableBoneFps.Value, (Action<bool>)delegate(bool value)
		{
			prefEnableBoneFps.Value = value;
			ApplyBoneFpsSettings();
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("BoneFPS Near dist", Color.white, prefBoneFpsNear.Value, 1f, 5f, 80f, (Action<float>)delegate(float value)
		{
			prefBoneFpsNear.Value = value;
			ApplyBoneFpsSettings();
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("BoneFPS Mid dist", Color.white, prefBoneFpsMid.Value, 1f, 10f, 120f, (Action<float>)delegate(float value)
		{
			prefBoneFpsMid.Value = value;
			ApplyBoneFpsSettings();
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("BoneFPS Far dist", Color.white, prefBoneFpsFar.Value, 1f, 20f, 200f, (Action<float>)delegate(float value)
		{
			prefBoneFpsFar.Value = value;
			ApplyBoneFpsSettings();
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("Pixel lights", Color.white, prefPixelLightCount.Value, 1f, 0f, 8f, (Action<float>)delegate(float value)
		{
			prefPixelLightCount.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateEnum("Shadow quality", Color.white, (Enum)(object)ToShadowQuality(prefShadowQuality.Value), (Action<Enum>)delegate(Enum value)
		{
			prefShadowQuality.Value = Convert.ToInt32(value);
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateEnum("Shadow resolution", Color.white, (Enum)(object)ToShadowResolution(prefShadowResolution.Value), (Action<Enum>)delegate(Enum value)
		{
			prefShadowResolution.Value = Convert.ToInt32(value);
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("Shadow cascades", Color.white, prefShadowCascades.Value, 1f, 0f, 4f, (Action<float>)delegate(float value)
		{
			prefShadowCascades.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateEnum("Anisotropic filtering", Color.white, (Enum)(object)ToAniso(prefAniso.Value), (Action<Enum>)delegate(Enum value)
		{
			prefAniso.Value = Convert.ToInt32(value);
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("Texture limit", Color.white, prefTextureLimit.Value, 1f, 0f, 3f, (Action<float>)delegate(float value)
		{
			prefTextureLimit.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateBool("Close quality guard", Color.white, prefCloseQualityGuard.Value, (Action<bool>)delegate(bool value)
		{
			prefCloseQualityGuard.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("Min close LOD bias", Color.white, prefMinCloseLodBias.Value, 0.1f, 0.5f, 2.5f, (Action<float>)delegate(float value)
		{
			prefMinCloseLodBias.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("Max close texture limit", Color.white, prefMaxCloseTextureLimit.Value, 1f, 0f, 3f, (Action<float>)delegate(float value)
		{
			prefMaxCloseTextureLimit.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("VSync count", Color.white, prefVSyncCount.Value, 1f, 0f, 2f, (Action<float>)delegate(float value)
		{
			prefVSyncCount.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateBool("Soft particles", Color.white, prefSoftParticles.Value, (Action<bool>)delegate(bool value)
		{
			prefSoftParticles.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateBool("Realtime reflection probes", Color.white, prefRealtimeReflectionProbes.Value, (Action<bool>)delegate(bool value)
		{
			prefRealtimeReflectionProbes.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("Solver iterations", Color.white, prefSolverIterations.Value, 1f, 1f, 20f, (Action<float>)delegate(float value)
		{
			prefSolverIterations.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("Solver velocity iters", Color.white, prefSolverVelocityIterations.Value, 1f, 1f, 20f, (Action<float>)delegate(float value)
		{
			prefSolverVelocityIterations.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("Cache interval (s)", Color.white, prefCacheInterval.Value, 0.5f, 1f, 15f, (Action<float>)delegate(float value)
		{
			prefCacheInterval.Value = value;
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("Shadow distance", Color.white, prefShadowDistance.Value, 1f, 5f, 50f, (Action<float>)delegate(float value)
		{
			prefShadowDistance.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateFloat("LOD bias", Color.white, prefLodBias.Value, 0.1f, 0.2f, 2f, (Action<float>)delegate(float value)
		{
			prefLodBias.Value = value;
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
		menuPage.CreateEnum("Anti-aliasing", Color.white, (Enum)ToAaLevel(prefAntiAliasing.Value), (Action<Enum>)delegate(Enum value)
		{
			prefAntiAliasing.Value = Convert.ToInt32(value);
			if (isOptimizedMode)
			{
				ApplyOptimizationSettings();
			}
			MarkSettingsDirty();
		});
	}

	private void ApplyPreset(string presetName)
	{
		string text = (presetName ?? string.Empty).Trim().ToLowerInvariant();
		switch (text)
		{
		case "potato":
			prefShadowDistance.Value = 8f;
			prefAntiAliasing.Value = 0;
			prefLodBias.Value = 0.35f;
			prefPixelLightCount.Value = 0f;
			prefShadowCascades.Value = 0f;
			prefShadowQuality.Value = 0;
			prefShadowResolution.Value = 0;
			prefAniso.Value = 0;
			prefTextureLimit.Value = 3f;
			prefVSyncCount.Value = 0f;
			prefSoftParticles.Value = false;
			prefRealtimeReflectionProbes.Value = false;
			prefSolverIterations.Value = 2f;
			prefSolverVelocityIterations.Value = 1f;
			prefCullDistance.Value = 28f;
			prefTargetFrameRate.Value = 72f;
			break;
		case "low":
			prefShadowDistance.Value = 12f;
			prefAntiAliasing.Value = 0;
			prefLodBias.Value = 0.45f;
			prefPixelLightCount.Value = 0f;
			prefShadowCascades.Value = 0f;
			prefShadowQuality.Value = 1;
			prefShadowResolution.Value = 0;
			prefAniso.Value = 0;
			prefTextureLimit.Value = 2f;
			prefVSyncCount.Value = 0f;
			prefSoftParticles.Value = false;
			prefRealtimeReflectionProbes.Value = false;
			prefSolverIterations.Value = 3f;
			prefSolverVelocityIterations.Value = 1f;
			prefCullDistance.Value = 35f;
			prefTargetFrameRate.Value = 90f;
			break;
		case "balanced":
			prefShadowDistance.Value = 16f;
			prefAntiAliasing.Value = 2;
			prefLodBias.Value = 0.65f;
			prefPixelLightCount.Value = 1f;
			prefShadowCascades.Value = 1f;
			prefShadowQuality.Value = 2;
			prefShadowResolution.Value = 1;
			prefAniso.Value = 1;
			prefTextureLimit.Value = 1f;
			prefVSyncCount.Value = 0f;
			prefSoftParticles.Value = true;
			prefRealtimeReflectionProbes.Value = false;
			prefSolverIterations.Value = 4f;
			prefSolverVelocityIterations.Value = 2f;
			prefCullDistance.Value = 45f;
			prefTargetFrameRate.Value = 120f;
			break;
		case "high":
			prefShadowDistance.Value = 24f;
			prefAntiAliasing.Value = 4;
			prefLodBias.Value = 0.95f;
			prefPixelLightCount.Value = 2f;
			prefShadowCascades.Value = 2f;
			prefShadowQuality.Value = 2;
			prefShadowResolution.Value = 2;
			prefAniso.Value = 2;
			prefTextureLimit.Value = 0f;
			prefVSyncCount.Value = 0f;
			prefSoftParticles.Value = true;
			prefRealtimeReflectionProbes.Value = true;
			prefSolverIterations.Value = 6f;
			prefSolverVelocityIterations.Value = 3f;
			prefCullDistance.Value = 60f;
			prefTargetFrameRate.Value = 0f;
			break;
		case "ultra":
			prefShadowDistance.Value = 32f;
			prefAntiAliasing.Value = 8;
			prefLodBias.Value = 1.2f;
			prefPixelLightCount.Value = 4f;
			prefShadowCascades.Value = 4f;
			prefShadowQuality.Value = 2;
			prefShadowResolution.Value = 3;
			prefAniso.Value = 2;
			prefTextureLimit.Value = 0f;
			prefVSyncCount.Value = 0f;
			prefSoftParticles.Value = true;
			prefRealtimeReflectionProbes.Value = true;
			prefSolverIterations.Value = 8f;
			prefSolverVelocityIterations.Value = 4f;
			prefCullDistance.Value = 75f;
			prefTargetFrameRate.Value = 0f;
			break;
		default:
			MelonLogger.Warning("[VOIDWARD] Unknown preset '" + presetName + "'.");
			return;
		}
		if (!isOptimizedMode)
		{
			EnableOptimization();
		}
		else
		{
			ApplyOptimizationSettings();
		}
		ApplyFrameRateCap();
		ApplyBoneFpsSettings();
		RefreshBodyCache();
		MarkSettingsDirty();
		SavePreferences(verbose: true);
		MelonLogger.Msg("[VOIDWARD] Preset applied: " + text);
	}

	private void UpdateAdaptiveRuntime(float fps)
	{
		int num = Mathf.Max(1, PlayerIDManager.PlayerCount);
		float num2 = Mathf.Clamp01(((float)num - 1f) / 12f);
		float num3 = 0f;
		float num4 = Mathf.Max(5f, prefLowFps.Value);
		float num5 = Mathf.Max(num4 + 1f, prefHighFps.Value);
		if (fps <= num4)
		{
			num3 = 1f;
		}
		else if (fps < num5)
		{
			num3 = Mathf.InverseLerp(num5, num4, fps);
		}
		float num6 = Mathf.Clamp01(num3 * 0.75f + num2 * 0.25f);
		float num7 = Mathf.Max(1.1f, prefAdaptiveMaxIntervalMultiplier.Value);
		if (prefEnableAdaptiveTick.Value)
		{
			float num8 = Mathf.Lerp(0.9f, num7, num6);
			float num9 = Mathf.Lerp(0.9f, num7 + 0.35f, num6);
			runtimeCheckInterval = Mathf.Clamp(prefCheckInterval.Value * num8, 0.2f, 30f);
			runtimeCacheInterval = Mathf.Clamp(prefCacheInterval.Value * num9, 0.2f, 45f);
		}
		else
		{
			runtimeCheckInterval = Mathf.Max(0.1f, prefCheckInterval.Value);
			runtimeCacheInterval = Mathf.Max(0.1f, prefCacheInterval.Value);
		}
		if (prefEnableDynamicCullScaling.Value)
		{
			float num10 = Mathf.Clamp(prefDynamicCullMinFactor.Value, 0.25f, 1f);
			float num11 = Mathf.Lerp(1f, num10, num6);
			float num12 = Mathf.Max(5f, prefCullDistance.Value * num11);
			runtimeCullDistance = Mathf.Lerp(runtimeCullDistance, num12, Time.unscaledDeltaTime * 4f);
		}
		else
		{
			runtimeCullDistance = Mathf.Max(5f, prefCullDistance.Value);
		}
	}

	private void MarkSettingsDirty()
	{
		settingsDirty = true;
		settingsSaveTimer = 0f;
	}

	private void SavePreferences(bool verbose)
	{
		settingsDirty = false;
		settingsSaveTimer = 0f;
		MelonPreferences.Save();
		if (verbose)
		{
			MelonLogger.Msg("[VOIDWARD] Settings saved.");
		}
	}

	private void ApplyFrameRateCap()
	{
		int num = ClampInt((int)prefTargetFrameRate.Value, 0, 240);
		if (num > 0 && QualitySettings.vSyncCount > 0)
		{
			QualitySettings.vSyncCount = 0;
		}
		Application.targetFrameRate = ((num <= 0) ? (-1) : num);
	}

	private void WakeAllCachedBodies()
	{
		if (cachedBodies == null || cachedBodies.Length == 0)
		{
			return;
		}
		Rigidbody[] array = cachedBodies;
		foreach (Rigidbody val in array)
		{
			if (!((Object)(object)val == (Object)null) && val.IsSleeping())
			{
				val.WakeUp();
			}
		}
	}

	private static AaLevel ToAaLevel(int value)
	{
		return value switch
		{
			2 => AaLevel.X2, 
			4 => AaLevel.X4, 
			8 => AaLevel.X8, 
			_ => AaLevel.Off, 
		};
	}

	private static int ClampInt(int value, int min, int max)
	{
		if (value < min)
		{
			return min;
		}
		if (value > max)
		{
			return max;
		}
		return value;
	}

	private static ShadowQuality ToShadowQuality(int value)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		return (ShadowQuality)ClampInt(value, 0, 2);
	}

	private static ShadowResolution ToShadowResolution(int value)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		return (ShadowResolution)ClampInt(value, 0, 3);
	}

	private static AnisotropicFiltering ToAniso(int value)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		return (AnisotropicFiltering)ClampInt(value, 0, 2);
	}

	private void ApplyBoneFpsSettings()
	{
		if (prefEnableBoneFps.Value && EnsureBoneFpsType())
		{
			TrySetBoneFpsValue(new string[4] { "NearDist", "_nearDist", "nearDist", "NearDistance" }, prefBoneFpsNear.Value);
			TrySetBoneFpsValue(new string[4] { "MidDist", "_midDist", "midDist", "MidDistance" }, prefBoneFpsMid.Value);
			TrySetBoneFpsValue(new string[4] { "FarDist", "_farDist", "farDist", "FarDistance" }, prefBoneFpsFar.Value);
			TryInvokeBoneFps(new string[4] { "ApplyAllOptimizations", "ApplyAllRenderOptimizations", "ApplyAll", "ApplyBasicSettings" });
		}
	}

	private bool EnsureBoneFpsType()
	{
		if (boneFpsChecked)
		{
			return boneFpsType != null;
		}
		boneFpsChecked = true;
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			if (!(assembly.GetName().Name == "BoneFPS"))
			{
				continue;
			}
			boneFpsType = assembly.GetType("BoneFPS.BoneFPSMod");
			if (!(boneFpsType == null))
			{
				break;
			}
			Type[] types = assembly.GetTypes();
			foreach (Type type in types)
			{
				if (type.FullName != null && type.FullName.EndsWith("BoneFPSMod", StringComparison.Ordinal))
				{
					boneFpsType = type;
					break;
				}
			}
			break;
		}
		if (boneFpsType != null)
		{
			BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
			boneFpsInstance = boneFpsType.GetField("_mod", bindingAttr)?.GetValue(null) ?? boneFpsType.GetProperty("Instance", bindingAttr)?.GetValue(null);
		}
		return boneFpsType != null;
	}

	private void TrySetBoneFpsValue(string[] names, float value)
	{
		if (boneFpsType == null)
		{
			return;
		}
		BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
		object obj = boneFpsInstance;
		foreach (string name in names)
		{
			FieldInfo field = boneFpsType.GetField(name, bindingAttr);
			if (field != null)
			{
				object obj2 = (field.IsStatic ? null : obj);
				if (field.FieldType == typeof(float))
				{
					field.SetValue(obj2, value);
					break;
				}
				if (field.FieldType == typeof(int))
				{
					field.SetValue(obj2, (int)value);
					break;
				}
			}
			PropertyInfo property = boneFpsType.GetProperty(name, bindingAttr);
			if (property != null && property.CanWrite)
			{
				MethodInfo? getMethod = property.GetGetMethod(nonPublic: true);
				object obj3 = (((object)getMethod != null && getMethod.IsStatic) ? null : obj);
				if (property.PropertyType == typeof(float))
				{
					property.SetValue(obj3, value, null);
					break;
				}
				if (property.PropertyType == typeof(int))
				{
					property.SetValue(obj3, (int)value, null);
					break;
				}
			}
		}
	}

	private void TryInvokeBoneFps(string[] methodNames)
	{
		if (boneFpsType == null)
		{
			return;
		}
		BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
		object obj = boneFpsInstance;
		for (int i = 0; i < methodNames.Length; i++)
		{
			MethodInfo method = boneFpsType.GetMethod(methodNames[i], bindingAttr, null, Type.EmptyTypes, null);
			if (method != null)
			{
				method.Invoke(method.IsStatic ? null : obj, null);
				break;
			}
		}
	}

	public override void OnGUI()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		if (showFps)
		{
			GUI.Label(new Rect(10f, 10f, 320f, 90f), $"FPS: {lastFps:0.0}\nOptimized: {isOptimizedMode}\nCulled: {lastCulledCount}");
		}
	}

	public override void OnApplicationQuit()
	{
		if (settingsDirty || prefAutoSave.Value)
		{
			SavePreferences(verbose: false);
		}
		hostStabilityGuard?.Dispose();
		hostStabilityGuard = null;
	}
}