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 PlayerStatsSync v1.1.2
plugins/PlayerStatsSync.dll
Decompiled 5 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; 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: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PlayerStatsSync")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.2.0")] [assembly: AssemblyInformationalVersion("1.1.2+061d20b1083c5e01070b87ccc5a30e3a54d09a6f")] [assembly: AssemblyProduct("PlayerStatsSync")] [assembly: AssemblyTitle("PlayerStatsSync")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.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; } } } namespace PlayerStatsSync { [BepInPlugin("SunPheonyx.PlayerStatsSync", "Player Stats Sync", "1.1.2")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "SunPheonyx.PlayerStatsSync"; public const string PluginName = "Player Stats Sync"; public const string PluginVersion = "1.1.2"; internal const string RpcName = "PlayerStatsSync_Update"; internal const string DamageDoneKey = "PlayerStatsSync_DamageDone"; internal const string DamageTakenKey = "PlayerStatsSync_DamageTaken"; private const float SendInterval = 60f; internal static ManualLogSource Log; private float _timer; private void Awake() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; new Harmony("SunPheonyx.PlayerStatsSync").PatchAll(); } private void Update() { _timer += Time.unscaledDeltaTime; if (!(_timer < 60f)) { _timer = 0f; StatsSender.Send(); } } } internal static class DamageCounter { internal static void Add(Player player, string key, float amount) { if (!(amount <= 0f) && !float.IsNaN(amount) && !float.IsInfinity(amount)) { player.m_customData[key] = (Read(player, key) + amount).ToString("R", CultureInfo.InvariantCulture); } } internal static float Read(Player player, string key) { if (!player.m_customData.TryGetValue(key, out var value) || !float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return 0f; } return result; } } [HarmonyPatch(typeof(Character), "Damage")] internal static class DamageDonePatch { private static void Prefix(Character __instance, HitData hit) { //IL_0018: 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) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && !(__instance is Player) && hit.m_attacker == ((Character)localPlayer).GetZDOID()) { DamageCounter.Add(localPlayer, "PlayerStatsSync_DamageDone", hit.GetTotalDamage()); } } } [HarmonyPatch(typeof(Character), "ApplyDamage")] internal static class DamageTakenPatch { private static void Postfix(Character __instance, HitData hit) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { DamageCounter.Add(Player.m_localPlayer, "PlayerStatsSync_DamageTaken", hit.GetTotalDamage()); } } } [HarmonyPatch(typeof(Game), "Start")] internal static class RegisterRpcPatch { private static void Postfix() { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { ZRoutedRpc.instance.Register<ZPackage>("PlayerStatsSync_Update", (Action<long, ZPackage>)StatsReceiver.Receive); } } } [HarmonyPatch(typeof(Game), "Logout")] internal static class SendOnLogoutPatch { private static void Prefix() { StatsSender.Send(); } } internal static class StatsSender { internal static void Send() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Invalid comparison between Unknown and I4 //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Invalid comparison between Unknown and I4 //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; Game instance = Game.instance; PlayerProfile val = ((instance != null) ? instance.GetPlayerProfile() : null); if ((Object)(object)localPlayer == (Object)null || val == null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || ZRoutedRpc.instance == null) { return; } List<KeyValuePair<string, float>> list = new List<KeyValuePair<string, float>>(); foreach (PlayerStatType value in Enum.GetValues(typeof(PlayerStatType))) { if ((int)value != 205 && (int)value != 206) { float num = Mathf.Max(val.GetStat(value, (DifficultyRequirement)0), val.GetStat(value, (DifficultyRequirement)1)); if (num != 0f) { list.Add(new KeyValuePair<string, float>(((object)value/*cast due to .constrained prefix*/).ToString(), num)); } } } ZPackage val3 = new ZPackage(); val3.Write(1); val3.Write(val.GetName()); val3.Write(DamageCounter.Read(localPlayer, "PlayerStatsSync_DamageDone")); val3.Write(DamageCounter.Read(localPlayer, "PlayerStatsSync_DamageTaken")); val3.Write(list.Count); foreach (KeyValuePair<string, float> item in list) { val3.Write(item.Key); val3.Write(item.Value); } ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "PlayerStatsSync_Update", new object[1] { val3 }); } } internal static class StatsReceiver { private const int MaxStats = 400; private const int MaxTextLength = 64; private const string DamageDoneTotal = "damage_done"; private const string DamageTakenTotal = "damage_taken"; private static readonly Dictionary<string, long> Sessions = new Dictionary<string, long>(); internal static void Receive(long sender, ZPackage package) { try { ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer == null || package.ReadInt() != 1) { return; } string text = DigitsOnly(peer.m_socket.GetHostName()); if (text.Length == 0) { return; } string text2 = Limit(package.ReadString()); float value = package.ReadSingle(); float value2 = package.ReadSingle(); int num = package.ReadInt(); if (num < 0 || num > 400) { return; } string text3 = Path.Combine(Paths.ConfigPath, "PlayerStatsSync"); string text4 = Path.Combine(Paths.ConfigPath, "PlayerStatsSync.totals"); Directory.CreateDirectory(text3); Directory.CreateDirectory(text4); string text5 = Path.Combine(text3, text + ".json"); string path = Path.Combine(text4, text + "_" + Hex(text2) + ".dat"); string fileName = Path.GetFileName(path); long value3; bool num2 = !Sessions.TryGetValue(fileName, out value3) || value3 != sender; Sessions[fileName] = sender; bool skipGap = num2 && (File.Exists(path) || !AlreadyListed(text5, text2)); ServerTotals serverTotals = ServerTotals.Load(path); StringBuilder stringBuilder = new StringBuilder("{"); stringBuilder.Append("\"name\":\"").Append(Escape(Limit(peer.m_playerName))).Append("\","); stringBuilder.Append("\"character\":\"").Append(Escape(text2)).Append("\","); stringBuilder.Append("\"updated\":").Append(DateTimeOffset.UtcNow.ToUnixTimeSeconds()).Append(','); stringBuilder.Append("\"damage_done\":").Append(Number(serverTotals.Apply("damage_done", Clean(value), skipGap))).Append(','); stringBuilder.Append("\"damage_taken\":").Append(Number(serverTotals.Apply("damage_taken", Clean(value2), skipGap))).Append(','); stringBuilder.Append("\"stats\":{"); bool flag = true; for (int i = 0; i < num; i++) { string text6 = Limit(package.ReadString()); float num3 = serverTotals.Apply(text6, Clean(package.ReadSingle()), skipGap); if (num3 != 0f) { stringBuilder.Append(flag ? "" : ","); stringBuilder.Append('"').Append(Escape(text6)).Append("\":") .Append(Number(num3)); flag = false; } } stringBuilder.Append("}}"); string text7 = text5 + ".tmp"; File.WriteAllText(text7, stringBuilder.ToString()); File.Copy(text7, text5, overwrite: true); File.Delete(text7); serverTotals.Save(path); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Ignored a stats update: " + ex.Message)); } } private static bool AlreadyListed(string target, string character) { if (File.Exists(target)) { return File.ReadAllText(target).Contains("\"character\":\"" + Escape(character) + "\""); } return false; } private static string Hex(string text) { StringBuilder stringBuilder = new StringBuilder(); byte[] bytes = Encoding.UTF8.GetBytes(text); foreach (byte b in bytes) { stringBuilder.Append(b.ToString("x2")); } return stringBuilder.ToString(); } private static float Clean(float value) { if (!float.IsNaN(value) && !float.IsInfinity(value)) { return value; } return 0f; } private static string DigitsOnly(string text) { StringBuilder stringBuilder = new StringBuilder(); string text2 = text ?? ""; foreach (char c in text2) { if (c >= '0' && c <= '9') { stringBuilder.Append(c); } } return stringBuilder.ToString(); } private static string Limit(string text) { text = text ?? ""; if (text.Length <= 64) { return text; } return text.Substring(0, 64); } private static string Escape(string text) { StringBuilder stringBuilder = new StringBuilder(); foreach (char c in text) { if (c == '"' || c == '\\') { stringBuilder.Append('\\').Append(c); } else if (c < ' ') { stringBuilder.Append(' '); } else { stringBuilder.Append(c); } } return stringBuilder.ToString(); } private static string Number(float value) { if (!float.IsNaN(value) && !float.IsInfinity(value)) { return value.ToString("R", CultureInfo.InvariantCulture); } return "0"; } } internal class ServerTotals { private class Entry { internal double Last; internal double Offset; } private readonly Dictionary<string, Entry> _entries = new Dictionary<string, Entry>(); internal float Apply(string name, float total, bool skipGap) { if (!_entries.TryGetValue(name, out var value)) { value = new Entry(); _entries[name] = value; } if (skipGap) { value.Offset += (double)total - value.Last; } value.Last = total; return (float)Math.Max(0.0, (double)total - value.Offset); } internal static ServerTotals Load(string path) { ServerTotals serverTotals = new ServerTotals(); if (!File.Exists(path)) { return serverTotals; } using BinaryReader binaryReader = new BinaryReader(File.OpenRead(path)); int num = binaryReader.ReadInt32(); for (int i = 0; i < num; i++) { string key = binaryReader.ReadString(); serverTotals._entries[key] = new Entry { Last = binaryReader.ReadDouble(), Offset = binaryReader.ReadDouble() }; } return serverTotals; } internal void Save(string path) { string text = path + ".tmp"; using (BinaryWriter binaryWriter = new BinaryWriter(File.Create(text))) { binaryWriter.Write(_entries.Count); foreach (KeyValuePair<string, Entry> entry in _entries) { binaryWriter.Write(entry.Key); binaryWriter.Write(entry.Value.Last); binaryWriter.Write(entry.Value.Offset); } } File.Copy(text, path, overwrite: true); File.Delete(text); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }