Decompiled source of MEL Performance v0.1.0

plugins/VSM.Perf/VSM.Perf.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Profiling;
using Unity.Profiling.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Profiling;
using UnityEngine.UI;
using VSM.Perf.Infra;
using VSM.Perf.Logic;
using VSM.Perf.Runtime;
using Valheim.SettingsGui;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("VSM.Perf")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+0defb453dde03b7e7af06426a514a561b6c81bb7")]
[assembly: AssemblyProduct("VSM.Perf")]
[assembly: AssemblyTitle("VSM.Perf")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[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 VSM.Perf
{
	[BepInPlugin("vsm.perf", "MEL Performance", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class PerfPlugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static ConfigFile Cfg;

		internal static string DataDir;

		private Harmony _harmony;

		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<KeyboardShortcut> OverlayKey;

		public static ConfigEntry<bool> OverlayOnStart;

		public static ConfigEntry<bool> DeferAssetUnload;

		public static ConfigEntry<int> PointShadowBudget;

		public static ConfigEntry<PointShadowQuality> PointShadowResolution;

		public static ConfigEntry<float> SunSmallShadowCull;

		public static ConfigEntry<float> SunShadowDistance;

		public static ConfigEntry<bool> DepthNormalsOff;

		public static ConfigEntry<float> SpawnBudgetMs;

		public static ConfigEntry<float> SpawnMaxExtraDelay;

		public static ConfigEntry<bool> HorizonIntegration;

		public static ConfigEntry<string> ProfilerExtraTargets;

		public static ConfigEntry<int> BenchSeconds;

		internal static bool On
		{
			get
			{
				if (Enabled != null)
				{
					return Enabled.Value;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = ((BaseUnityPlugin)this).Config;
			try
			{
				BindConfig();
			}
			catch (Exception ex)
			{
				Log.LogError((object)("vsm.perf: error preparando la configuración; el mod queda apagado: " + ex));
				return;
			}
			Log.LogInfo((object)("vsm.perf 0.1.0 | Valheim " + Env.GameVersion + " | Unity " + Application.unityVersion + " | headless=" + Env.IsHeadless));
			if (Env.IsHeadless)
			{
				Log.LogInfo((object)"vsm.perf: servidor dedicado: nada que hacer (mod de cliente).");
				return;
			}
			try
			{
				DataDir = Path.Combine(Paths.ConfigPath, "vsm.perf");
				PerfLocalization.Register(Log, Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), PerfTexts.Spanish());
				_harmony = new Harmony("vsm.perf");
				SafePatch.PatchAll(Log, _harmony, typeof(PerfPlugin).Assembly);
				SettingsRow.SavePatched = IsPatchedByUs(AccessTools.DeclaredMethod(typeof(AccessibilitySettings), "OnOkAsync", new Type[1] { typeof(OkActionCompletedHandler) }, (Type[])null));
				((Component)this).gameObject.AddComponent<PerfController>();
				Log.LogInfo((object)("vsm.perf: " + (Enabled.Value ? "encendido" : "apagado (se enciende en Ajustes → Accesibilidad → MEL Performance)") + "."));
			}
			catch (Exception ex2)
			{
				Log.LogError((object)("vsm.perf: no se pudo iniciar; el mod queda apagado: " + ex2));
			}
		}

		private void BindConfig()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Expected O, but got Unknown
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Expected O, but got Unknown
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Expected O, but got Unknown
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Expected O, but got Unknown
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Expected O, but got Unknown
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Expected O, but got Unknown
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Expected O, but got Unknown
			//IL_0322: Unknown result type (might be due to invalid IL or missing references)
			//IL_032c: Expected O, but got Unknown
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_036d: Expected O, but got Unknown
			//IL_03b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03be: Expected O, but got Unknown
			//IL_0401: Unknown result type (might be due to invalid IL or missing references)
			//IL_040b: Expected O, but got Unknown
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "Enabled", false, new ConfigDescription("Interruptor general de MEL Performance (experimental). Viene apagado: se enciende en Ajustes → Accesibilidad → \"MEL Performance (más FPS)\", aquí o con vsm_perf on. Apagado, el juego queda exactamente como sin el mod.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 101
				}
			}));
			OverlayKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("1. General", "OverlayKey", new KeyboardShortcut((KeyCode)290, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), new ConfigDescription("Muestra u oculta el medidor (FPS, 1 % low, draw calls, sombras, piezas, horizon…). Comando: vsm_perf hud.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 100
				}
			}));
			OverlayOnStart = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "OverlayOnStart", false, new ConfigDescription("Mostrar el medidor al entrar al juego.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 99
				}
			}));
			DeferAssetUnload = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Picos", "DeferAssetUnload", true, new ConfigDescription("Cada hora el juego descarga lo que no usa y recolecta basura (congelón de 0,1–0,5 s). Con esto, si te toca jugando se aplaza a un momento tranquilo (menú, inventario, durmiendo, carga o 30 s sin tocar nada), como mucho una hora más.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 90
				}
			}));
			PointShadowBudget = ((BaseUnityPlugin)this).Config.Bind<int>("3. Sombras", "PointShadowBudget", 4, new ConfigDescription("Máximo de luces puntuales (antorchas, fogatas…) con sombra a la vez. Se quedan con sombra las que más se ven (la que te ilumina, siempre); al resto se le quita solo mientras se dibuja el frame (el sigilo no cambia). Solo quita sombras que el juego haya dado: si en Ajustes tienes pocas, esto no cambia nada. -1 = como el juego.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-1, 32), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 80
				}
			}));
			PointShadowResolution = ((BaseUnityPlugin)this).Config.Bind<PointShadowQuality>("3. Sombras", "PointShadowResolution", PointShadowQuality.Vanilla, new ConfigDescription("Resolución de las sombras de luces puntuales. Vanilla = como el juego; Balanced = igual de cerca, 512 a media distancia y 256 lejos; Low = 512 cerca y 256 el resto.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 79
				}
			}));
			SunSmallShadowCull = ((BaseUnityPlugin)this).Config.Bind<float>("3. Sombras", "SunSmallShadowCull", 0f, new ConfigDescription("Distancia (m) hasta la que proyectan sombra del sol los objetos chicos (objetos tirados, decoración chica, antorchas y alfombras). 0 = como el juego. Probar 30–50.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 150f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 78
				}
			}));
			SunShadowDistance = ((BaseUnityPlugin)this).Config.Bind<float>("3. Sombras", "SunShadowDistance", 0f, new ConfigDescription("Distancia de las sombras del sol (m). 0 = la de tu calidad de sombras en Ajustes (80/120/150 m).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 77
				}
			}));
			DepthNormalsOff = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Cámara", "DepthNormalsOff", false, new ConfigDescription("EXPERIMENTAL. El juego pide siempre a la cámara una textura de profundidad + normales (puede costar una pasada extra de toda la escena). Con esto pide solo profundidad. Si algún efecto se ve raro, apágalo (compáralo con: vsm_perf diff depthnormals).", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 70
				}
			}));
			SpawnBudgetMs = ((BaseUnityPlugin)this).Config.Bind<float>("5. Carga de objetos", "SpawnBudgetMs", 4f, new ConfigDescription("Al llegar a una base el juego crea muchos objetos de golpe (tirones). Tiempo máximo (ms) por tanda para crearlos; nunca menos que el mínimo del juego ni en pantallas de carga. 0 = como el juego.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 60
				}
			}));
			SpawnMaxExtraDelay = ((BaseUnityPlugin)this).Config.Bind<float>("5. Carga de objetos", "SpawnMaxExtraDelaySeconds", 1.5f, new ConfigDescription("Como mucho cuántos segundos más que el juego puede tardar en aparecer todo. Con MEL Horizons cargado se limita además al 10 % de su MinLoadedSeconds (para que no guarde bases a medio llegar).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 10f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 59
				}
			}));
			HorizonIntegration = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Integración", "Horizon", true, new ConfigDescription("Si está MEL Horizons (vsm.horizon): mostrar su costo en el medidor y el benchmark, medirlo en el instrumentador, ajustar el plazo de carga de objetos y dejar fijo su modo adaptativo mientras corre un benchmark. No hace falta tenerlo.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 50
				}
			}));
			ProfilerExtraTargets = ((BaseUnityPlugin)this).Config.Bind<string>("9. Medición", "ProfilerExtraTargets", string.Empty, new ConfigDescription("Métodos extra para el instrumentador (vsm_perf prof on), separados por coma: Tipo.Método (p. ej. Player.Update, ClutterSystem.LateUpdate).", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 10,
					IsAdvanced = true
				}
			}));
			BenchSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("9. Medición", "BenchSeconds", 30, new ConfigDescription("Duración por defecto de la medición de un benchmark (s), sin contar la espera y el calentamiento.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 300), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 9
				}
			}));
		}

		private static bool IsPatchedByUs(MethodBase method)
		{
			if (method == null)
			{
				return false;
			}
			Patches patchInfo = Harmony.GetPatchInfo(method);
			if (patchInfo != null)
			{
				return patchInfo.Owners.Contains("vsm.perf");
			}
			return false;
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "vsm.perf";

		public const string PLUGIN_NAME = "MEL Performance";

		public const string PLUGIN_VERSION = "0.1.0";
	}
}
namespace VSM.Perf.Runtime
{
	internal static class AssetUnload
	{
		private static readonly DeferPolicy s_policy = new DeferPolicy();

		private static bool s_bypass;

		public static DeferPolicy Policy => s_policy;

		public static bool OnPeriodic()
		{
			if (s_bypass)
			{
				return true;
			}
			bool num = s_policy.OnPeriodic(PerfPlugin.On && PerfPlugin.DeferAssetUnload.Value, IsSafeMoment(), Time.realtimeSinceStartup);
			if (!num)
			{
				PerfPlugin.Log.LogInfo((object)"vsm.perf: limpieza de assets de cada hora aplazada hasta un momento tranquilo.");
			}
			return num;
		}

		public static void Tick()
		{
			if (!s_policy.Pending)
			{
				return;
			}
			Game instance = Game.instance;
			if ((Object)(object)instance == (Object)null || !s_policy.Poll(PerfPlugin.On && PerfPlugin.DeferAssetUnload.Value, IsSafeMoment(), MemoryHigh(), Time.realtimeSinceStartup))
			{
				return;
			}
			PerfPlugin.Log.LogInfo((object)"vsm.perf: ejecutando la limpieza de assets aplazada.");
			s_bypass = true;
			try
			{
				instance.CollectResourcesCheckPeriodic();
			}
			catch (Exception ex)
			{
				PerfPlugin.Log.LogWarning((object)("vsm.perf: la limpieza aplazada falló: " + ex.Message));
			}
			finally
			{
				s_bypass = false;
			}
		}

		public static bool IsSafeMoment()
		{
			Player localPlayer = Player.m_localPlayer;
			bool loading = (Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsTeleporting();
			bool sleeping = (Object)(object)localPlayer != (Object)null && localPlayer.IsSleeping();
			double idleSeconds = 0.0;
			try
			{
				if (ZInput.instance != null)
				{
					idleSeconds = (DateTime.Now - ZInput.instance.GetLastInputTimer()).TotalSeconds;
				}
			}
			catch (Exception)
			{
				idleSeconds = 0.0;
			}
			return DeferPolicy.IsSafeMoment(Menu.IsVisible(), InventoryGui.IsVisible(), sleeping, loading, idleSeconds);
		}

		private static bool MemoryHigh()
		{
			long num = (long)SystemInfo.systemMemorySize * 1024L * 1024;
			if (num > 0)
			{
				return Profiler.GetTotalReservedMemoryLong() > num * 8 / 10;
			}
			return false;
		}
	}
	internal struct BenchPoint
	{
		public string Name;

		public Vector3 Position;

		public float Yaw;
	}
	internal sealed class BenchRunner
	{
		public const float MaxDistanceFromPoint = 2.5f;

		public static bool SweepActive;

		public static float SweepYaw;

		public static float SweepPitch;

		private readonly Counters _counters;

		private readonly Toggles _toggles;

		private readonly HorizonBridge _horizon;

		private readonly List<float> _frames = new List<float>();

		private double[] _counterSum;

		private int[] _counterN;

		private BenchPlan _plan;

		private BenchPoint _point;

		private string _label;

		private StreamWriter _csv;

		private Action<BenchResult> _onDone;

		private Action<string> _onCancel;

		public bool Running => _plan != null;

		public string Status
		{
			get
			{
				if (_plan == null)
				{
					return string.Empty;
				}
				string text = ((_plan.Phase == BenchPhase.Settle) ? "esperando a que todo cargue" : ((_plan.Phase == BenchPhase.Warmup) ? "calentando" : "midiendo"));
				return PerfLocalization.T("vsm_perf_hud_bench", _point.Name + (string.IsNullOrEmpty(_label) ? string.Empty : (" " + _label)), text, _plan.PhaseTime.ToString("0", CultureInfo.InvariantCulture));
			}
		}

		public static string PointsFile => Path.Combine(PerfPlugin.DataDir, "bench_puntos.txt");

		public BenchRunner(Counters counters, Toggles toggles, HorizonBridge horizon)
		{
			_counters = counters;
			_toggles = toggles;
			_horizon = horizon;
		}

		public static List<BenchPoint> LoadPoints()
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			List<BenchPoint> list = new List<BenchPoint>();
			if (!File.Exists(PointsFile))
			{
				return list;
			}
			string[] array = File.ReadAllLines(PointsFile, Encoding.UTF8);
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { ';' });
				if (array2.Length >= 5 && !text.StartsWith("#", StringComparison.Ordinal))
				{
					try
					{
						list.Add(new BenchPoint
						{
							Name = array2[0],
							Position = new Vector3(float.Parse(array2[1], CultureInfo.InvariantCulture), float.Parse(array2[2], CultureInfo.InvariantCulture), float.Parse(array2[3], CultureInfo.InvariantCulture)),
							Yaw = float.Parse(array2[4], CultureInfo.InvariantCulture)
						});
					}
					catch (FormatException)
					{
					}
				}
			}
			return list;
		}

		public static bool TryGetPoint(string name, out BenchPoint point)
		{
			foreach (BenchPoint item in LoadPoints())
			{
				if (string.Equals(item.Name, name, StringComparison.OrdinalIgnoreCase))
				{
					point = item;
					return true;
				}
			}
			point = default(BenchPoint);
			return false;
		}

		public static string MarkPoint(string name)
		{
			//IL_0072: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			Camera mainCamera = RenderOverrides.MainCamera;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)mainCamera == (Object)null)
			{
				return "hay que estar en el mundo para marcar un punto.";
			}
			if (string.IsNullOrEmpty(name) || name.IndexOf(';') >= 0)
			{
				return "nombre de punto inválido.";
			}
			List<BenchPoint> list = LoadPoints();
			list.RemoveAll((BenchPoint p) => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase));
			Vector3 position = ((Component)localPlayer).transform.position;
			list.Add(new BenchPoint
			{
				Name = name,
				Position = position,
				Yaw = ((Component)mainCamera).transform.eulerAngles.y
			});
			Directory.CreateDirectory(PerfPlugin.DataDir);
			List<string> list2 = new List<string> { "# nombre;x;y;z;giro  (vsm_perf bench marcar <nombre>)" };
			foreach (BenchPoint item in list)
			{
				string[] obj = new string[5] { item.Name, null, null, null, null };
				float x = item.Position.x;
				obj[1] = x.ToString("0.###", CultureInfo.InvariantCulture);
				x = item.Position.y;
				obj[2] = x.ToString("0.###", CultureInfo.InvariantCulture);
				x = item.Position.z;
				obj[3] = x.ToString("0.###", CultureInfo.InvariantCulture);
				x = item.Yaw;
				obj[4] = x.ToString("0.#", CultureInfo.InvariantCulture);
				list2.Add(string.Join(";", obj));
			}
			File.WriteAllLines(PointsFile, list2.ToArray(), Encoding.UTF8);
			return "punto «" + name + "» guardado en " + ((Vector3)(ref position)).ToString("F1") + ".";
		}

		public string Start(BenchPoint point, float seconds, string label, Action<BenchResult> onDone, Action<string> onCancel)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (Running)
			{
				return "ya hay un benchmark corriendo (vsm_perf bench parar).";
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return "hay que estar en el mundo.";
			}
			float num = Vector3.Distance(((Component)localPlayer).transform.position, point.Position);
			if (num > 2.5f)
			{
				return "acércate al punto «" + point.Name + "» (estás a " + num.ToString("0.0", CultureInfo.InvariantCulture) + " m; máximo " + 2.5f + " m).";
			}
			_point = point;
			_label = label ?? string.Empty;
			_onDone = onDone;
			_onCancel = onCancel;
			_plan = new BenchPlan(seconds);
			_frames.Clear();
			_counterSum = new double[BenchReport.CounterColumns.Length];
			_counterN = new int[BenchReport.CounterColumns.Length];
			_toggles.BeginSession();
			_horizon.SetTemp("Adaptive", value: false);
			string text = Path.Combine(PerfPlugin.DataDir, "bench");
			Directory.CreateDirectory(text);
			string path = Safe(point.Name) + (string.IsNullOrEmpty(_label) ? string.Empty : ("_" + Safe(_label))) + "_" + DateTime.Now.ToString("yyyyMMdd_HHmmss", CultureInfo.InvariantCulture) + ".csv";
			_csv = new StreamWriter(Path.Combine(text, path), append: false, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			_csv.WriteLine(BenchReport.FrameHeader);
			SetPose(0f);
			SweepActive = true;
			return "benchmark «" + point.Name + "» " + _label + ": quieto y sin tocar nada; espera + 5 s de calentamiento + " + _plan.MeasureSeconds.ToString("0", CultureInfo.InvariantCulture) + " s.";
		}

		public void Tick(float dt)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (_plan == null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				Cancel("saliste del mundo");
				return;
			}
			if (Vector3.Distance(((Component)localPlayer).transform.position, _point.Position) > 3.5f)
			{
				Cancel("te moviste del punto");
				return;
			}
			BenchPhase phase = _plan.Phase;
			BenchPhase benchPhase = _plan.Step(dt, SpawnControl.PendingNear);
			if (phase == BenchPhase.Measure)
			{
				float num = dt * 1000f;
				_frames.Add(num);
				double[] array = _counters.Snapshot();
				for (int i = 0; i < array.Length && i < _counterSum.Length; i++)
				{
					if (!double.IsNaN(array[i]))
					{
						_counterSum[i] += array[i];
						_counterN[i]++;
					}
				}
				_csv.WriteLine(BenchReport.FrameRow(_frames.Count, (_plan.Phase == BenchPhase.Measure) ? _plan.PhaseTime : _plan.MeasureSeconds, num, array));
			}
			switch (benchPhase)
			{
			case BenchPhase.Measure:
				SetPose(_plan.PhaseTime);
				break;
			case BenchPhase.Done:
				Finish();
				break;
			}
		}

		public void Cancel(string reason)
		{
			if (_plan != null)
			{
				Close();
				Action<string> onCancel = _onCancel;
				_onCancel = null;
				_onDone = null;
				onCancel?.Invoke(reason);
			}
		}

		private void Finish()
		{
			BenchResult benchResult = new BenchResult
			{
				Utc = DateTime.UtcNow,
				Point = _point.Name,
				Label = _label,
				Frames = FrameStats.Summarize(_frames),
				Toggles = Toggles.Codes(),
				Graphics = PerfController.GraphicsSummary(),
				Horizon = _horizon.Describe(),
				System = PerfController.SystemSummary(),
				SettleTimedOut = _plan.SettleTimedOut
			};
			for (int i = 0; i < benchResult.Counters.Length; i++)
			{
				benchResult.Counters[i] = ((_counterN[i] > 0) ? (_counterSum[i] / (double)_counterN[i]) : double.NaN);
			}
			try
			{
				string path = Path.Combine(PerfPlugin.DataDir, "bench_resumen.csv");
				bool flag = !File.Exists(path);
				using StreamWriter streamWriter = new StreamWriter(path, append: true, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				if (flag)
				{
					streamWriter.WriteLine(BenchReport.SummaryHeader);
				}
				streamWriter.WriteLine(BenchReport.SummaryRow(benchResult));
			}
			catch (Exception ex)
			{
				PerfPlugin.Log.LogWarning((object)("vsm.perf: no se pudo escribir bench_resumen.csv: " + ex.Message));
			}
			Close();
			Action<BenchResult> onDone = _onDone;
			_onDone = null;
			_onCancel = null;
			onDone?.Invoke(benchResult);
		}

		private void Close()
		{
			SweepActive = false;
			_plan = null;
			try
			{
				if (_csv != null)
				{
					_csv.Dispose();
				}
			}
			catch (Exception)
			{
			}
			_csv = null;
			_toggles.EndSession();
		}

		private void SetPose(float t)
		{
			BenchPlan.Sweep(t, _plan.MeasureSeconds, out var yaw, out var pitch);
			SweepYaw = _point.Yaw + yaw;
			SweepPitch = pitch;
		}

		private static string Safe(string s)
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (char c in s)
			{
				stringBuilder.Append((char.IsLetterOrDigit(c) || c == '-' || c == '_') ? c : '_');
			}
			return stringBuilder.ToString();
		}
	}
	internal sealed class Counters : IDisposable
	{
		private struct Rec
		{
			public ProfilerCategory Category;

			public string Name;

			public ProfilerRecorder Recorder;
		}

		private readonly Rec[] _recs = new Rec[6]
		{
			new Rec
			{
				Category = ProfilerCategory.Render,
				Name = "Draw Calls Count"
			},
			new Rec
			{
				Category = ProfilerCategory.Render,
				Name = "Batches Count"
			},
			new Rec
			{
				Category = ProfilerCategory.Render,
				Name = "SetPass Calls Count"
			},
			new Rec
			{
				Category = ProfilerCategory.Render,
				Name = "Triangles Count"
			},
			new Rec
			{
				Category = ProfilerCategory.Render,
				Name = "Shadow Casters Count"
			},
			new Rec
			{
				Category = ProfilerCategory.Memory,
				Name = "GC Allocated In Frame"
			}
		};

		private readonly double[] _values;

		private readonly FrameTiming[] _timings = (FrameTiming[])(object)new FrameTiming[1];

		private bool _timingEnabled;

		private bool _active;

		public double CpuMainMs { get; private set; }

		public double CpuRenderMs { get; private set; }

		public double GpuMs { get; private set; }

		public double this[int index] => _values[index];

		public int Length => _values.Length;

		public Counters()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//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_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_008b: 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)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			_values = new double[_recs.Length];
			Clear();
		}

		public void SetActive(bool active)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if (active == _active)
			{
				return;
			}
			_active = active;
			if (!active)
			{
				Dispose();
				Clear();
				return;
			}
			for (int i = 0; i < _recs.Length; i++)
			{
				try
				{
					_recs[i].Recorder = ProfilerRecorder.StartNew(_recs[i].Category, _recs[i].Name, 1, (ProfilerRecorderOptions)24);
				}
				catch (Exception)
				{
				}
			}
			try
			{
				_timingEnabled = FrameTimingManager.IsFeatureEnabled();
			}
			catch (Exception)
			{
				_timingEnabled = false;
			}
		}

		private void Clear()
		{
			for (int i = 0; i < _values.Length; i++)
			{
				_values[i] = double.NaN;
			}
			double num = (GpuMs = double.NaN);
			double cpuMainMs = (CpuRenderMs = num);
			CpuMainMs = cpuMainMs;
			_timingEnabled = false;
		}

		public void Sample()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (!_active)
			{
				return;
			}
			for (int i = 0; i < _recs.Length; i++)
			{
				ProfilerRecorder recorder = _recs[i].Recorder;
				double num = (((ProfilerRecorder)(ref recorder)).Valid ? ((double)((ProfilerRecorder)(ref recorder)).LastValue) : double.NaN);
				_values[i] = ((i == 5 && !double.IsNaN(num)) ? (num / 1024.0) : num);
			}
			if (_timingEnabled)
			{
				FrameTimingManager.CaptureFrameTimings();
				if (FrameTimingManager.GetLatestTimings(1u, _timings) != 0)
				{
					CpuMainMs = _timings[0].cpuMainThreadFrameTime;
					CpuRenderMs = _timings[0].cpuRenderThreadFrameTime;
					GpuMs = _timings[0].gpuFrameTime;
				}
			}
		}

		public double[] Snapshot()
		{
			return (double[])_values.Clone();
		}

		public List<string> Describe()
		{
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			for (int i = 0; i < _recs.Length; i++)
			{
				list.Add((((ProfilerRecorder)(ref _recs[i].Recorder)).Valid ? "  OK   " : "  n/d  ") + ((ProfilerCategory)(ref _recs[i].Category)).Name + " / " + _recs[i].Name);
			}
			list.Add("  FrameTimingManager: " + (_timingEnabled ? "habilitado" : "no habilitado en este build"));
			try
			{
				List<ProfilerRecorderHandle> list2 = new List<ProfilerRecorderHandle>();
				ProfilerRecorderHandle.GetAvailable(list2);
				list.Add("  Disponibles en total: " + list2.Count);
				List<string> list3 = new List<string>();
				foreach (ProfilerRecorderHandle item in list2)
				{
					ProfilerRecorderDescription description = ProfilerRecorderHandle.GetDescription(item);
					ProfilerCategory category = ((ProfilerRecorderDescription)(ref description)).Category;
					list3.Add(((ProfilerCategory)(ref category)).Name + " / " + ((ProfilerRecorderDescription)(ref description)).Name);
				}
				list3.Sort(StringComparer.Ordinal);
				foreach (string item2 in list3)
				{
					list.Add("    " + item2);
				}
			}
			catch (Exception ex)
			{
				list.Add("  (no se pudo listar: " + ex.Message + ")");
			}
			return list;
		}

		public void Dispose()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < _recs.Length; i++)
			{
				try
				{
					((ProfilerRecorder)(ref _recs[i].Recorder)).Dispose();
				}
				catch (Exception)
				{
				}
				_recs[i].Recorder = default(ProfilerRecorder);
			}
		}
	}
	internal static class Dump
	{
		public static string Write(HorizonBridge horizon)
		{
			string text = Path.Combine(PerfPlugin.DataDir, "dump_" + DateTime.Now.ToString("yyyyMMdd_HHmmss", CultureInfo.InvariantCulture));
			Directory.CreateDirectory(text);
			int num = WritePieceRender(Path.Combine(text, "piece_render.csv"));
			WriteBase(Path.Combine(text, "perf_base.txt"), horizon);
			return text + " (" + num + " prefabs de piezas)";
		}

		private static int WritePieceRender(string path)
		{
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null)
			{
				File.WriteAllText(path, "sin ZNetScene (entra a un mundo)\n");
				return 0;
			}
			int num = 0;
			using StreamWriter streamWriter = new StreamWriter(path, append: false, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			streamWriter.WriteLine("prefab,lodgroups,renderer,parte,lod,lod_altura,activo,capa,sombra,recibe,materiales,shaders,cola,instancing,legible,vertices,submallas");
			foreach (GameObject prefab in instance.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<Piece>() == (Object)null)
				{
					continue;
				}
				num++;
				WearNTear component = prefab.GetComponent<WearNTear>();
				LODGroup[] componentsInChildren = prefab.GetComponentsInChildren<LODGroup>(true);
				Dictionary<Renderer, KeyValuePair<int, float>> dictionary = new Dictionary<Renderer, KeyValuePair<int, float>>();
				LODGroup[] array = componentsInChildren;
				Renderer[] renderers;
				for (int i = 0; i < array.Length; i++)
				{
					LOD[] lODs = array[i].GetLODs();
					for (int j = 0; j < lODs.Length; j++)
					{
						renderers = lODs[j].renderers;
						foreach (Renderer val in renderers)
						{
							if ((Object)(object)val != (Object)null && !dictionary.ContainsKey(val))
							{
								dictionary[val] = new KeyValuePair<int, float>(j, lODs[j].screenRelativeTransitionHeight);
							}
						}
					}
				}
				renderers = prefab.GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val2 in renderers)
				{
					if (ParticleSystemRendererLike(val2))
					{
						continue;
					}
					Material[] sharedMaterials = val2.sharedMaterials;
					Mesh val3 = null;
					MeshFilter component2 = ((Component)val2).GetComponent<MeshFilter>();
					if ((Object)(object)component2 != (Object)null)
					{
						val3 = component2.sharedMesh;
					}
					else
					{
						SkinnedMeshRenderer val4 = (SkinnedMeshRenderer)(object)((val2 is SkinnedMeshRenderer) ? val2 : null);
						if (val4 != null)
						{
							val3 = val4.sharedMesh;
						}
					}
					KeyValuePair<int, float> value;
					bool flag = dictionary.TryGetValue(val2, out value);
					streamWriter.WriteLine(string.Join(",", BenchReport.Field(((Object)prefab).name), componentsInChildren.Length.ToString(CultureInfo.InvariantCulture), BenchReport.Field(PathOf(((Component)val2).transform, prefab.transform)), Part(component, ((Component)val2).transform), flag ? value.Key.ToString(CultureInfo.InvariantCulture) : "-1", flag ? BenchReport.Number(value.Value) : string.Empty, (((Component)val2).gameObject.activeSelf && val2.enabled) ? "si" : "no", BenchReport.Field(LayerMask.LayerToName(((Component)val2).gameObject.layer)), ((object)val2.shadowCastingMode/*cast due to .constrained prefix*/).ToString(), val2.receiveShadows ? "si" : "no", BenchReport.Field(string.Join("|", sharedMaterials.Select((Material m) => (!((Object)(object)m != (Object)null)) ? "null" : ((Object)m).name).ToArray())), BenchReport.Field(string.Join("|", sharedMaterials.Select((Material m) => (!((Object)(object)m != (Object)null) || !((Object)(object)m.shader != (Object)null)) ? "null" : ((Object)m.shader).name).ToArray())), (sharedMaterials.Length != 0 && (Object)(object)sharedMaterials[0] != (Object)null) ? sharedMaterials[0].renderQueue.ToString(CultureInfo.InvariantCulture) : string.Empty, (sharedMaterials.Length != 0 && sharedMaterials.All((Material m) => (Object)(object)m != (Object)null && m.enableInstancing)) ? "si" : "no", (!((Object)(object)val3 != (Object)null)) ? string.Empty : (val3.isReadable ? "si" : "no"), ((Object)(object)val3 != (Object)null) ? val3.vertexCount.ToString(CultureInfo.InvariantCulture) : string.Empty, ((Object)(object)val3 != (Object)null) ? val3.subMeshCount.ToString(CultureInfo.InvariantCulture) : string.Empty));
				}
			}
			return num;
		}

		private static bool ParticleSystemRendererLike(Renderer r)
		{
			return ((object)r).GetType().Name == "ParticleSystemRenderer";
		}

		private static string Part(WearNTear wnt, Transform t)
		{
			if ((Object)(object)wnt == (Object)null)
			{
				return "sin_desgaste";
			}
			if (Under(t, wnt.m_new))
			{
				return "nuevo";
			}
			if (Under(t, wnt.m_worn))
			{
				return "gastado";
			}
			if (Under(t, wnt.m_broken))
			{
				return "roto";
			}
			if (Under(t, wnt.m_wet))
			{
				return "mojado";
			}
			if (((Object)(object)wnt.m_snow != (Object)null && (Object)(object)t == (Object)(object)((Component)wnt.m_snow).transform) || ((Object)(object)wnt.m_snowWorn != (Object)null && (Object)(object)t == (Object)(object)((Component)wnt.m_snowWorn).transform) || ((Object)(object)wnt.m_snowBroken != (Object)null && (Object)(object)t == (Object)(object)((Component)wnt.m_snowBroken).transform))
			{
				return "nieve";
			}
			if (wnt.m_fragmentRoots != null)
			{
				GameObject[] fragmentRoots = wnt.m_fragmentRoots;
				foreach (GameObject root in fragmentRoots)
				{
					if (Under(t, root))
					{
						return "escombros";
					}
				}
			}
			return "otro";
		}

		private static bool Under(Transform t, GameObject root)
		{
			if ((Object)(object)root != (Object)null)
			{
				if (!((Object)(object)t == (Object)(object)root.transform))
				{
					return t.IsChildOf(root.transform);
				}
				return true;
			}
			return false;
		}

		private static string PathOf(Transform t, Transform root)
		{
			List<string> list = new List<string>();
			while ((Object)(object)t != (Object)null && (Object)(object)t != (Object)(object)root)
			{
				list.Add(((Object)t).name);
				t = t.parent;
			}
			list.Reverse();
			if (list.Count != 0)
			{
				return string.Join("/", list.ToArray());
			}
			return "(raíz)";
		}

		private static void WriteBase(string path, HorizonBridge horizon)
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: 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)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: 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_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0401: Unknown result type (might be due to invalid IL or missing references)
			//IL_0406: Unknown result type (might be due to invalid IL or missing references)
			//IL_0422: Unknown result type (might be due to invalid IL or missing references)
			//IL_0427: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0523: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("MEL Performance 0.1.0 · " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture));
			stringBuilder.AppendLine("Valheim " + Env.GameVersion + " · Unity " + Application.unityVersion);
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("== Equipo");
			stringBuilder.AppendLine(PerfController.SystemSummary());
			stringBuilder.AppendLine("Hilos de render: " + PerfController.ThreadingMode());
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("== Calidad");
			stringBuilder.AppendLine(PerfController.GraphicsSummary());
			stringBuilder.AppendLine("shadowCascades " + QualitySettings.shadowCascades + " · shadowResolution " + ((object)QualitySettings.shadowResolution/*cast due to .constrained prefix*/).ToString() + " · shadowProjection " + ((object)QualitySettings.shadowProjection/*cast due to .constrained prefix*/).ToString() + " · maximumLODLevel " + QualitySettings.maximumLODLevel + " · softParticles " + QualitySettings.softParticles + " · maxQueuedFrames " + QualitySettings.maxQueuedFrames + " · vSyncCount " + QualitySettings.vSyncCount + " · targetFrameRate " + Application.targetFrameRate);
			stringBuilder.AppendLine("Mejoras: " + Toggles.Codes());
			stringBuilder.AppendLine("Horizon: " + horizon.Describe());
			stringBuilder.AppendLine();
			Camera mainCamera = RenderOverrides.MainCamera;
			stringBuilder.AppendLine("== Cámara principal");
			if ((Object)(object)mainCamera != (Object)null)
			{
				stringBuilder.AppendLine("depthTextureMode " + ((object)mainCamera.depthTextureMode/*cast due to .constrained prefix*/).ToString() + " · renderingPath " + ((object)mainCamera.renderingPath/*cast due to .constrained prefix*/).ToString() + " · actualRenderingPath " + ((object)mainCamera.actualRenderingPath/*cast due to .constrained prefix*/).ToString() + " · HDR " + mainCamera.allowHDR + " · MSAA " + mainCamera.allowMSAA + " · far " + mainCamera.farClipPlane.ToString("0", CultureInfo.InvariantCulture) + " · fov " + mainCamera.fieldOfView.ToString("0", CultureInfo.InvariantCulture) + " · destino " + (((Object)(object)mainCamera.targetTexture != (Object)null) ? (((Texture)mainCamera.targetTexture).width + "x" + ((Texture)mainCamera.targetTexture).height) : "pantalla"));
				MonoBehaviour[] components = ((Component)mainCamera).GetComponents<MonoBehaviour>();
				foreach (MonoBehaviour val in components)
				{
					if (!((Object)(object)val == (Object)null))
					{
						stringBuilder.AppendLine("  componente " + ((object)val).GetType().FullName + (((Behaviour)val).enabled ? "" : " (apagado)") + ProfileModels(val));
					}
				}
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("== Sondas de reflejos");
			ReflectionProbe[] array = Object.FindObjectsByType<ReflectionProbe>((FindObjectsSortMode)0);
			foreach (ReflectionProbe val2 in array)
			{
				stringBuilder.AppendLine("  " + ((Object)val2).name + " · modo " + ((object)val2.mode/*cast due to .constrained prefix*/).ToString() + " · refresco " + ((object)val2.refreshMode/*cast due to .constrained prefix*/).ToString() + " · reparto " + ((object)val2.timeSlicingMode/*cast due to .constrained prefix*/).ToString() + " · resolución " + val2.resolution + " · máscara " + val2.cullingMask + " · activa " + ((Behaviour)val2).isActiveAndEnabled);
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("== Luces (LightLod)");
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			foreach (LightLod light in LightLod.m_lights)
			{
				if (!((Object)(object)light == (Object)null) && !((Object)(object)light.m_light == (Object)null))
				{
					num++;
					if (((Behaviour)light.m_light).enabled && light.m_light.range > 0f)
					{
						num2++;
					}
					if ((int)light.m_light.shadows != 0)
					{
						num3++;
					}
				}
			}
			stringBuilder.AppendLine("total " + num + " · encendidas " + num2 + " · con sombra " + num3 + " · límite luces " + LightLod.m_lightLimit + " · límite sombras " + LightLod.m_shadowLimit);
			stringBuilder.AppendLine();
			Player localPlayer = Player.m_localPlayer;
			stringBuilder.AppendLine("== Alrededor del jugador (64 m, celdas de 16 m)");
			if ((Object)(object)localPlayer != (Object)null)
			{
				WriteCells(stringBuilder, ((Component)localPlayer).transform.position, 64f, 16f);
			}
			else
			{
				stringBuilder.AppendLine("(sin jugador)");
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("== Plugins cargados");
			foreach (KeyValuePair<string, PluginInfo> item in Chainloader.PluginInfos.OrderBy((KeyValuePair<string, PluginInfo> k) => k.Key))
			{
				stringBuilder.AppendLine("  " + item.Key + " " + item.Value.Metadata.Version);
			}
			File.WriteAllText(path, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		private static void WriteCells(StringBuilder sb, Vector3 center, float radius, float cell)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//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_006c: 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_0088: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<Vector3Int, int[]> dictionary = new Dictionary<Vector3Int, int[]>();
			HashSet<Material> hashSet = new HashSet<Material>();
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			Vector3Int key = default(Vector3Int);
			foreach (WearNTear allInstance in WearNTear.GetAllInstances())
			{
				if ((Object)(object)allInstance == (Object)null || Vector3.Distance(((Component)allInstance).transform.position, center) > radius)
				{
					continue;
				}
				num++;
				Vector3 position = ((Component)allInstance).transform.position;
				((Vector3Int)(ref key))..ctor(Mathf.FloorToInt(position.x / cell), Mathf.FloorToInt(position.y / cell), Mathf.FloorToInt(position.z / cell));
				if (!dictionary.TryGetValue(key, out var value))
				{
					value = (dictionary[key] = new int[2]);
				}
				value[0]++;
				num5 += ((Component)allInstance).GetComponentsInChildren<LODGroup>(false).Length;
				Renderer[] componentsInChildren = ((Component)allInstance).GetComponentsInChildren<Renderer>(false);
				foreach (Renderer val in componentsInChildren)
				{
					if (!val.enabled)
					{
						continue;
					}
					num2++;
					value[1]++;
					if (val.HasPropertyBlock())
					{
						num3++;
					}
					Material[] sharedMaterials = val.sharedMaterials;
					foreach (Material val2 in sharedMaterials)
					{
						if (!((Object)(object)val2 == (Object)null))
						{
							hashSet.Add(val2);
							if (((Object)val2).name.IndexOf("(Instance)", StringComparison.Ordinal) >= 0)
							{
								num4++;
							}
						}
					}
				}
			}
			sb.AppendLine("piezas " + num + " · renderers activos " + num2 + " · LODGroups " + num5 + " · materiales distintos " + hashSet.Count + " · renderers con property block " + num3 + " · materiales propios \"(Instance)\" " + num4);
			sb.AppendLine("celdas con piezas: " + dictionary.Count + " (las 15 más cargadas: celda → piezas / renderers)");
			foreach (KeyValuePair<Vector3Int, int[]> item in dictionary.OrderByDescending((KeyValuePair<Vector3Int, int[]> k) => k.Value[1]).Take(15))
			{
				sb.AppendLine("  " + ((object)item.Key/*cast due to .constrained prefix*/).ToString() + " → " + item.Value[0] + " / " + item.Value[1]);
			}
		}

		private static string ProfileModels(MonoBehaviour mb)
		{
			try
			{
				FieldInfo field = ((object)mb).GetType().GetField("profile", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				object obj = ((field != null) ? field.GetValue(mb) : null);
				if (obj == null)
				{
					return string.Empty;
				}
				List<string> list = new List<string>();
				FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
				foreach (FieldInfo fieldInfo in fields)
				{
					object value = fieldInfo.GetValue(obj);
					if (value != null)
					{
						PropertyInfo property = value.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public);
						if (property != null && property.PropertyType == typeof(bool) && (bool)property.GetValue(value, null))
						{
							list.Add(fieldInfo.Name);
						}
					}
				}
				return " · perfil: " + ((list.Count == 0) ? "nada encendido" : string.Join(", ", list.ToArray()));
			}
			catch (Exception)
			{
				return string.Empty;
			}
		}
	}
	internal sealed class HorizonBridge
	{
		public const string Guid = "vsm.horizon";

		public const string ControllerType = "VSM.Horizon.Runtime.HorizonController";

		private bool _resolved;

		private bool _failed;

		private ConfigFile _config;

		private ConfigEntryBase _enabled;

		private ConfigEntryBase _quality;

		private ConfigEntryBase _adaptive;

		private ConfigEntryBase _targetFps;

		private ConfigEntryBase _minLoaded;

		private PropertyInfo _instanceProp;

		private PropertyInfo _scaleProp;

		private FieldInfo _rendererField;

		private PropertyInfo _drawnProp;

		private PropertyInfo _drawCallsProp;

		private Type _controllerType;

		private readonly Dictionary<ConfigEntryBase, object> _saved = new Dictionary<ConfigEntryBase, object>();

		private bool _savedSaveOnSet;

		private static bool Wanted
		{
			get
			{
				if (PerfPlugin.HorizonIntegration != null)
				{
					return PerfPlugin.HorizonIntegration.Value;
				}
				return true;
			}
		}

		public bool Present
		{
			get
			{
				Resolve();
				if (Wanted && !_failed)
				{
					return _config != null;
				}
				return false;
			}
		}

		public bool Enabled
		{
			get
			{
				bool flag = default(bool);
				int num;
				if (Present && _enabled != null)
				{
					object boxedValue = _enabled.BoxedValue;
					if (boxedValue is bool)
					{
						flag = (bool)boxedValue;
						num = 1;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
		}

		public double MinLoadedSeconds
		{
			get
			{
				if (!Enabled || _minLoaded == null)
				{
					return 0.0;
				}
				try
				{
					return Convert.ToDouble(_minLoaded.BoxedValue, CultureInfo.InvariantCulture);
				}
				catch (Exception)
				{
					return 0.0;
				}
			}
		}

		public Type HorizonControllerType
		{
			get
			{
				Resolve();
				if (!Present)
				{
					return null;
				}
				return _controllerType;
			}
		}

		public string Describe()
		{
			if (!Present)
			{
				return "no";
			}
			if (!Enabled)
			{
				return "apagado";
			}
			string text = "encendido · " + Value(_quality);
			bool flag = default(bool);
			int num;
			if (_adaptive != null)
			{
				object boxedValue = _adaptive.BoxedValue;
				if (boxedValue is bool)
				{
					flag = (bool)boxedValue;
					num = 1;
				}
				else
				{
					num = 0;
				}
			}
			else
			{
				num = 0;
			}
			bool flag2 = (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			text += (flag2 ? (" · adaptativo " + Value(_targetFps) + " FPS") : " · fijo");
			try
			{
				object obj = ((_instanceProp != null) ? _instanceProp.GetValue(null, null) : null);
				if (obj != null)
				{
					if (_scaleProp != null)
					{
						text = text + " · escala " + ((float)_scaleProp.GetValue(obj, null)).ToString("0.00", CultureInfo.InvariantCulture);
					}
					object obj2 = ((_rendererField != null) ? _rendererField.GetValue(obj) : null);
					if (obj2 != null && _drawnProp != null && _drawCallsProp != null)
					{
						text = text + " · " + _drawnProp.GetValue(obj2, null)?.ToString() + " inst · " + _drawCallsProp.GetValue(obj2, null)?.ToString() + " draws";
					}
				}
			}
			catch (Exception ex)
			{
				Fail("leer su estado", ex);
			}
			return text;
		}

		public bool SetTemp(string key, bool value)
		{
			if (!Present)
			{
				return false;
			}
			ConfigEntryBase val = ((key == "Enabled") ? _enabled : ((key == "Adaptive") ? _adaptive : null));
			if (val == null)
			{
				return false;
			}
			if (_saved.Count == 0)
			{
				_savedSaveOnSet = _config.SaveOnConfigSet;
			}
			if (!_saved.ContainsKey(val))
			{
				_saved[val] = val.BoxedValue;
			}
			_config.SaveOnConfigSet = false;
			try
			{
				val.BoxedValue = value;
			}
			catch (Exception ex)
			{
				Fail("cambiar " + key, ex);
				return false;
			}
			return true;
		}

		public void RestoreTemp()
		{
			if (_saved.Count == 0 || _config == null)
			{
				return;
			}
			try
			{
				foreach (KeyValuePair<ConfigEntryBase, object> item in _saved)
				{
					item.Key.BoxedValue = item.Value;
				}
			}
			catch (Exception ex)
			{
				Fail("restaurar su configuración", ex);
			}
			finally
			{
				_saved.Clear();
				_config.SaveOnConfigSet = _savedSaveOnSet;
			}
		}

		private void Resolve()
		{
			if (_resolved)
			{
				return;
			}
			if (!Chainloader.PluginInfos.TryGetValue("vsm.horizon", out var value) || value == null || (Object)(object)value.Instance == (Object)null)
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					_resolved = true;
				}
				return;
			}
			_resolved = true;
			try
			{
				_config = value.Instance.Config;
				foreach (ConfigDefinition key in _config.Keys)
				{
					switch (key.Key)
					{
					case "Enabled":
						_enabled = _config[key];
						break;
					case "Quality":
						_quality = _config[key];
						break;
					case "Adaptive":
						_adaptive = _config[key];
						break;
					case "TargetFps":
						_targetFps = _config[key];
						break;
					case "MinLoadedSeconds":
						_minLoaded = _config[key];
						break;
					}
				}
				_controllerType = ((object)value.Instance).GetType().Assembly.GetType("VSM.Horizon.Runtime.HorizonController", throwOnError: false);
				if (_controllerType != null)
				{
					_instanceProp = AccessTools.Property(_controllerType, "Instance");
					_scaleProp = AccessTools.Property(_controllerType, "Scale");
					_rendererField = AccessTools.Field(_controllerType, "_renderer");
					if (_rendererField != null)
					{
						_drawnProp = AccessTools.Property(_rendererField.FieldType, "DrawnInstances");
						_drawCallsProp = AccessTools.Property(_rendererField.FieldType, "DrawCalls");
					}
				}
				PerfPlugin.Log.LogInfo((object)("vsm.perf: MEL Horizons " + value.Metadata.Version?.ToString() + " detectado; integración " + (Wanted ? "encendida" : "apagada por config") + "."));
			}
			catch (Exception ex)
			{
				Fail("detectarlo", ex);
			}
		}

		private void Fail(string what, Exception ex)
		{
			if (!_failed)
			{
				_failed = true;
				PerfPlugin.Log.LogWarning((object)("vsm.perf: integración con MEL Horizons apagada (no se pudo " + what + "): " + ex.Message));
			}
		}

		private static string Value(ConfigEntryBase e)
		{
			if (e != null && e.BoxedValue != null)
			{
				return Convert.ToString(e.BoxedValue, CultureInfo.InvariantCulture);
			}
			return "?";
		}
	}
	internal static class PerfCommands
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__1_0;

			public static Action<BenchResult> <>9__4_0;

			public static Action<string> <>9__4_1;

			internal void <Register>b__1_0(ConsoleEventArgs args)
			{
				PerfController instance = PerfController.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					args.Context.AddString("vsm.perf: no está activo en este proceso.");
					return;
				}
				string sub = ((args.Length > 1) ? args.Args[1].ToLowerInvariant() : string.Empty);
				try
				{
					Run(instance, sub, args);
				}
				catch (Exception ex)
				{
					args.Context.AddString("vsm.perf: error: " + ex.Message);
					PerfPlugin.Log.LogWarning((object)("vsm.perf: error en el comando: " + ex));
				}
			}

			internal void <BenchCmd>b__4_0(BenchResult r)
			{
				PerfController.Notify(PerfLocalization.T("vsm_perf_bench_done", r.Point, r.Frames.AvgFps.ToString("0.0", CultureInfo.InvariantCulture), r.Frames.Low1Fps.ToString("0.0", CultureInfo.InvariantCulture)) + (r.SettleTimedOut ? " (ojo: quedaban objetos por crear)" : string.Empty));
			}

			internal void <BenchCmd>b__4_1(string reason)
			{
				PerfController.Notify(PerfLocalization.T("vsm_perf_bench_cancel", reason));
			}
		}

		private static readonly string[] Help = new string[12]
		{
			"vsm_perf                       estado (mejoras, horizon, picos, contadores)", "vsm_perf on|off                enciende o apaga el mod (= Ajustes → Accesibilidad)", "vsm_perf hud                   muestra/oculta el medidor (también Ctrl+F9)", "vsm_perf contadores            qué contadores del motor hay en este build (y los guarda en un .txt)", "vsm_perf prof on|off|reporte|reset   instrumentador: ms por sistema del juego", "vsm_perf bench marcar <punto>  guarda tu posición y hacia dónde miras", "vsm_perf bench puntos          lista los puntos guardados", "vsm_perf bench <punto> [s]     benchmark reproducible desde ese punto (quieto, sin tocar nada)", "vsm_perf bench parar           corta el benchmark o A/B en curso", "vsm_perf ab <mejora> <punto> [s] [rondas]   A/B con secuencia ABBA (sin guardar la config)",
			"vsm_perf diff <mejora>         captura A y B en el mismo frame y cuenta píxeles distintos", "vsm_perf dump                  volcado de cómo se dibujan las piezas y de la base alrededor"
		};

		public static void Register()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			object obj = <>c.<>9__1_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					PerfController instance = PerfController.Instance;
					if ((Object)(object)instance == (Object)null)
					{
						args.Context.AddString("vsm.perf: no está activo en este proceso.");
						return;
					}
					string sub = ((args.Length > 1) ? args.Args[1].ToLowerInvariant() : string.Empty);
					try
					{
						Run(instance, sub, args);
					}
					catch (Exception ex)
					{
						args.Context.AddString("vsm.perf: error: " + ex.Message);
						PerfPlugin.Log.LogWarning((object)("vsm.perf: error en el comando: " + ex));
					}
				};
				<>c.<>9__1_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("vsm_perf", "MEL Performance: vsm_perf ayuda", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		private static void Run(PerfController ctl, string sub, ConsoleEventArgs args)
		{
			Terminal context = args.Context;
			if (!(sub == "on"))
			{
				if (sub == "off")
				{
					PerfPlugin.Enabled.Value = false;
					context.AddString("vsm.perf: apagado: el juego queda como sin el mod.");
					return;
				}
				if (!PerfPlugin.On && sub != string.Empty && sub != "estado" && sub != "status" && sub != "ayuda" && sub != "help")
				{
					context.AddString("vsm.perf: MEL Performance está apagado. Enciéndelo en Ajustes → Accesibilidad → \"MEL Performance (más FPS)\" o con vsm_perf on.");
					return;
				}
				switch (sub)
				{
				case "estado":
				case "status":
				case "":
				{
					foreach (string item in Status(ctl))
					{
						context.AddString(item);
						PerfPlugin.Log.LogInfo((object)item);
					}
					break;
				}
				case "ayuda":
				case "help":
				{
					string[] help = Help;
					foreach (string text in help)
					{
						context.AddString(text);
					}
					context.AddString("mejoras para ab/diff:");
					{
						foreach (string item2 in ctl.Toggles.Help())
						{
							context.AddString(item2);
						}
						break;
					}
				}
				case "hud":
					ctl.ToggleOverlay();
					break;
				case "contadores":
				case "counters":
				{
					List<string> list = ctl.Counters.Describe();
					Directory.CreateDirectory(PerfPlugin.DataDir);
					string text2 = Path.Combine(PerfPlugin.DataDir, "contadores.txt");
					File.WriteAllLines(text2, list.ToArray());
					foreach (string item3 in list.Take(8))
					{
						context.AddString(item3);
					}
					context.AddString("lista completa en " + text2);
					break;
				}
				case "prof":
					Prof(ctl, context, (args.Length > 2) ? args.Args[2].ToLowerInvariant() : "reporte");
					break;
				case "bench":
					BenchCmd(ctl, context, args);
					break;
				case "ab":
				{
					if (args.Length < 4)
					{
						context.AddString("uso: vsm_perf ab <mejora> <punto> [segundos] [rondas]. Mejoras:");
						{
							foreach (string item4 in ctl.Toggles.Help())
							{
								context.AddString(item4);
							}
							break;
						}
					}
					if (!BenchRunner.TryGetPoint(args.Args[3], out var point))
					{
						context.AddString("no hay un punto «" + args.Args[3] + "» (vsm_perf bench marcar " + args.Args[3] + ").");
					}
					else
					{
						float seconds = ((args.Length > 4) ? ParseFloat(args.Args[4], 20f) : 20f);
						int val = ((args.Length <= 5) ? 1 : ((int)ParseFloat(args.Args[5], 1f)));
						context.AddString(ctl.StartAb(args.Args[2], point, seconds, Math.Max(1, Math.Min(5, val))));
					}
					break;
				}
				case "diff":
					context.AddString(ctl.StartDiff((args.Length > 2) ? args.Args[2] : string.Empty));
					break;
				case "dump":
					context.AddString("vsm.perf: volcado en " + Dump.Write(ctl.Horizon));
					break;
				default:
					context.AddString("vsm.perf: subcomando desconocido. vsm_perf ayuda");
					break;
				}
			}
			else
			{
				PerfPlugin.Enabled.Value = true;
				context.AddString("vsm.perf: encendido (queda guardado; también en Ajustes → Accesibilidad).");
			}
		}

		private static void Prof(PerfController ctl, Terminal t, string op)
		{
			switch (op)
			{
			case "on":
			{
				string text = PerfPlugin.ProfilerExtraTargets.Value ?? string.Empty;
				t.AddString("vsm.perf: " + ctl.Profiler.Start(text.Split(new char[1] { ',' }), ctl.Horizon.HorizonControllerType));
				t.AddString("  (tiempos inclusivos: un sistema incluye lo que llama; mira el medidor con Ctrl+F9)");
				return;
			}
			case "off":
				ctl.Profiler.Stop();
				t.AddString("vsm.perf: instrumentador apagado.");
				return;
			case "reset":
				ctl.Profiler.Reset();
				t.AddString("vsm.perf: instrumentador a cero.");
				return;
			}
			if (!ctl.Profiler.Active && ctl.Profiler.WindowFrames == 0)
			{
				t.AddString("vsm.perf: el instrumentador está apagado (vsm_perf prof on).");
				return;
			}
			foreach (SystemProfiler.Row item in ctl.Profiler.Top(15))
			{
				string[] obj = new string[6] { "  ", null, null, null, null, null };
				double avgMs = item.AvgMs;
				obj[1] = avgMs.ToString("0.000", CultureInfo.InvariantCulture);
				obj[2] = " ms/frame (máx ";
				avgMs = item.MaxMs;
				obj[3] = avgMs.ToString("0.0", CultureInfo.InvariantCulture);
				obj[4] = ")  ";
				obj[5] = item.Key;
				t.AddString(string.Concat(obj));
			}
			t.AddString("reporte completo: " + ctl.Profiler.WriteReport(PerfPlugin.DataDir));
		}

		private static void BenchCmd(PerfController ctl, Terminal t, ConsoleEventArgs args)
		{
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			string text = ((args.Length > 2) ? args.Args[2] : string.Empty);
			switch (text.ToLowerInvariant())
			{
			case "":
				t.AddString("uso: vsm_perf bench marcar <punto> | puntos | parar | <punto> [segundos]");
				return;
			case "marcar":
			case "mark":
				t.AddString("vsm.perf: " + BenchRunner.MarkPoint((args.Length > 3) ? args.Args[3] : string.Empty));
				return;
			case "points":
			case "puntos":
			{
				foreach (BenchPoint item in BenchRunner.LoadPoints())
				{
					string[] obj = new string[6] { "  ", item.Name, "  ", null, null, null };
					Vector3 position = item.Position;
					obj[3] = ((Vector3)(ref position)).ToString("F1");
					obj[4] = "  giro ";
					float yaw = item.Yaw;
					obj[5] = yaw.ToString("0", CultureInfo.InvariantCulture);
					t.AddString(string.Concat(obj));
				}
				return;
			}
			case "stop":
			case "parar":
				ctl.Bench.Cancel("pedido con vsm_perf bench parar");
				return;
			}
			if (!BenchRunner.TryGetPoint(text, out var point))
			{
				t.AddString("no hay un punto «" + text + "» (vsm_perf bench marcar " + text + ").");
				return;
			}
			float seconds = ((args.Length > 3) ? ParseFloat(args.Args[3], PerfPlugin.BenchSeconds.Value) : ((float)PerfPlugin.BenchSeconds.Value));
			t.AddString("vsm.perf: " + ctl.Bench.Start(point, seconds, string.Empty, delegate(BenchResult r)
			{
				PerfController.Notify(PerfLocalization.T("vsm_perf_bench_done", r.Point, r.Frames.AvgFps.ToString("0.0", CultureInfo.InvariantCulture), r.Frames.Low1Fps.ToString("0.0", CultureInfo.InvariantCulture)) + (r.SettleTimedOut ? " (ojo: quedaban objetos por crear)" : string.Empty));
			}, delegate(string reason)
			{
				PerfController.Notify(PerfLocalization.T("vsm_perf_bench_cancel", reason));
			}));
		}

		private static IEnumerable<string> Status(PerfController ctl)
		{
			CultureInfo inv = CultureInfo.InvariantCulture;
			yield return "MEL Performance 0.1.0 · " + (PerfPlugin.On ? "ENCENDIDO" : "APAGADO (vsm_perf on o Ajustes → Accesibilidad)") + " · Valheim " + Env.GameVersion + " · " + PerfController.SystemSummary();
			yield return "  fila en Accesibilidad: " + SettingsRow.LastResult;
			yield return "  mejoras: " + Toggles.Codes();
			yield return "  render: " + ctl.Tweaks.Describe() + " · luces " + ctl.Shadows.LightsOn + "/" + ctl.Shadows.LightsTotal + " · con sombra " + ctl.Shadows.GameShadowed + " (quitadas " + ctl.Shadows.Suppressed + ")";
			yield return "  carga de objetos: por crear " + SpawnControl.PendingNear + " · ticks recortados " + SpawnControl.Budget.ThrottledTicks + " · atraso " + SpawnControl.Budget.Deficit.ToString("0", inv) + " obj · " + SpawnControl.Budget.TickRate.ToString("0", inv) + " ticks/s";
			yield return "  limpieza de assets: " + (AssetUnload.Policy.Pending ? "aplazada (esperando un momento tranquilo)" : "nada pendiente");
			yield return "  horizon: " + ctl.Horizon.Describe();
			yield return "  hilos de render: " + PerfController.ThreadingMode() + " · " + PerfController.GraphicsSummary();
			if (SafePatch.IsDegraded)
			{
				yield return "  MODO DEGRADADO, parches que fallaron: " + string.Join("; ", SafePatch.FailedTargets.ToArray());
			}
			yield return "  datos en " + PerfPlugin.DataDir + " · vsm_perf ayuda";
		}

		private static float ParseFloat(string s, float fallback)
		{
			if (!float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return fallback;
			}
			return result;
		}
	}
	internal sealed class PerfController : MonoBehaviour
	{
		private sealed class AbRun
		{
			private readonly PerfController _ctl;

			private readonly string _toggle;

			private readonly BenchPoint _point;

			private readonly float _seconds;

			private readonly bool[] _sequence;

			private readonly List<double> _fpsA = new List<double>();

			private readonly List<double> _fpsB = new List<double>();

			private readonly List<double> _lowA = new List<double>();

			private readonly List<double> _lowB = new List<double>();

			private int _index;

			public bool Started { get; private set; }

			public AbRun(PerfController ctl, string toggle, BenchPoint point, float seconds, int rounds)
			{
				_ctl = ctl;
				_toggle = toggle;
				_point = point;
				_seconds = seconds;
				_sequence = AbSchedule.Sequence(rounds);
			}

			public string Start()
			{
				_ctl.Toggles.BeginSession();
				string result = Next();
				Started = _ctl.Bench.Running;
				if (!Started)
				{
					_ctl.Toggles.EndSession();
				}
				return result;
			}

			private string Next()
			{
				if (_index >= _sequence.Length)
				{
					Finish();
					return string.Empty;
				}
				bool flag = _sequence[_index];
				if (!_ctl.Toggles.Set(_toggle, flag, out var error))
				{
					Abort(error);
					return "A/B: " + error;
				}
				_ctl.Shadows.Invalidate();
				_ctl.Tweaks.Apply();
				string label = (flag ? "B" : "A") + (_index + 1).ToString(CultureInfo.InvariantCulture) + "-" + _toggle;
				string text = _ctl.Bench.Start(_point, _seconds, label, OnDone, OnCancel);
				if (!_ctl.Bench.Running && _index > 0)
				{
					Abort(text);
				}
				return "A/B «" + _toggle + "» (" + _sequence.Length + " corridas): " + text;
			}

			private void OnDone(BenchResult r)
			{
				bool flag = _sequence[_index];
				(flag ? _fpsB : _fpsA).Add(r.Frames.AvgFps);
				(flag ? _lowB : _lowA).Add(r.Frames.Low1Fps);
				Print("vsm.perf A/B: corrida " + (_index + 1) + "/" + _sequence.Length + " (" + (flag ? "B" : "A") + "): " + r.Frames.AvgFps.ToString("0.0", CultureInfo.InvariantCulture) + " FPS, 1% low " + r.Frames.Low1Fps.ToString("0.0", CultureInfo.InvariantCulture));
				_index++;
				Next();
			}

			private void OnCancel(string reason)
			{
				Abort(reason);
			}

			private void Abort(string reason)
			{
				Notify(PerfLocalization.T("vsm_perf_bench_cancel", reason));
				_ctl.Toggles.EndSession();
				_ctl.AbFinished();
			}

			private void Finish()
			{
				AbResult abResult = AbSchedule.Compare(_fpsA, _fpsB);
				AbResult abResult2 = AbSchedule.Compare(_lowA, _lowB);
				CultureInfo invariantCulture = CultureInfo.InvariantCulture;
				Notify("vsm.perf A/B «" + _toggle + "»: A " + abResult.MeanA.ToString("0.0", invariantCulture) + " → B " + abResult.MeanB.ToString("0.0", invariantCulture) + " FPS (" + ((abResult.DeltaPct >= 0.0) ? "+" : string.Empty) + abResult.DeltaPct.ToString("0.0", invariantCulture) + " % ± " + abResult.NoisePct.ToString("0.0", invariantCulture) + ") · 1% low " + ((abResult2.DeltaPct >= 0.0) ? "+" : string.Empty) + abResult2.DeltaPct.ToString("0.0", invariantCulture) + " %");
				_ctl.Toggles.EndSession();
				_ctl.AbFinished();
			}
		}

		public readonly FrameWindow Window = new FrameWindow(10.0);

		public Counters Counters;

		public ShadowGovernor Shadows;

		public SceneTweaks Tweaks;

		public SystemProfiler Profiler;

		public HorizonBridge Horizon;

		public Toggles Toggles;

		public BenchRunner Bench;

		private RenderOverrides _overrides;

		private AbRun _ab;

		private bool _overlay;

		private string _overlayText = string.Empty;

		private Vector2 _overlaySize;

		private GUIStyle _style;

		private float _nextOverlay;

		private float _nextSecond;

		public static PerfController Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
			Horizon = new HorizonBridge();
			SpawnControl.Horizon = Horizon;
			Counters = new Counters();
			Shadows = new ShadowGovernor();
			_overrides = new RenderOverrides(Shadows);
			Tweaks = new SceneTweaks();
			Profiler = new SystemProfiler();
			Toggles = new Toggles(Horizon);
			Bench = new BenchRunner(Counters, Toggles, Horizon);
			_overlay = PerfPlugin.On && PerfPlugin.OverlayOnStart.Value;
			Counters.SetActive(PerfPlugin.On);
			PerfPlugin.PointShadowBudget.SettingChanged += delegate
			{
				Shadows.Invalidate();
			};
			PerfPlugin.Enabled.SettingChanged += OnEnabledChanged;
			((MonoBehaviour)this).StartCoroutine(EndOfFrameLoop());
		}

		private void OnEnabledChanged(object sender, EventArgs e)
		{
			bool flag = PerfPlugin.On;
			try
			{
				Counters.SetActive(flag);
				if (!flag)
				{
					_overlay = false;
					Profiler.Stop();
					Bench.Cancel("MEL Performance apagado");
					Window.Clear();
				}
				Shadows.Invalidate();
				Tweaks.Apply();
				SettingsRow.SyncFromConfig();
			}
			catch (Exception ex)
			{
				PerfPlugin.Log.LogWarning((object)("vsm.perf: al " + (flag ? "encender" : "apagar") + ": " + ex.Message));
			}
			PerfPlugin.Log.LogInfo((object)("vsm.perf: " + (flag ? "encendido." : "apagado.")));
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				((Character)localPlayer).Message((MessageType)1, PerfLocalization.T(flag ? "vsm_perf_enabled_msg" : "vsm_perf_disabled_msg"), 0, (Sprite)null, false);
			}
		}

		private void Update()
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			float unscaledTime = Time.unscaledTime;
			_overrides.SafetyRestore();
			if (!PerfPlugin.On)
			{
				if (unscaledTime >= _nextSecond)
				{
					_nextSecond = unscaledTime + 1f;
					try
					{
						Tweaks.Apply();
						AssetUnload.Tick();
						return;
					}
					catch (Exception)
					{
						return;
					}
				}
				return;
			}
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			Window.Add(unscaledDeltaTime * 1000f);
			try
			{
				Counters.Sample();
			}
			catch (Exception)
			{
			}
			if (Env.Pressed(PerfPlugin.OverlayKey.Value) && !Typing())
			{
				_overlay = !_overlay;
			}
			try
			{
				Bench.Tick(unscaledDeltaTime);
			}
			catch (Exception ex3)
			{
				PerfPlugin.Log.LogWarning((object)("vsm.perf: error en el benchmark: " + ex3));
				Bench.Cancel("error: " + ex3.Message);
			}
			if (unscaledTime >= _nextSecond)
			{
				_nextSecond = unscaledTime + 1f;
				try
				{
					Tweaks.Apply();
					AssetUnload.Tick();
				}
				catch (Exception ex4)
				{
					PerfPlugin.Log.LogWarning((object)("vsm.perf: " + ex4.Message));
				}
			}
			if (_overlay && unscaledTime >= _nextOverlay)
			{
				_nextOverlay = unscaledTime + 0.5f;
				_overlayText = BuildOverlay();
				_overlaySize = Vector2.zero;
			}
		}

		private IEnumerator EndOfFrameLoop()
		{
			WaitForEndOfFrame wait = new WaitForEndOfFrame();
			while (true)
			{
				yield return wait;
				Profiler.EndFrame();
			}
		}

		private void OnGUI()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			if (_overlay && !string.IsNullOrEmpty(_overlayText))
			{
				if (_style == null)
				{
					_style = new GUIStyle(GUI.skin.label)
					{
						fontSize = 13,
						wordWrap = false
					};
					_style.normal.textColor = Color.white;
				}
				if (_overlaySize == Vector2.zero)
				{
					_overlaySize = _style.CalcSize(new GUIContent(_overlayText));
				}
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(10f, 10f, _overlaySize.x + 16f, _overlaySize.y + 12f);
				Color color = GUI.color;
				GUI.color = new Color(0f, 0f, 0f, 0.6f);
				GUI.DrawTexture(val, (Texture)(object)Texture2D.whiteTexture);
				GUI.color = color;
				GUI.Label(new Rect(((Rect)(ref val)).x + 8f, ((Rect)(ref val)).y + 6f, _overlaySize.x, _overlaySize.y), _overlayText, _style);
			}
		}

		private void OnDestroy()
		{
			try
			{
				PerfPlugin.Enabled.SettingChanged -= OnEnabledChanged;
				Bench.Cancel("cerrando el juego");
				Profiler.Stop();
				_overrides.Dispose();
				Tweaks.Dispose();
				Counters.Dispose();
			}
			catch (Exception)
			{
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		public void ToggleOverlay()
		{
			_overlay = PerfPlugin.On && !_overlay;
			_nextOverlay = 0f;
		}

		private static bool Typing()
		{
			Chat instance = Chat.instance;
			if (!Console.IsVisible() && !TextInput.IsVisible())
			{
				if ((Object)(object)instance != (Object)null)
				{
					return instance.HasFocus();
				}
				return false;
			}
			return true;
		}

		private string BuildOverlay()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			string text = PerfLocalization.T("vsm_perf_na");
			FrameSummary frameSummary = Window.Summarize();
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("MEL Performance 0.1.0 · " + ((object)SystemInfo.graphicsDeviceType/*cast due to .constrained prefix*/).ToString() + " · " + ThreadingMode());
			stringBuilder.AppendLine(PerfLocalization.T("vsm_perf_hud_fps", frameSummary.AvgFps.ToString("0", invariantCulture), frameSummary.AvgMs.ToString("0.0", invariantCulture), frameSummary.Low1Fps.ToString("0", invariantCulture), frameSummary.Low01Fps.ToString("0", invariantCulture), frameSummary.WorstMs.ToString("0", invariantCulture)));
			stringBuilder.AppendLine(PerfLocalization.T("vsm_perf_hud_cpu", Ms(Counters.CpuMainMs, text), Ms(Counters.CpuRenderMs, text), Ms(Counters.GpuMs, text)));
			stringBuilder.AppendLine(PerfLocalization.T("vsm_perf_hud_draw", Num(Counters[0], text), Num(Counters[1], text), Num(Counters[2], text), Big(Counters[3], text), Num(Counters[4], text), double.IsNaN(Counters[5]) ? text : (Counters[5].ToString("0", invariantCulture) + " KB")));
			ZNetScene instance = ZNetScene.instance;
			stringBuilder.AppendLine(PerfLocalization.T("vsm_perf_hud_scene", WearNTear.GetAllInstances().Count, ((Object)(object)instance != (Object)null) ? instance.m_instances.Count : 0, SpawnControl.PendingNear, Shadows.LightsOn, Shadows.LightsTotal, Shadows.GameShadowed - Shadows.Suppressed, Shadows.Suppressed));
			if (Horizon.Present)
			{
				stringBuilder.AppendLine(PerfLocalization.T("vsm_perf_hud_horizon", Horizon.Describe()));
			}
			stringBuilder.AppendLine(PerfLocalization.T("vsm_perf_hud_toggles", Toggles.Codes()));
			if (Bench.Running)
			{
				stringBuilder.AppendLine(Bench.Status);
			}
			if (Profiler.Active)
			{
				stringBuilder.AppendLine(PerfLocalization.T("vsm_perf_hud_prof"));
				foreach (SystemProfiler.Row item in Profiler.Top(8))
				{
					string[] obj = new string[6] { "  ", null, null, null, null, null };
					double avgMs = item.AvgMs;
					obj[1] = avgMs.ToString("0.00", invariantCulture);
					obj[2] = "  (";
					avgMs = item.MaxMs;
					obj[3] = avgMs.ToString("0.0", invariantCulture);
					obj[4] = ")  ";
					obj[5] = item.Key;
					stringBuilder.AppendLine(string.Concat(obj));
				}
			}
			return stringBuilder.ToString().TrimEnd(Array.Empty<char>());
		}

		private static string Ms(double v, string na)
		{
			if (!double.IsNaN(v) && !(v <= 0.0))
			{
				return v.ToString("0.0", CultureInfo.InvariantCulture) + " ms";
			}
			return na;
		}

		private static string Num(double v, string na)
		{
			if (!double.IsNaN(v))
			{
				return v.ToString("0", CultureInfo.InvariantCulture);
			}
			return na;
		}

		private static string Big(double v, string na)
		{
			if (double.IsNaN(v))
			{
				return na;
			}
			if (!(v >= 1000000.0))
			{
				return (v / 1000.0).ToString("0", CultureInfo.InvariantCulture) + "k";
			}
			return (v / 1000000.0).ToString("0.0", CultureInfo.InvariantCulture) + "M";
		}

		public static string ThreadingMode()
		{
			try
			{
				object obj = ((typeof(SystemInfo).GetProperty("renderingThreadingMode") != null) ? typeof(SystemInfo).GetProperty("renderingThreadingMode").GetValue(null, null) : null);
				return (obj != null) ? obj.ToString() : (SystemInfo.graphicsMultiThreaded ? "multihilo" : "un hilo");
			}
			catch (Exception)
			{
				return "?";
			}
		}

		public static string GraphicsSummary()
		{
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			return Screen.width + "x" + Screen.height + " · sombras " + QualitySettings.shadowDistance.ToString("0", invariantCulture) + " m/" + QualitySettings.shadowCascades + " cascadas · lodBias " + QualitySettings.lodBias.ToString("0.##", invariantCulture) + " · luces px " + QualitySettings.pixelLightCount + " · luces " + LightLod.m_lightLimit + " sombras puntuales " + LightLod.m_shadowLimit + " · vsync " + QualitySettings.vSyncCount + " · límite FPS " + Application.targetFrameRate;
		}

		public static string SystemSummary()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			return SystemInfo.graphicsDeviceName + " (" + SystemInfo.graphicsMemorySize + " MB, " + ((object)SystemInfo.graphicsDeviceType/*cast due to .constrained prefix*/).ToString() + ") · " + SystemInfo.processorType + " (" + SystemInfo.processorCount + " hilos) · RAM " + SystemInfo.systemMemorySize + " MB";
		}

		public string StartAb(string toggle, BenchPoint point, float seconds, int rounds)
		{
			if (_ab != null || Bench.Running)
			{
				return "ya hay un benchmark o A/B corriendo (vsm_perf bench parar).";
			}
			if (!Toggles.Exists(toggle))
			{
				return "no existe la mejora «" + toggle + "».";
			}
			AbRun abRun = (_ab = new AbRun(this, toggle, point, seconds, rounds));
			string result = abRun.Start();
			if (!abRun.Started && _ab == abRun)
			{
				_ab = null;
			}
			return result;
		}

		internal void AbFinished()
		{
			_ab = null;
		}

		public static void Print(string msg)
		{
			PerfPlugin.Log.LogInfo((object)msg);
			try
			{
				if ((Object)(object)Console.instance != (Object)null)
				{
					((Terminal)Console.instance).AddString(msg);
				}
			}
			catch (Exception)
			{
			}
		}

		public static void Notify(string msg)
		{
			Print(msg);
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				((Character)localPlayer).Message((MessageType)1, msg, 0, (Sprite)null, false);
			}
		}

		public string StartDiff(string toggle)
		{
			if (!Toggles.Exists(toggle) || string.Equals(toggle, "horizon", StringComparison.OrdinalIgnoreCase))
			{
				return "uso: vsm_perf diff <mejora> (sombras, resolucion, depthnormals, solchicos, soldistancia).";
			}
			if ((Object)(object)RenderOverrides.MainCamera == (Object)null)
			{
				return "hay que estar en el mundo.";
			}
			((MonoBehaviour)this).StartCoroutine(DiffRoutine(toggle));
			return "capturando A y B en el mismo frame…";
		}

		private IEnumerator DiffRoutine(string toggle)
		{
			yield return (object)new WaitForEndOfFrame();
			Camera mainCamera = RenderOverrides.MainCamera;
			if ((Object)(object)mainCamera == (Object)null)
			{
				yield break;
			}
			Texture2D val = null;
			Texture2D val2 = null;
			string error = null;
			Toggles.BeginSession();
			try
			{
				if (!Toggles.Set(toggle, b: false, out error))
				{
					yield break;
				}
				Shadows.Invalidate();
				Tweaks.Apply();
				val = Capture(mainCamera);
				Toggles.Set(toggle, b: true, out error);
				Shadows.Invalidate();
				Tweaks.Apply();
				val2 = Capture(mainCamera);
			}
			finally
			{
				Toggles.EndSession();
				Shadows.Invalidate();
				Tweaks.Apply();
				if (error != null)
				{
					Print("vsm.perf diff: " + error);
				}
			}
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null))
			{
				byte[] rawTextureData = val.GetRawTextureData();
				byte[] rawTextureData2 = val2.GetRawTextureData();
				byte[] array = new byte[rawTextureData.Length];
				int num = PixelDiff.CountDifferent(rawTextureData, rawTextureData2, 8, array);
				int num2 = rawTextureData.Length / 4;
				Texture2D val3 = new Texture2D(((Texture)val).width, ((Texture)val).height, (TextureFormat)4, false);
				val3.LoadRawTextureData(array);
				val3.Apply(false);
				string text = Path.Combine(PerfPlugin.DataDir, "diff");
				Directory.CreateDirectory(text);
				string text2 = Path.Combine(text, toggle + "_" + DateTime.Now.ToString("yyyyMMdd_HHmmss", CultureInfo.InvariantCulture));
				File.WriteAllBytes(text2 + "_A.png", ImageConversion.EncodeToPNG(val));
				File.WriteAllBytes(text2 + "_B.png", ImageConversion.EncodeToPNG(val2));
				File.WriteAllBytes(text2 + "_dif.png", ImageConversion.EncodeToPNG(val3));
				Object.Destroy((Object)(object)val);
				Object.Destroy((Object)(object)val2);
				Object.Destroy((Object)(object)val3);
				double num3 = ((num2 > 0) ? ((double)num * 100.0 / (double)num2) : 0.0);
				Notify("vsm.perf diff " + toggle + ": " + num3.ToString("0.00", CultureInfo.InvariantCulture) + " % de píxeles distintos (" + text2 + "_*.png)");
			}
		}

		private static Texture2D Capture(Camera cam)
		{
			//IL_0048: 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: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			int pixelWidth = cam.pixelWidth;
			int pixelHeight = cam.pixelHeight;
			RenderTexture temporary = RenderTexture.GetTemporary(pixelWidth, pixelHeight, 24, (RenderTextureFormat)0);
			RenderTexture targetTexture = cam.targetTexture;
			RenderTexture active = RenderTexture.active;
			try
			{
				cam.targetTexture = temporary;
				cam.Render();
			}
			finally
			{
				cam.targetTexture = targetTexture;
			}
			RenderTexture.active = temporary;
			Texture2D val = new Texture2D(pixelWidth, pixelHeight, (TextureFormat)4, false);
			val.ReadPixels(new Rect(0f, 0f, (float)pixelWidth, (float)pixelHeight), 0, 0, false);
			val.Apply(false);
			RenderTexture.active = active;
			RenderTexture.ReleaseTemporary(temporary);
			return val;
		}
	}
	internal sealed class RenderOverrides : IDisposable
	{
		private readonly ShadowGovernor _shadows;

		private bool _applied;

		private bool _failedOnce;

		public static Camera MainCamera
		{
			get
			{
				GameCamera instance = GameCamera.instance;
				if (!((Object)(object)instance != (Object)null))
				{
					return null;
				}
				return instance.m_camera;
			}
		}

		public RenderOverrides(ShadowGovernor shadows)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			_shadows = shadows;
			Camera.onPreCull = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPreCull, (Delegate?)new CameraCallback(OnPreCull));
			Camera.onPostRender = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPostRender, (Delegate?)new CameraCallback(OnPostRender));
		}

		private void OnPreCull(Camera cam)
		{
			if ((Object)(object)cam == (Object)null || (Object)(object)cam != (Object)(object)MainCamera)
			{
				return;
			}
			try
			{
				_shadows.Apply(cam);
				_applied = true;
			}
			catch (Exception ex)
			{
				Fail(ex);
			}
		}

		private void OnPostRender(Camera cam)
		{
			if (_applied && !((Object)(object)cam == (Object)null) && !((Object)(object)cam != (Object)(object)MainCamera))
			{
				try
				{
					_shadows.Restore();
				}
				catch (Exception ex)
				{
					Fail(ex);
				}
				_applied = false;
			}
		}

		public void SafetyRestore()
		{
			if (_applied)
			{
				_shadows.Restore();
				_applied = false;
			}
		}

		private void Fail(Exception ex)
		{
			if (!_failedOnce)
			{
				_failedOnce = true;
				PerfPlugin.Log.LogWarning((object)("vsm.perf: error en los ajustes de render (se sigue intentando sin repetir este aviso): " + ex));
			}
		}

		public void Dispose()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			Camera.onPreCull = (CameraCallback)Delegate.Remove((Delegate?)(object)Camera.onPreCull, (Delegate?)new CameraCallback(OnPreCull));
			Camera.onPostRender = (CameraCallback)Delegate.Remove((Delegate?)(object)Camera.onPostRender, (Delegate?)new CameraCallback(OnPostRender));
			SafetyRestore();
		}
	}
	internal sealed class SceneTweaks : IDisposable
	{
		private static readonly string[] SmallLayers = new string[3] { "item", "Default_small", "piece_nonsolid" };

		private Light _sunApplied;

		private float[] _sunOriginal;

		private bool _shadowDistanceOverridden;

		private float _vanillaShadowDistance;

		private Camera _depthCam;

		private bool _depthChanged;

		private bool _depthHadDepth;

		public SceneTweaks()
		{
			GraphicsSettingsManager.GraphicsSettingsChanged += OnGraphicsSettingsChanged;
			PerfPlugin.Enabled.SettingChanged += OnConfigChanged;
			PerfPlugin.SunSmallShadowCull.SettingChanged += OnConfigChanged;
			PerfPlugin.SunShadowDistance.SettingChanged += OnConfigChanged;
			PerfPlugin.DepthNormalsOff.SettingChanged += OnConfigChanged;
		}

		public void Apply()
		{
			ApplySun();
			ApplyShadowDistance();
			ApplyDepth();
		}

		private void OnGraphicsSettingsChanged()
		{
			_shadowDistanceOverridden = false;
			Apply();
		}

		private void OnConfigChanged(object sender, EventArgs e)
		{
			Apply();
		}

		private void ApplySun()
		{
			EnvMan instance = EnvMan.instance;
			Light val = (((Object)(object)instance != (Object)null) ? instance.m_dirLight : null);
			float num = (PerfPlugin.On ? PerfPlugin.SunSmallShadowCull.Value : 0f);
			if ((Object)(object)_sunApplied != (Object)null && ((Object)(object)_sunApplied != (Object)(object)val || num <= 0f))
			{
				_sunApplied.layerShadowCullDistances = _sunOriginal ?? new float[32];
				_sunApplied = null;
				_sunOriginal = null;
			}
			if ((Object)(object)val == (Object)null || num <= 0f)
			{
				return;
			}
			if ((Object)(object)_sunApplied == (Object)null)
			{
				float[] layerShadowCullDistances = val.layerShadowCullDistances;
				_sunOriginal = ((layerShadowCullDistances != null && layerShadowCullDistances.Length == 32) ? ((float[])layerShadowCullDistances.Clone()) : null);
				_sunApplied = val;
			}
			float[] array = ((_sunOriginal != null) ? ((float[])_sunOriginal.Clone()) : new float[32]);
			string[] smallLayers = SmallLayers;
			for (int i = 0; i < smallLayers.Length; i++)
			{
				int num2 = LayerMask.NameToLayer(smallLayers[i]);
				if (num2 >= 0 && num2 < 32)
				{
					array[num2] = ((array[num2] > 0f) ? Mathf.Min(array[num2], num) : num);
				}
			}
			val.layerShadowCullDistances = array;
		}

		private void ApplyShadowDistance()
		{
			float num = (PerfPlugin.On ? PerfPlugin.SunShadowDistance.Value : 0f);
			if (num <= 0f)
			{
				if (_shadowDistanceOverridden)
				{
					QualitySettings.shadowDistance = _vanillaShadowDistance;
					_shadowDistanceOverridden = false;
				}
				return;
			}
			if (!_shadowDistanceOverridden)
			{
				_vanillaShadowDistance = QualitySettings.shadowDistance;
				_shadowDistanceOverridden = true;
			}
			if (!Mathf.Approximately(QualitySettings.shadowDistance, num))
			{
				QualitySettings.shadowDistance = num;
			}
		}

		private void ApplyDepth()
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Invalid comparison between Unknown and I4
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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)
			Camera mainCamera = RenderOverrides.MainCamera;
			bool flag = PerfPlugin.On && PerfPlugin.DepthNormalsOff.Value;
			if (_depthChanged && ((Object)(object)_depthCam != (Object)(object)mainCamera || !flag))
			{
				if ((Object)(object)_depthCam != (Object)null)
				{
					DepthTextureMode val = (DepthTextureMode)(_depthCam.depthTextureMode | 2);
					_depthCam.depthTextureMode = (DepthTextureMode)(_depthHadDepth ? val : (val & -2));
				}
				_depthChanged = false;
				_depthCam = null;
			}
			if (flag && !((Object)(object)mainCamera == (Object)null) && (mainCamera.depthTextureMode & 2) != 0)
			{
				_depthHadDepth = (mainCamera.depthTextureMode & 1) > 0;
				mainCamera.depthTextureMode = (DepthTextureMode)((mainCamera.depthTextureMode & -3) | 1);
				_depthCam = mainCamera;
				_depthChanged = true;
			}
		}

		public string Describe()
		{
			return "sol objetos chicos " + ((PerfPlugin.SunSmallShadowCull.Value > 0f) ? (PerfPlugin.SunSmallShadowCull.Value.ToString("0") + " m") : "como el juego") + " · distancia sombras " + QualitySettings.shadowDistance.ToString("0") + " m · profundidad " + (_depthChanged ? "sin normales" : "con normales (juego)");
		}

		public void Dispose()
		{
			GraphicsSettingsManager.GraphicsSettingsChanged -= OnGraphicsSettingsChanged;
			PerfPlugin.Enabled.SettingChanged -= OnConfigChanged;
			PerfPlugin.SunSmallShadowCull.SettingChanged -= OnConfigChanged;
			PerfPlugin.SunShadowDistance.SettingChanged -= OnConfigChanged;
			PerfPlugin.DepthNormalsOff.SettingChanged -= OnConfigChanged;
		}
	}
	internal static class SettingsRow
	{
		private sealed class SelectableNav : INavGraph<Selectable>
		{
			internal static readonly SelectableNav Instance = new SelectableNav();

			public Selectable Get(Selectable node, Direction direction)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				Navigation navigation = node.navigation;
				Selectable val = (Selectable)(direction switch
				{
					Direction.Up => ((Navigation)(ref navigation)).selectOnUp, 
					Direction.Down => ((Navigation)(ref navigation)).selectOnDown, 
					Direction.Left => ((Navigation)(ref navigation)).selectOnLeft, 
					_ => ((Navigation)(ref navigation)).selectOnRight, 
				});
				if (!((Object)(object)val != (Object)null))
				{
					return null;
				}
				return val;
			}

			public void Set(Selectable node, Direction direction, Selectable target)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				Navigation navigation = node.navigation;
				switch (direction)
				{
				case Direction.Up:
					((Navigation)(ref navigation)).selectOnUp = target;
					break;
				case Direction.Down:
					((Navigation)(ref navigation)).selectOnDown = target;
					break;
				case Direction.Left:
					((Navigation)(ref navigation)).selectOnLeft = target;
					break;
				default:
					((Navigation)(ref navigation)).selectOnRight = target;
					break;
				}
				node.navigation = navigation;
			}
		}

		internal const string RowName = "vsm_perf_row";

		internal static bool SavePatched;

		private static Toggle s_toggle;

		private static int s_failedFor;

		private static bool s_warned;

		private static bool s_loggedException;

		private static readonly Vector3[] s_corners = (Vector3[])(object)new Vector3[4];

		internal static string LastResult = "la pestaña Accesibilidad aún no se abrió";

		internal static void OnTabOpen(AccessibilitySettings tab)
		{
			if ((Object)(object)tab == (Object)null)
			{
				return;
			}
			Toggle val = FindRow(((Component)tab).transform);
			if ((Object)(object)val == (Object)null && !SavePatched)
			{
				LastResult = "no se muestra (no se pudo parchear el OK de la pestaña)";
			}
			else if ((Object)(object)val == (Object)null && ((Object)tab).GetInstanceID() != s_failedFor)
			{
				val = Create(tab);
				if ((Object)(object)val == (Object)null)
				{
					s_failedFor = ((Object)tab).GetInstanceID();
				}
			}
			s_toggle = val;
			if ((Object)(object)val != (Object)null)
			{
				val.SetIsOnWithoutNotify(PerfPlugin.Enabled.Value);
				Link(tab, val);
			}
		}

		internal static void OnOk(AccessibilitySettings tab)
		{
			if ((Object)(object)tab == (Object)null)
			{
				return;
			}
			Toggle val = s_toggle;
			if ((Object)(object)val == (Object)null || !((Component)val).transform.IsChildOf(((Component)tab).transform))
			{
				val = FindRow(((Component)tab).transform);
			}
			if (!((Object)(object)val == (Object)null))
			{
				bool isOn = val.isOn;
				if (PerfPlugin.Enabled.Value != isOn)
				{
					PerfPlugin.Enabled.Value = isOn;
					PerfPlugin.Log.LogInfo((object)("vsm.perf: Enabled = " + isOn + " (Ajustes → Accesibilidad)."));
				}
			}
		}

		internal static void SyncFromConfig()
		{
			Toggle val = s_toggle;
			if ((Object)(object)val != (Object)null && val.isOn != PerfPlugin.Enabled.Value)
			{
				val.SetIsOnWithoutNotify(PerfPlugin.Enabled.Value);
			}
		}

		private static Toggle FindRow(Transform root)
		{
			Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (((Object)val).name == "vsm_perf_row")
				{
					Toggle componentInChildren = ((Component)val).GetComponentInChildren<Toggle>(true);
					if ((Object)(object)componentInChildren != (Object)null)
					{
						return componentInChildren;
					}
				}
			}
			return null;
		}

		private static Toggle PickTemplate(AccessibilitySettings tab)
		{
			Toggle[] obj = new Toggle[9] { tab.m_reduceFlashingLights, tab.m_cameraShake, tab.m_immersiveCamera, tab.m_toggleRun, tab.m_toggleBlockToggle, tab.m_depthOfFieldToggle, tab.m_motionblurToggle, tab.m_soundIndicatorsToggle, tab.m_closedCaptionsToggle };
			Toggle val = null;
			Toggle[] array = (Toggle[])(object)obj;
			foreach (Toggle val2 in array)
			{
				if (!((Object)(object)val2 == (Object)null) && ((Component)val2).transform.IsChildOf(((Component)tab).transform))
				{
					if (((Component)val2).gameObject.activeSelf)
					{
						return val2;
					}
					if ((Object)(object)val == (Object)null)
					{
						val = val2;
					}
				}
			}
			return val;
		}

		private static Transform RowRoot(Transform control, Transform page)
		{
			Transform val = control;
			while ((Object)(object)val.parent != (Object)null && (Object)(object)val.parent != (Object)(object)page && val.parent.IsChildOf(page) && ((Component)val.parent).GetComponentsInChildren<Selectable>(true).Length == 1)
			{
				val = val.parent;
			}
			return val;
		}

		private static Toggle Create(AccessibilitySettings tab)
		{
			Toggle val = PickTemplate(tab);
			if ((Object)(object)val == (Object)null)
			{
				return Fail("no hay ningún interruptor vanilla que copiar en la pestaña", null);
			}
			Transform val2 = RowRoot(((Component)val).transform, ((Component)tab).transform);
			Transform parent = val2.parent;
			if ((Object)(object)parent == (Object)null)
			{
				return Fail("la fila plantilla no tiene padre", null);
			}
			GameObject val3 = Object.Instantiate<GameObject>(((Component)val2).gameObject, parent, false);
			try
			{
				((Object)val3).name = "vsm_perf_row";
				val3.SetActive(true);
				Toggle val4 = MainToggle(val3.transform, val2, ((Component)val).transform);
				if ((Object)(object)val4 == (Object)null)
				{
					return Fail("la copia no tiene el interruptor", val3);
				}
				string text = Configure(tab, val3, val4);
				if (text != null)
				{
					return Fail(text, val3);
				}
				text = Place(tab, val3, val4, parent, out var where);