Decompiled source of PlayerStatsSync v1.1.2

plugins/PlayerStatsSync.dll

Decompiled 5 days ago
using 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)
		{
		}
	}
}