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;
}
}