using System; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Pigeon.Movement; using Sparroh.UI; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Sparroh")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.2.0")] [assembly: AssemblyInformationalVersion("1.2.2")] [assembly: AssemblyProduct("Speedometer")] [assembly: AssemblyTitle("Speedometer")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.2.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; } } } public static class ConfigManager { private const float DebounceSeconds = 0.25f; private static ConfigFile config; private static ManualLogSource logger; private static FileSystemWatcher configWatcher; private static volatile bool pendingRefresh; private static volatile bool reloadPending; private static float lastReloadTime; public static ConfigEntry EnableSpeedometerHUD { get; private set; } public static HudAnchors Anchors { get; private set; } public static ConfigColor ValueColor { get; private set; } public static void Initialize(ConfigFile configFile, ManualLogSource log) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) config = configFile; logger = log; EnableSpeedometerHUD = config.Bind("General", "Enable Speedometer", true, "Enables the speedometer HUD display."); Anchors = HudAnchors.Bind(config, "Speedometer", 0.06418981f, 0.2298982f, "HUD Positioning"); ValueColor = ConfigColor.Bind(config, "Colors", "Speed Color", UIColors.Sky, "Rich-text value color for speed (hex RRGGBB or #RRGGBB)."); EnableSpeedometerHUD.SettingChanged += OnSettingChanged; try { SetupFileWatcher(); } catch (Exception ex) { logger.LogError((object)("Error setting up config file watcher: " + ex.Message)); } } public static void Tick() { if (!reloadPending || Time.unscaledTime - lastReloadTime < 0.25f) { return; } reloadPending = false; lastReloadTime = Time.unscaledTime; try { config.Reload(); pendingRefresh = true; logger.LogInfo((object)"Config reloaded from disk."); } catch (Exception ex) { logger.LogError((object)("Error reloading config: " + ex.Message)); } } public static bool ConsumePendingRefresh() { if (!pendingRefresh) { return false; } pendingRefresh = false; return true; } public static void Dispose() { if (EnableSpeedometerHUD != null) { EnableSpeedometerHUD.SettingChanged -= OnSettingChanged; } if (configWatcher != null) { configWatcher.EnableRaisingEvents = false; configWatcher.Changed -= OnConfigFileChanged; configWatcher.Created -= OnConfigFileChanged; configWatcher.Renamed -= OnConfigFileChanged; configWatcher.Dispose(); configWatcher = null; } } private static void SetupFileWatcher() { configWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.speedometer.cfg"); configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite; configWatcher.Changed += OnConfigFileChanged; configWatcher.Created += OnConfigFileChanged; configWatcher.Renamed += OnConfigFileChanged; configWatcher.EnableRaisingEvents = true; } private static void OnConfigFileChanged(object sender, FileSystemEventArgs e) { reloadPending = true; } private static void OnSettingChanged(object sender, EventArgs e) { pendingRefresh = true; } } public sealed class PlayerSpeedReader { private readonly FieldInfo currentMoveSpeedField; private readonly FieldInfo moveVelocityField; private readonly FieldInfo rbField; private readonly PropertyInfo rbProp; private readonly FieldInfo vkField; private readonly PropertyInfo vkProp; public PlayerSpeedReader() { try { currentMoveSpeedField = typeof(Player).GetField("currentMoveSpeed", BindingFlags.Instance | BindingFlags.NonPublic); vkField = typeof(Player).GetField("velocity", BindingFlags.Instance | BindingFlags.NonPublic) ?? typeof(Player).GetField("velocity", BindingFlags.Instance | BindingFlags.Public); if (vkField == null) { vkProp = typeof(Player).GetProperty("velocity", BindingFlags.Instance | BindingFlags.Public); } if (vkField == null && vkProp == null) { rbField = typeof(Player).GetField("rb", BindingFlags.Instance | BindingFlags.NonPublic) ?? typeof(Player).GetField("rb", BindingFlags.Instance | BindingFlags.Public); } if (rbField == null && vkField == null && vkProp == null) { rbProp = typeof(Player).GetProperty("rb", BindingFlags.Instance | BindingFlags.Public); } if (rbField == null && rbProp == null && vkField == null && vkProp == null) { moveVelocityField = typeof(Player).GetField("moveVelocity", BindingFlags.Instance | BindingFlags.NonPublic) ?? typeof(Player).GetField("moveVelocity", BindingFlags.Instance | BindingFlags.Public); } } catch (Exception ex) { ManualLogSource logger = SpeedometerPlugin.Logger; if (logger != null) { logger.LogError((object)("Failed to initialize PlayerSpeedReader reflection: " + ex.Message)); } } } public float Read(Player player) { //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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return 0f; } float num = 0f; if (vkField != null || vkProp != null) { if (vkField != null) { if (vkField.GetValue(player) is Vector3 val) { num = ((Vector3)(ref val)).magnitude; } } else if (vkProp != null && vkProp.GetValue(player) is Vector3 val2) { num = ((Vector3)(ref val2)).magnitude; } } else if (rbField != null || rbProp != null) { Vector3 velocity; if (rbField != null) { object? value = rbField.GetValue(player); Rigidbody val3 = (Rigidbody)((value is Rigidbody) ? value : null); if (val3 != null) { velocity = val3.velocity; num = ((Vector3)(ref velocity)).magnitude; } } else if (rbProp != null) { object? value2 = rbProp.GetValue(player); Rigidbody val4 = (Rigidbody)((value2 is Rigidbody) ? value2 : null); if (val4 != null) { velocity = val4.velocity; num = ((Vector3)(ref velocity)).magnitude; } } } if (num == 0f && currentMoveSpeedField != null && currentMoveSpeedField.GetValue(player) is float num2) { num = num2; } if (num == 0f && moveVelocityField != null && moveVelocityField.GetValue(player) is Vector3 val5) { num = ((Vector3)(ref val5)).magnitude; } return num; } } [BepInPlugin("sparroh.speedometer", "Speedometer", "1.2.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [MycoMod(/*Could not decode attribute arguments.*/)] public class SpeedometerPlugin : BaseUnityPlugin { public const string PluginGUID = "sparroh.speedometer"; public const string PluginName = "Speedometer"; public const string PluginVersion = "1.2.2"; internal static ManualLogSource Logger; private Harmony harmony; private SpeedometerMod speedometer; private void Awake() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown Logger = ((BaseUnityPlugin)this).Logger; try { ConfigManager.Initialize(((BaseUnityPlugin)this).Config, Logger); } catch (Exception ex) { Logger.LogError((object)("Failed to initialize config: " + ex.Message)); return; } try { harmony = new Harmony("sparroh.speedometer"); } catch (Exception ex2) { Logger.LogError((object)("Failed to create Harmony instance: " + ex2.Message)); return; } try { speedometer = new SpeedometerMod(); } catch (Exception ex3) { Logger.LogError((object)("Failed to initialize Speedometer: " + ex3.Message)); } try { harmony.PatchAll(); } catch (Exception ex4) { Logger.LogError((object)("Failed to apply Harmony patches: " + ex4.Message)); } Logger.LogInfo((object)"Speedometer loaded successfully."); } private void Update() { try { ConfigManager.Tick(); if (ConfigManager.ConsumePendingRefresh() && speedometer != null) { speedometer.OnConfigChanged(); } if (speedometer != null) { speedometer.Update(); } } catch (Exception ex) { Logger.LogError((object)("Error in Speedometer.Update(): " + ex.Message)); } } private void OnDestroy() { try { if (speedometer != null) { speedometer.OnDestroy(); } } catch (Exception ex) { Logger.LogError((object)("Error in Speedometer.OnDestroy(): " + ex.Message)); } try { ConfigManager.Dispose(); } catch (Exception ex2) { Logger.LogError((object)("Error disposing config: " + ex2.Message)); } try { if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex3) { Logger.LogError((object)("Error unpatching Harmony: " + ex3.Message)); } } } public class SpeedometerMod { private readonly PlayerSpeedReader speedReader = new PlayerSpeedReader(); private HudHandle hud; public static SpeedometerMod Instance { get; private set; } public bool IsActive { get { if (HudHandle.IsValid(hud)) { return hud.IsActive; } return false; } } public Vector2 GetSize { get { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (!HudHandle.IsValid(hud)) { return Vector2.zero; } return hud.Size; } } public SpeedometerMod() { Instance = this; } public void OnConfigChanged() { if (!ConfigManager.EnableSpeedometerHUD.Value && HudHandle.IsValid(hud)) { DestroyHud(); } UpdateHudVisibility(); } public void UpdateHudVisibility() { if (HudHandle.IsValid(hud)) { hud.SetActive(ConfigManager.EnableSpeedometerHUD.Value); } } private void CreateSpeedometerHUD() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (hud != null && !hud.IsAlive) { hud = null; } if (!HudHandle.IsValid(hud)) { hud = HudBuilder.Create("SpeedometerHUD").ParentToReticle(true).Anchor(ConfigManager.Anchors.XValue, ConfigManager.Anchors.YValue) .Pivot(new Vector2(0f, 0.5f)) .Size(300f, 25f, true) .AddText("SpeedText", -1f, (TextAlignmentOptions)513) .Build(); if (HudHandle.IsValid(hud)) { hud.EnableReposition("sparroh.speedometer", "Speedometer", ConfigManager.Anchors); UpdateHudVisibility(); } } } private void DestroyHud() { if (hud != null) { if (hud.IsAlive) { hud.Destroy(); } hud = null; } } public void Update() { //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (!ConfigManager.EnableSpeedometerHUD.Value) { return; } if (!HudHandle.IsValid(hud)) { CreateSpeedometerHUD(); return; } if (hud.Primary == null || (Object)(object)Player.LocalPlayer == (Object)null) { if (hud.Primary != null) { hud.Primary.Text = "No Player"; } return; } float num = speedReader.Read(Player.LocalPlayer); if (num > 0f) { hud.Primary.SetRich("Speed", num, ConfigManager.ValueColor.Value, "m/s", "F1"); } else { hud.Primary.Text = "No Speed Detected"; } } public void OnDestroy() { DestroyHud(); } } namespace Speedometer { public static class MyPluginInfo { public const string PLUGIN_GUID = "Speedometer"; public const string PLUGIN_NAME = "Speedometer"; public const string PLUGIN_VERSION = "1.2.2"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }