Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CharacterStats v1.2.1
Character Stats.dll
Decompiled a month agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; 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: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("headclef")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.1.0")] [assembly: AssemblyInformationalVersion("1.2.1+5bb025f820942f9fe51d19886e2fd1dfc851b172")] [assembly: AssemblyProduct("Character Stats")] [assembly: AssemblyTitle("Character Stats")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 Character_Stats { [BepInPlugin("headclef.CharacterStats", "Character Stats", "1.2.1")] public class Character_Stats : BaseUnityPlugin { private const string PluginGuid = "headclef.CharacterStats"; private const string PluginName = "Character Stats"; private const string PluginVersion = "1.2.1"; private static readonly Dictionary<string, Dictionary<string, int>> _playerStats = new Dictionary<string, Dictionary<string, int>>(); private static bool _statsReady; private static readonly Dictionary<string, Dictionary<string, int>> _tempBonuses = new Dictionary<string, Dictionary<string, int>>(); private static readonly (string Key, string Field)[] _statKeyMap = new(string, string)[13] { ("Health", "playerUpgradeHealth"), ("Stamina", "playerUpgradeStamina"), ("Speed", "playerUpgradeSpeed"), ("Strength", "playerUpgradeStrength"), ("Range", "playerUpgradeRange"), ("Throw", "playerUpgradeThrow"), ("Extra Jump", "playerUpgradeExtraJump"), ("Launch", "playerUpgradeLaunch"), ("Crouch Rest", "playerUpgradeCrouchRest"), ("Map Player Count", "playerUpgradeMapPlayerCount"), ("Tumble Climb", "playerUpgradeTumbleClimb"), ("Tumble Wings", "playerUpgradeTumbleWings"), ("Death Head Battery", "playerUpgradeDeathHeadBattery") }; private static readonly Dictionary<string, FieldInfo?> _dictFieldCache = new Dictionary<string, FieldInfo>(); internal static Character_Stats Instance { get; private set; } = null; internal static ManualLogSource Logger => Instance._logger; private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger; internal Harmony? Harmony { get; set; } public static bool AreStatsReady => _statsReady; private void Awake() { //IL_003d: 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) //IL_0044: Expected O, but got Unknown //IL_0049: Expected O, but got Unknown Instance = this; ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; if (Harmony == null) { Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); Harmony val2 = val; Harmony = val; } Harmony.PatchAll(); Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!"); } private void OnDestroy() { Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } public static int GetUpgradeLevel(string steamId, string upgradeKey) { int num = 0; if (_playerStats.TryGetValue(steamId, out Dictionary<string, int> value) && value.TryGetValue(upgradeKey, out var value2)) { num = value2; } return num + GetTemporaryBonus(steamId, upgradeKey); } public static Dictionary<string, int> GetAllUpgrades(string steamId) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); if (_playerStats.TryGetValue(steamId, out Dictionary<string, int> value)) { foreach (KeyValuePair<string, int> item in value) { dictionary[item.Key] = item.Value; } } if (_tempBonuses.TryGetValue(steamId, out Dictionary<string, int> value2)) { foreach (KeyValuePair<string, int> item2 in value2) { dictionary[item2.Key] = (dictionary.TryGetValue(item2.Key, out var value3) ? value3 : 0) + item2.Value; } } return dictionary; } public static void SetTemporaryBonus(string steamId, string upgradeKey, int amount) { if (string.IsNullOrEmpty(steamId) || string.IsNullOrEmpty(upgradeKey)) { return; } if (amount == 0) { ClearTemporaryBonus(steamId, upgradeKey); return; } if (!_tempBonuses.TryGetValue(steamId, out Dictionary<string, int> value)) { value = new Dictionary<string, int>(); _tempBonuses[steamId] = value; } value[upgradeKey] = amount; } public static void ClearTemporaryBonus(string steamId, string upgradeKey) { if (_tempBonuses.TryGetValue(steamId, out Dictionary<string, int> value)) { value.Remove(upgradeKey); if (value.Count == 0) { _tempBonuses.Remove(steamId); } } } public static void ClearAllTemporaryBonuses(string steamId) { _tempBonuses.Remove(steamId); } public static int GetTemporaryBonus(string steamId, string upgradeKey) { if (_tempBonuses.TryGetValue(steamId, out Dictionary<string, int> value) && value.TryGetValue(upgradeKey, out var value2)) { return value2; } return 0; } public static IEnumerable<string> GetTrackedPlayers() { return _playerStats.Keys; } public static string? GetLocalSteamId() { return PlayerController.instance?.playerSteamID; } private static Dictionary<string, int>? GetStatDict(string fieldName) { if (!_dictFieldCache.TryGetValue(fieldName, out FieldInfo value)) { value = typeof(StatsManager).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _dictFieldCache[fieldName] = value; } return value?.GetValue(StatsManager.instance) as Dictionary<string, int>; } internal static void RefreshStats() { StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null) { return; } List<PlayerAvatar> list = SemiFunc.PlayerGetAll(); if (list == null || list.Count == 0) { return; } Dictionary<string, Dictionary<string, int>> dictionary = new Dictionary<string, Dictionary<string, int>>(); foreach (PlayerAvatar item3 in list) { string text = SemiFunc.PlayerGetSteamID(item3); if (string.IsNullOrEmpty(text)) { continue; } Dictionary<string, int> dictionary2 = new Dictionary<string, int>(); try { Dictionary<string, int> dictionary3 = instance.FetchPlayerUpgrades(text); if (dictionary3 != null) { foreach (KeyValuePair<string, int> item4 in dictionary3) { dictionary2[item4.Key] = item4.Value; } } } catch (Exception ex) { Logger.LogDebug((object)("FetchPlayerUpgrades threw for " + text + ": " + ex.Message)); } (string, string)[] statKeyMap = _statKeyMap; for (int i = 0; i < statKeyMap.Length; i++) { (string, string) tuple = statKeyMap[i]; string item = tuple.Item1; string item2 = tuple.Item2; Dictionary<string, int> statDict = GetStatDict(item2); if (statDict != null && statDict.TryGetValue(text, out var value)) { dictionary2[item] = value; } } dictionary[text] = dictionary2; } if (dictionary.Count == 0) { return; } _playerStats.Clear(); foreach (KeyValuePair<string, Dictionary<string, int>> item5 in dictionary) { _playerStats[item5.Key] = item5.Value; } _statsReady = true; } internal static void ClearStats() { _playerStats.Clear(); _statsReady = false; } } } namespace Character_Stats.Patches { [HarmonyPatch] internal static class StatReaderPatch { private static Coroutine? _refreshLoop; [HarmonyPatch(typeof(GameDirector), "Start")] [HarmonyPostfix] private static void GameDirector_Start_Postfix() { if (_refreshLoop != null) { ((MonoBehaviour)Character_Stats.Instance).StopCoroutine(_refreshLoop); } _refreshLoop = ((MonoBehaviour)Character_Stats.Instance).StartCoroutine(ReadStatsDelayed()); } private static IEnumerator ReadStatsDelayed() { while (!SemiFunc.LevelGenDone()) { yield return (object)new WaitForSeconds(0.5f); } if (SemiFunc.RunIsLevel()) { yield return (object)new WaitForSeconds(1f); while (SemiFunc.RunIsLevel()) { Character_Stats.RefreshStats(); yield return (object)new WaitForSeconds(1f); } _refreshLoop = null; } } [HarmonyPatch(typeof(PunManager), "ReceiveSyncData")] [HarmonyPostfix] private static void ReceiveSyncData_Postfix(bool finalChunk) { if (finalChunk) { ((MonoBehaviour)Character_Stats.Instance).StartCoroutine(ReadStatsAfterSync()); } } private static IEnumerator ReadStatsAfterSync() { yield return null; yield return null; yield return null; yield return (object)new WaitForSeconds(1f); Character_Stats.RefreshStats(); } [HarmonyPatch(typeof(RunManager), "ResetProgress")] [HarmonyPostfix] private static void RunManager_ResetProgress_Postfix() { Character_Stats.ClearStats(); } } }