using System; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("AutoLodBias")] [assembly: AssemblyDescription("Adaptive Valheim LOD bias controller")] [assembly: AssemblyCompany("LEGIOmods")] [assembly: AssemblyProduct("AutoLodBias")] [assembly: AssemblyCopyright("Copyright 2026 LEGIOmods")] [assembly: ComVisible(false)] [assembly: Guid("3605278b-2b13-42e3-903a-e49e21e598fa")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially or fully created with the assistance of Generative AI for code, documentation, and package preparation.")] [assembly: AssemblyMetadata("AI_Model_Vendor", "OpenAI")] [assembly: AssemblyMetadata("AI_Agent", "OpenAI Codex")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace AutoLodBias { [BepInPlugin("domen.valheim.autolodbias", "AutoLodBias", "1.0.0")] public sealed class AutoLodBiasPlugin : BaseUnityPlugin { public const string PluginGuid = "domen.valheim.autolodbias"; public const string PluginName = "AutoLodBias"; public const string PluginVersion = "1.0.0"; private ModConfig _config; private FpsLodController _controller; private PortalState _portal; private OverlayRenderer _overlay; private Harmony _harmony; internal static AutoLodBiasPlugin Instance { get; private set; } private void Awake() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown Instance = this; _config = new ModConfig(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); _controller = new FpsLodController(_config, ((BaseUnityPlugin)this).Logger); _portal = new PortalState(_config, _controller, ((BaseUnityPlugin)this).Logger); _overlay = new OverlayRenderer(_config); _controller.SetLod(_config.DefaultLodBias.Value, "startup"); _harmony = new Harmony("domen.valheim.autolodbias"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"AutoLodBias 1.0.0 loaded."); } private void Update() { HandleHotkeys(); if (_config.Enabled.Value) { float unscaledDeltaTime = Time.unscaledDeltaTime; if (_portal.Active) { _portal.Tick(); } else if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)ZNetScene.instance == (Object)null)) { _controller.Tick(unscaledDeltaTime); } } } private void OnGUI() { _overlay.Draw(_controller.SmoothedFps, _config.Enabled.Value, _portal.Phase); } private void HandleHotkeys() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) bool flag = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307); bool num = !_config.RequireAlt.Value || flag; if (num && Input.GetKeyDown(_config.ToggleOverlayKey.Value)) { _config.ShowOverlay.Value = !_config.ShowOverlay.Value; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Overlay " + (_config.ShowOverlay.Value ? "enabled." : "disabled."))); } if (num && Input.GetKeyDown(_config.ToggleModKey.Value)) { _config.Enabled.Value = !_config.Enabled.Value; _portal.Cancel(); _controller.Reset(); _controller.SetLod(_config.DefaultLodBias.Value, _config.Enabled.Value ? "mod enabled" : "mod disabled"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Automatic control " + (_config.Enabled.Value ? "enabled." : "disabled."))); } } internal void BeginPortal() { if (_config.Enabled.Value) { _portal.Begin(); } } private void OnDestroy() { _portal?.Cancel(); if (_config != null && _controller != null) { _controller.SetLod(_config.DefaultLodBias.Value, "unload"); } Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } Instance = null; } } internal sealed class ModConfig { internal readonly ConfigEntry Enabled; internal readonly ConfigEntry DefaultLodBias; internal readonly ConfigEntry MinimumLodBias; internal readonly ConfigEntry MaximumLodBias; internal readonly ConfigEntry LodStep; internal readonly ConfigEntry TargetFps; internal readonly ConfigEntry LowDuration; internal readonly ConfigEntry HighDuration; internal readonly ConfigEntry SevereDuration; internal readonly ConfigEntry CriticalDuration; internal readonly ConfigEntry DownAdjustmentCooldown; internal readonly ConfigEntry UpAdjustmentCooldown; internal readonly ConfigEntry PostEmergencyIncreaseLock; internal readonly ConfigEntry FastSmoothingSeconds; internal readonly ConfigEntry SlowSmoothingSeconds; internal readonly ConfigEntry PortalLodBias; internal readonly ConfigEntry PortalLowDuration; internal readonly ConfigEntry PortalRecoveryLodBias; internal readonly ConfigEntry PositionJumpThreshold; internal readonly ConfigEntry PostArrivalLowDuration; internal readonly ConfigEntry PortalTimeout; internal readonly ConfigEntry RecoveryTimeout; internal readonly ConfigEntry ShowOverlay; internal readonly ConfigEntry ShowTargetFps; internal readonly ConfigEntry ShowClock; internal readonly ConfigEntry Use24HourClock; internal readonly ConfigEntry RequireAlt; internal readonly ConfigEntry PositionX; internal readonly ConfigEntry PositionY; internal readonly ConfigEntry FontSize; internal readonly ConfigEntry RefreshInterval; internal readonly ConfigEntry ToggleOverlayKey; internal readonly ConfigEntry ToggleModKey; internal int ActiveTargetFps { get; private set; } internal int ActiveLowerThreshold { get; private set; } internal int ActiveUpperThreshold { get; private set; } internal int ActiveSevereThreshold { get; private set; } internal int ActiveCriticalThreshold { get; private set; } internal ModConfig(ConfigFile config, ManualLogSource log) { Enabled = config.Bind("General", "Enabled", true, "Enable automatic LOD control."); DefaultLodBias = config.Bind("General", "DefaultLodBias", 3f, "LOD bias used at startup and while disabled."); MinimumLodBias = config.Bind("General", "MinimumLodBias", 1f, "Minimum permitted LOD bias."); MaximumLodBias = config.Bind("General", "MaximumLodBias", 100f, "Maximum permitted LOD bias."); LodStep = config.Bind("General", "LodStep", 1f, "Legacy setting retained for configuration compatibility."); TargetFps = config.Bind("FPS", "TargetFPS", 0, "0 selects a profile from the display refresh rate; a positive value selects a manual target."); FastSmoothingSeconds = config.Bind("FPS", "FastSmoothingSeconds", 0.15f, "Smoothing time used for downward adjustments."); SlowSmoothingSeconds = config.Bind("FPS", "SlowSmoothingSeconds", 1f, "Smoothing time used for upward adjustments."); LowDuration = config.Bind("FPS", "LowDuration", 0.25f, "Time the fast FPS average must remain below the lower threshold."); HighDuration = config.Bind("FPS", "HighDuration", 1f, "Time the slow FPS average must remain above the upper threshold."); SevereDuration = config.Bind("FPS", "SevereDuration", 0.25f, "Time below the severe profile threshold before switching to LOD 3."); CriticalDuration = config.Bind("FPS", "CriticalDuration", 0.25f, "Time below the critical profile threshold before switching to LOD 1."); DownAdjustmentCooldown = config.Bind("FPS", "DownAdjustmentCooldown", 0.25f, "Cooldown after a normal downward adjustment."); UpAdjustmentCooldown = config.Bind("FPS", "UpAdjustmentCooldown", 1f, "Cooldown after an upward adjustment."); PostEmergencyIncreaseLock = config.Bind("FPS", "PostEmergencyIncreaseLock", 1f, "Time an increase remains blocked after a safety reduction."); PortalLodBias = config.Bind("Portal", "PortalLodBias", 1f, "LOD bias during portal travel."); PortalLowDuration = config.Bind("Portal", "PortalLowDuration", 2f, "Legacy setting retained for configuration compatibility."); PortalRecoveryLodBias = config.Bind("Portal", "PortalRecoveryLodBias", 3f, "LOD bias after portal arrival."); PositionJumpThreshold = config.Bind("Portal", "PositionJumpThreshold", 50f, "Minimum horizontal position jump that counts as portal arrival."); PostArrivalLowDuration = config.Bind("Portal", "PostArrivalLowDuration", 0.75f, "Time to keep the low LOD bias after a detected position jump."); PortalTimeout = config.Bind("Portal", "PortalTimeout", 8f, "Maximum time to wait for a position jump."); RecoveryTimeout = config.Bind("Portal", "RecoveryTimeout", 3f, "Hard timeout after a detected arrival."); ShowOverlay = config.Bind("Overlay", "ShowOverlay", true, "Show FPS and LOD overlay."); ShowTargetFps = config.Bind("Overlay", "ShowTargetFPS", true, "Show the active FPS profile next to the measured FPS."); PositionX = config.Bind("Overlay", "PositionX", 15, "Overlay X position."); PositionY = config.Bind("Overlay", "PositionY", 15, "Overlay Y position."); FontSize = config.Bind("Overlay", "FontSize", 16, "Overlay font size."); RefreshInterval = config.Bind("Overlay", "RefreshInterval", 0.25f, "Overlay refresh interval."); ShowClock = config.Bind("Clock", "ShowClock", true, "Show the local real-world time in the overlay."); Use24HourClock = config.Bind("Clock", "Use24HourClock", true, "Use 24-hour time instead of 12-hour time."); ToggleOverlayKey = config.Bind("Hotkeys", "ToggleOverlayKey", (KeyCode)282, "Overlay toggle key."); ToggleModKey = config.Bind("Hotkeys", "ToggleModKey", (KeyCode)283, "Automatic control toggle key."); RequireAlt = config.Bind("Hotkeys", "RequireAlt", true, "Require left or right Alt."); Validate(config, log); SelectRuntimeProfile(log); } private void Validate(ConfigFile config, ManualLogSource log) { bool flag = false; flag |= SetIfDifferent(MinimumLodBias, 1f); flag |= SetIfDifferent(MaximumLodBias, 100f); flag |= SetIfDifferent(LodStep, 1f); if (TargetFps.Value < 0) { TargetFps.Value = 0; flag = true; } flag |= EnsurePositive(FastSmoothingSeconds, 0.15f); flag |= EnsurePositive(SlowSmoothingSeconds, 1f); flag |= EnsureNonNegative(LowDuration, 0.25f); flag |= EnsureNonNegative(HighDuration, 1f); flag |= EnsureNonNegative(SevereDuration, 0.25f); flag |= EnsureNonNegative(CriticalDuration, 0.25f); flag |= EnsureNonNegative(DownAdjustmentCooldown, 0.25f); flag |= EnsureNonNegative(UpAdjustmentCooldown, 1f); if (flag | EnsureNonNegative(PostEmergencyIncreaseLock, 1f)) { config.Save(); log.LogWarning((object)"Invalid configuration values were corrected and saved."); } } private void SelectRuntimeProfile(ManualLogSource log) { float refreshRate; bool flag = TryGetDisplayRefreshRate(out refreshRate); bool flag2 = TargetFps.Value == 0; int target; if (flag2) { if (flag) { target = ((refreshRate <= 75f) ? 60 : ((!(refreshRate < 120f)) ? 120 : 90)); } else { target = 60; log.LogWarning((object)"Display refresh rate could not be detected; using the 60 FPS profile."); } } else { target = TargetFps.Value; } ApplyProfile(target, log); string text = (flag ? (refreshRate.ToString("0.##", CultureInfo.InvariantCulture) + " Hz") : "unknown"); log.LogInfo((object)("Display: " + text + " | Mode: " + (flag2 ? "Auto" : "Manual") + " | Selected target: " + ActiveTargetFps + " FPS")); } private void ApplyProfile(int target, ManualLogSource log) { ActiveTargetFps = target; switch (target) { case 30: SetThresholds(25, 29, 20, 15); return; case 60: SetThresholds(50, 57, 40, 30); return; case 90: SetThresholds(80, 100, 60, 40); return; case 120: SetThresholds(105, 125, 80, 55); return; } int num = Mathf.RoundToInt((float)target * 0.85f); int num2 = Mathf.RoundToInt((float)target * 0.97f); int num3 = Mathf.RoundToInt((float)target * 0.67f); int num4 = Mathf.RoundToInt((float)target * 0.5f); bool num5 = num4 >= num3 || num3 >= num || num >= num2; num4 = Mathf.Max(1, num4); num3 = Mathf.Max(num4 + 1, num3); num = Mathf.Max(num3 + 1, num); num2 = Mathf.Max(num + 1, num2); SetThresholds(num, num2, num3, num4); if (num5) { log.LogWarning((object)"Calculated FPS thresholds were corrected to preserve their required order."); } } private void SetThresholds(int lower, int upper, int severe, int critical) { ActiveLowerThreshold = lower; ActiveUpperThreshold = upper; ActiveSevereThreshold = severe; ActiveCriticalThreshold = critical; } private static bool TryGetDisplayRefreshRate(out float refreshRate) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) Resolution currentResolution = Screen.currentResolution; if (((Resolution)(ref currentResolution)).refreshRateRatio.denominator == 0) { refreshRate = 0f; return false; } refreshRate = (float)((Resolution)(ref currentResolution)).refreshRateRatio.numerator / (float)((Resolution)(ref currentResolution)).refreshRateRatio.denominator; if (!float.IsNaN(refreshRate) && !float.IsInfinity(refreshRate) && refreshRate >= 30f) { return refreshRate <= 500f; } return false; } private static bool SetIfDifferent(ConfigEntry entry, float value) { if (Mathf.Abs(entry.Value - value) < 0.001f) { return false; } entry.Value = value; return true; } private static bool EnsurePositive(ConfigEntry entry, float fallback) { if (!float.IsNaN(entry.Value) && !float.IsInfinity(entry.Value) && entry.Value > 0f) { return false; } entry.Value = fallback; return true; } private static bool EnsureNonNegative(ConfigEntry entry, float fallback) { if (!float.IsNaN(entry.Value) && !float.IsInfinity(entry.Value) && entry.Value >= 0f) { return false; } entry.Value = fallback; return true; } } internal sealed class FpsLodController { private static readonly float[] LodStages = new float[16] { 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 10f, 15f, 20f, 25f, 50f, 75f, 100f }; private readonly ModConfig _config; private readonly ManualLogSource _log; private float _fastFps; private float _slowFps; private float _lowTime; private float _highTime; private float _criticalTime; private float _severeTime; private float _downCooldown; private float _upCooldown; private float _increaseBlock; private bool _hasSample; private bool _criticalLatched; private bool _severeLatched; internal float SmoothedFps => _slowFps; internal FpsLodController(ModConfig config, ManualLogSource log) { _config = config; _log = log; } internal void Reset() { _fastFps = (_slowFps = 0f); _downCooldown = (_upCooldown = (_increaseBlock = 0f)); _hasSample = false; _criticalLatched = (_severeLatched = false); ResetTimers(); } internal void SetLod(float requested, string reason) { SetStage(NearestStage(requested), reason); } internal void Tick(float dt) { if (dt <= 0f) { return; } UpdateAverages(dt); UpdateCooldowns(dt); UpdateTimers(dt); float lodBias = QualitySettings.lodBias; if (_criticalTime >= _config.CriticalDuration.Value && !_criticalLatched) { _criticalLatched = true; _increaseBlock = _config.PostEmergencyIncreaseLock.Value; _lowTime = (_highTime = 0f); if (lodBias > 1f) { SetStage(1f, "critical FPS"); return; } } if (_severeTime >= _config.SevereDuration.Value && !_severeLatched) { _severeLatched = true; _increaseBlock = _config.PostEmergencyIncreaseLock.Value; _lowTime = (_highTime = 0f); if (lodBias > 3f) { SetStage(3f, "severe FPS"); return; } } if (_lowTime >= _config.LowDuration.Value && _downCooldown <= 0f) { SetStage(PreviousStage(lodBias), "low FPS"); _downCooldown = _config.DownAdjustmentCooldown.Value; ResetTimers(); } else if (_highTime >= _config.HighDuration.Value && _upCooldown <= 0f && _increaseBlock <= 0f) { SetStage(NextUpStage(lodBias), "high FPS"); _upCooldown = _config.UpAdjustmentCooldown.Value; ResetTimers(); } } private void UpdateAverages(float dt) { float num = 1f / dt; if (!_hasSample) { _fastFps = (_slowFps = num); _hasSample = true; return; } float num2 = 1f - Mathf.Exp((0f - dt) / _config.FastSmoothingSeconds.Value); float num3 = 1f - Mathf.Exp((0f - dt) / _config.SlowSmoothingSeconds.Value); _fastFps = Mathf.Lerp(_fastFps, num, num2); _slowFps = Mathf.Lerp(_slowFps, num, num3); } private void UpdateCooldowns(float dt) { _downCooldown = Mathf.Max(0f, _downCooldown - dt); _upCooldown = Mathf.Max(0f, _upCooldown - dt); _increaseBlock = Mathf.Max(0f, _increaseBlock - dt); } private void UpdateTimers(float dt) { _lowTime = ((_fastFps < (float)_config.ActiveLowerThreshold) ? (_lowTime + dt) : 0f); if (_fastFps < (float)_config.ActiveCriticalThreshold) { _criticalTime += dt; } else { _criticalTime = 0f; _criticalLatched = false; } if (_fastFps < (float)_config.ActiveSevereThreshold) { _severeTime += dt; } else { _severeTime = 0f; _severeLatched = false; } if (_upCooldown > 0f || _increaseBlock > 0f) { _highTime = 0f; } else { _highTime = ((_slowFps > (float)_config.ActiveUpperThreshold) ? (_highTime + dt) : 0f); } } private void SetStage(float stage, string reason) { float num = NearestStage(stage); if (!(Mathf.Abs(QualitySettings.lodBias - num) < 0.001f)) { QualitySettings.lodBias = num; _log.LogInfo((object)("LOD changed to " + num.ToString("0") + " (" + reason + ").")); } } private static float PreviousStage(float current) { for (int num = LodStages.Length - 1; num >= 0; num--) { if (LodStages[num] < current - 0.001f) { return LodStages[num]; } } return LodStages[0]; } private static float NextUpStage(float current) { if (!IsValidStage(current)) { for (int i = 0; i < LodStages.Length; i++) { if (LodStages[i] > current + 0.001f) { return LodStages[i]; } } return LodStages[LodStages.Length - 1]; } if (current < 10f) { float num = Mathf.Min(10f, current + 2f); for (int num2 = LodStages.Length - 1; num2 >= 0; num2--) { if (LodStages[num2] <= num + 0.001f) { return LodStages[num2]; } } } for (int j = 0; j < LodStages.Length; j++) { if (LodStages[j] > current + 0.001f) { return LodStages[j]; } } return LodStages[LodStages.Length - 1]; } private static bool IsValidStage(float value) { for (int i = 0; i < LodStages.Length; i++) { if (Mathf.Abs(value - LodStages[i]) < 0.001f) { return true; } } return false; } private static float NearestStage(float value) { float num = LodStages[0]; float num2 = Mathf.Abs(value - num); for (int i = 1; i < LodStages.Length; i++) { float num3 = Mathf.Abs(value - LodStages[i]); if (num3 < num2) { num = LodStages[i]; num2 = num3; } } return num; } private void ResetTimers() { _lowTime = (_highTime = (_criticalTime = (_severeTime = 0f))); } } internal enum PortalPhase { Inactive, WaitingForPositionJump, PostArrivalRecovery } internal sealed class PortalState { private const float MinimumDetectionDelay = 0.1f; private readonly ModConfig _config; private readonly FpsLodController _controller; private readonly ManualLogSource _log; private Vector3 _startPosition; private float _portalStartedAt; private float _arrivalAt; internal PortalPhase Phase { get; private set; } internal bool Active => Phase != PortalPhase.Inactive; internal PortalState(ModConfig config, FpsLodController controller, ManualLogSource log) { _config = config; _controller = controller; _log = log; } internal void Begin() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!Active && !((Object)(object)localPlayer == (Object)null) && !((Object)(object)((Component)localPlayer).gameObject == (Object)null)) { Cancel(); _startPosition = ((Component)localPlayer).transform.position; _portalStartedAt = Time.unscaledTime; Phase = PortalPhase.WaitingForPositionJump; _controller.SetLod(_config.PortalLodBias.Value, "portal started"); _log.LogInfo((object)"Portal start detected."); } } internal void Tick() { if (!Active) { return; } float unscaledTime = Time.unscaledTime; if (Phase == PortalPhase.WaitingForPositionJump) { DetectPositionJump(unscaledTime); if (Phase == PortalPhase.WaitingForPositionJump && unscaledTime - _portalStartedAt >= Mathf.Max(0.1f, _config.PortalTimeout.Value)) { Finish("Portal timeout.", warning: true); } return; } float num = unscaledTime - _arrivalAt; if (num >= Mathf.Max(0f, _config.RecoveryTimeout.Value)) { Finish("Recovery timeout.", warning: true); } else if (num >= Mathf.Max(0f, _config.PostArrivalLowDuration.Value)) { Finish("Portal recovery completed.", warning: false); } } internal void Cancel() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) Phase = PortalPhase.Inactive; _portalStartedAt = 0f; _arrivalAt = 0f; _startPosition = Vector3.zero; } private void DetectPositionJump(float now) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (now - _portalStartedAt < 0.1f) { return; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)((Component)localPlayer).gameObject == (Object)null)) { Vector3 position = ((Component)localPlayer).transform.position; float num = position.x - _startPosition.x; float num2 = position.z - _startPosition.z; float num3 = Mathf.Sqrt(num * num + num2 * num2); if (!(num3 <= Mathf.Max(1f, _config.PositionJumpThreshold.Value))) { Phase = PortalPhase.PostArrivalRecovery; _arrivalAt = now; _log.LogInfo((object)("Portal position jump detected: " + num3.ToString("0.0") + " m.")); } } } private void Finish(string message, bool warning) { _controller.SetLod(_config.PortalRecoveryLodBias.Value, message); Cancel(); _controller.Reset(); if (warning) { _log.LogWarning((object)message); } else { _log.LogInfo((object)message); } } } internal sealed class OverlayRenderer { private static readonly CultureInfo German = CultureInfo.GetCultureInfo("de-DE"); private readonly ModConfig _config; private GUIStyle _style; private GUIStyle _shadow; private string _text = string.Empty; private float _nextRefresh; internal OverlayRenderer(ModConfig config) { _config = config; } internal void Draw(float fps, bool enabled, PortalPhase portalPhase) { //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) if (_config.ShowOverlay.Value) { if (Time.unscaledTime >= _nextRefresh) { _nextRefresh = Time.unscaledTime + Mathf.Max(0.05f, _config.RefreshInterval.Value); float lodBias = QualitySettings.lodBias; string text = ((Mathf.Abs(lodBias - Mathf.Round(lodBias)) < 0.001f) ? lodBias.ToString("0", German) : lodBias.ToString("0.0", German)); string text2 = (_config.ShowClock.Value ? (" | " + DateTime.Now.ToString(_config.Use24HourClock.Value ? "HH:mm" : "h:mm tt", CultureInfo.CurrentCulture)) : string.Empty); _text = Mathf.RoundToInt(fps) + " FPS" + (_config.ShowTargetFps.Value ? (" | " + _config.ActiveTargetFps + " mode") : string.Empty) + " | LOD: " + text + text2 + portalPhase switch { PortalPhase.PostArrivalRecovery => " | Laden", PortalPhase.WaitingForPositionJump => " | Portal", _ => enabled ? string.Empty : " | Auto: Aus", }; } EnsureStyles(); Rect val = default(Rect); ((Rect)(ref val))..ctor((float)_config.PositionX.Value, (float)_config.PositionY.Value, 650f, 30f); GUI.Label(new Rect(((Rect)(ref val)).x + 1f, ((Rect)(ref val)).y + 1f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), _text, _shadow); GUI.Label(val, _text, _style); } } private void EnsureStyles() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown int num = Mathf.Clamp(_config.FontSize.Value, 8, 48); if (_style == null || _style.fontSize != num) { GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = num }; val.normal.textColor = Color.white; _style = val; GUIStyle val2 = new GUIStyle(_style); val2.normal.textColor = new Color(0f, 0f, 0f, 0.85f); _shadow = val2; } } } [HarmonyPatch(typeof(TeleportWorld), "Teleport")] internal static class TeleportWorldTeleportPatch { private static void Prefix(Player player) { if ((Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer) { AutoLodBiasPlugin.Instance?.BeginPortal(); } } } }