Decompiled source of GsValheimStatsClient v0.2.1

GsValheimStatsClient.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
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: AssemblyCompany("GsValheimStatsClient")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: AssemblyInformationalVersion("0.2.1")]
[assembly: AssemblyProduct("GsValheimStatsClient")]
[assembly: AssemblyTitle("GsValheimStatsClient")]
[assembly: AssemblyVersion("0.2.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 GsValheimStatsClient
{
	[BepInPlugin("net.cproudlock.gsvalheimstatsclient", "GsValheimStatsClient", "0.2.1")]
	public class Plugin : BaseUnityPlugin
	{
		private class Combat
		{
			public readonly Dictionary<string, double> wDmg = new Dictionary<string, double>();

			public readonly Dictionary<string, int> wKill = new Dictionary<string, int>();

			public readonly Dictionary<string, double> wMax = new Dictionary<string, double>();

			public readonly Dictionary<string, double> wSwing = new Dictionary<string, double>();

			public readonly Dictionary<string, double> iDmg = new Dictionary<string, double>();

			public readonly Dictionary<string, int> iKill = new Dictionary<string, int>();

			public readonly Dictionary<string, double> iMax = new Dictionary<string, double>();

			public readonly Dictionary<string, double> bDmg = new Dictionary<string, double>();

			public readonly Dictionary<string, double> bEng = new Dictionary<string, double>();

			public readonly Dictionary<string, float> bLast = new Dictionary<string, float>();

			public string swW;

			public float swT;

			public double swD;

			public void Weapon(string w, double dmg)
			{
				wDmg.TryGetValue(w, out var value);
				wDmg[w] = value + dmg;
				wMax.TryGetValue(w, out var value2);
				if (dmg > value2)
				{
					wMax[w] = dmg;
				}
				float time = Time.time;
				if (w == swW && time - swT <= 0.2f)
				{
					swD += dmg;
				}
				else
				{
					swW = w;
					swD = dmg;
				}
				swT = time;
				wSwing.TryGetValue(w, out var value3);
				if (swD > value3)
				{
					wSwing[w] = swD;
				}
			}

			public void Item(string it, double dmg)
			{
				iDmg.TryGetValue(it, out var value);
				iDmg[it] = value + dmg;
				iMax.TryGetValue(it, out var value2);
				if (dmg > value2)
				{
					iMax[it] = dmg;
				}
			}

			public void Boss(string b, double dmg)
			{
				bDmg.TryGetValue(b, out var value);
				bDmg[b] = value + dmg;
				float time = Time.time;
				if (bLast.TryGetValue(b, out var value2) && time - value2 <= 15f)
				{
					bEng.TryGetValue(b, out var value3);
					bEng[b] = value3 + (double)(time - value2);
				}
				bLast[b] = time;
			}
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class Patch_LocalDamage
		{
			private static void Postfix(Character __instance, HitData hit)
			{
				try
				{
					if ((Object)(object)__instance == (Object)null || hit == null)
					{
						return;
					}
					if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
					{
						Character attacker = hit.GetAttacker();
						if ((Object)(object)attacker != (Object)null && !(attacker is Player))
						{
							lastKiller = CleanName(attacker);
							lastHitType = null;
						}
						else
						{
							lastHitType = ((object)(HitType)(ref hit.m_hitType)).ToString();
							lastKiller = null;
						}
						return;
					}
					ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
					if ((Object)(object)component == (Object)null || !component.IsOwner())
					{
						return;
					}
					Character attacker2 = hit.GetAttacker();
					Player val = (Player)(object)((attacker2 is Player) ? attacker2 : null);
					if ((Object)(object)val == (Object)null)
					{
						return;
					}
					double dmg = hit.GetTotalDamage();
					int instanceID = ((Object)__instance).GetInstanceID();
					string text = SafeSkill(hit);
					lastWeaponByVictim[instanceID] = text;
					string text2 = CleanName(__instance);
					bool flag = text2 != null && BossNames.Contains(text2);
					if ((Object)(object)val == (Object)(object)Player.m_localPlayer)
					{
						lastAttackerByVictim.Remove(instanceID);
						Instance?.AddWeaponDamage(text, dmg);
						string text3 = CurrentWeaponItem();
						lastItemByVictim[instanceID] = text3;
						Instance?.AddItemDamage(text3, dmg);
						if (flag)
						{
							Instance?.AddBossDamage(text2, dmg);
						}
						return;
					}
					string playerName = val.GetPlayerName();
					if (!string.IsNullOrEmpty(playerName) && !(playerName == "..."))
					{
						lastAttackerByVictim[instanceID] = playerName;
						string text4 = ItemOf((Character)(object)val);
						lastItemByVictim[instanceID] = text4;
						Combat combat = Instance?.Other(playerName);
						combat?.Weapon(text, dmg);
						combat?.Item(text4, dmg);
						if (flag)
						{
							combat?.Boss(text2, dmg);
						}
					}
				}
				catch
				{
				}
			}
		}

		[HarmonyPatch(typeof(Character), "OnDeath")]
		private static class Patch_CreatureDeath
		{
			private static void Postfix(Character __instance)
			{
				try
				{
					if (!((Object)(object)__instance == (Object)null) && !(__instance is Player))
					{
						Instance?.CreditWeaponKill(((Object)__instance).GetInstanceID());
					}
				}
				catch
				{
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnDeath")]
		private static class Patch_Death
		{
			private static void Postfix(Player __instance)
			{
				try
				{
					if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
					{
						Instance?.OnLocalDeath(__instance);
					}
				}
				catch (Exception ex)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)("[gs] death: " + ex.Message));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })]
		private static class Patch_AddItem
		{
			private static void Postfix(Inventory __instance, ItemData item)
			{
				//IL_0035: Unknown result type (might be due to invalid IL or missing references)
				//IL_003b: Invalid comparison between Unknown and I4
				try
				{
					if (item?.m_shared != null && !((Object)(object)Player.m_localPlayer == (Object)null) && __instance == ((Humanoid)Player.m_localPlayer).GetInventory() && (int)item.m_shared.m_itemType == 1)
					{
						string n = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item.m_shared.m_name);
						n = CleanPrefab(n);
						Instance.materials.TryGetValue(n, out var value);
						Instance.materials[n] = value + Math.Max(1, item.m_stack);
					}
				}
				catch
				{
				}
			}
		}

		public const string GUID = "net.cproudlock.gsvalheimstatsclient";

		public const string NAME = "GsValheimStatsClient";

		public const string VERSION = "0.2.1";

		internal static ManualLogSource Log;

		private static readonly HttpClient http = new HttpClient
		{
			Timeout = TimeSpan.FromSeconds(8.0)
		};

		private static ConfigEntry<string> cfgUrl;

		private static ConfigEntry<string> cfgToken;

		private static ConfigEntry<string> cfgWorld;

		private static ConfigEntry<int> cfgEmitInterval;

		private static Plugin Instance;

		private readonly Dictionary<string, int> crafts = new Dictionary<string, int>();

		private readonly Dictionary<string, long> materials = new Dictionary<string, long>();

		private readonly Dictionary<string, double> weaponDamage = new Dictionary<string, double>();

		private readonly Dictionary<string, int> weaponKills = new Dictionary<string, int>();

		private readonly Dictionary<string, double> weaponMaxHit = new Dictionary<string, double>();

		private readonly Dictionary<string, double> weaponBiggestSwing = new Dictionary<string, double>();

		private static string swingWeapon;

		private static float swingTime;

		private static double swingDamage;

		private readonly Dictionary<string, double> bossDamage = new Dictionary<string, double>();

		private readonly Dictionary<string, double> bossEngageSec = new Dictionary<string, double>();

		private static readonly Dictionary<string, float> bossLastHit = new Dictionary<string, float>();

		private readonly Dictionary<string, double> itemDamage = new Dictionary<string, double>();

		private readonly Dictionary<string, int> itemKills = new Dictionary<string, int>();

		private readonly Dictionary<string, double> itemMaxHit = new Dictionary<string, double>();

		private static readonly Dictionary<int, string> lastWeaponByVictim = new Dictionary<int, string>();

		private static readonly Dictionary<int, string> lastItemByVictim = new Dictionary<int, string>();

		private string weaponsPath;

		private readonly Dictionary<string, Combat> othersCombat = new Dictionary<string, Combat>();

		private static readonly Dictionary<int, string> lastAttackerByVictim = new Dictionary<int, string>();

		private static readonly HashSet<string> BossNames = new HashSet<string> { "Eikthyr", "gd_king", "Bonemass", "Dragon", "GoblinKing", "SeekerQueen", "Fader" };

		private float lifeStart;

		private bool lifeActive;

		private double enemyKillsAtSpawn;

		private int longestLifeSec;

		private int bestKillsBeforeDeath;

		private string currentLifeStartedUtc;

		private static string lastKiller;

		private static string lastHitType;

		private readonly List<string> pendingDeaths = new List<string>();

		private float lastEmit;

		private static bool dirty;

		private static MethodInfo getStatMethod;

		private static Array statTypeValues;

		private const BindingFlags BF = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private Combat Other(string name)
		{
			if (!othersCombat.TryGetValue(name, out var value))
			{
				value = new Combat();
				othersCombat[name] = value;
			}
			return value;
		}

		private void Awake()
		{
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			cfgUrl = ((BaseUnityPlugin)this).Config.Bind<string>("Ingest", "Url", "https://gs.proudtech.net/api/valheim/ingest", "gs dashboard ingest endpoint.");
			cfgToken = ((BaseUnityPlugin)this).Config.Bind<string>("Ingest", "Token", "", "Bearer token from the dashboard owner.");
			cfgWorld = ((BaseUnityPlugin)this).Config.Bind<string>("General", "World", "vhserver3", "World name to file your stats under (must match the server's world).");
			cfgEmitInterval = ((BaseUnityPlugin)this).Config.Bind<int>("General", "EmitIntervalSeconds", 120, "How often to POST your stats.");
			weaponsPath = Path.Combine(Paths.ConfigPath, "net.cproudlock.gsvalheimstatsclient." + cfgWorld.Value + ".weapons.tsv");
			LoadWeapons();
			new Harmony("net.cproudlock.gsvalheimstatsclient").PatchAll();
			Log.LogInfo((object)("GsValheimStatsClient 0.2.1 loaded, posting to " + cfgUrl.Value + " (token " + (string.IsNullOrEmpty(cfgToken.Value) ? "MISSING" : "set") + ")"));
		}

		private static string CleanName(Character c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return null;
			}
			string text = (((Object)(object)((Component)c).gameObject != (Object)null) ? ((Object)((Component)c).gameObject).name : ((Object)c).name);
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			int num = text.IndexOf('(');
			if (num > 0)
			{
				text = text.Substring(0, num);
			}
			return text.Trim();
		}

		private static string SafeSkill(HitData hit)
		{
			try
			{
				string text = ((object)(SkillType)(ref hit.m_skill)).ToString();
				if (string.IsNullOrEmpty(text) || text == "None" || text == "All")
				{
					return "Unarmed";
				}
				return text;
			}
			catch
			{
				return "Unarmed";
			}
		}

		private static string CurrentWeaponItem()
		{
			return ItemOf((Character)(object)Player.m_localPlayer);
		}

		private static string ItemOf(Character c)
		{
			try
			{
				Humanoid val = (Humanoid)(object)((c is Humanoid) ? c : null);
				ItemData val2 = (((Object)(object)val != (Object)null) ? val.GetCurrentWeapon() : null);
				string text = ((val2 != null && val2.m_shared != null) ? val2.m_shared.m_name : null);
				return string.IsNullOrEmpty(text) ? "Unarmed" : text;
			}
			catch
			{
				return "Unarmed";
			}
		}

		private void AddWeaponDamage(string weapon, double dmg)
		{
			if (!string.IsNullOrEmpty(weapon))
			{
				weaponDamage.TryGetValue(weapon, out var value);
				weaponDamage[weapon] = value + dmg;
				weaponMaxHit.TryGetValue(weapon, out var value2);
				if (dmg > value2)
				{
					weaponMaxHit[weapon] = dmg;
					dirty = true;
				}
				float time = Time.time;
				if (weapon == swingWeapon && time - swingTime <= 0.2f)
				{
					swingDamage += dmg;
				}
				else
				{
					swingWeapon = weapon;
					swingDamage = dmg;
				}
				swingTime = time;
				weaponBiggestSwing.TryGetValue(weapon, out var value3);
				if (swingDamage > value3)
				{
					weaponBiggestSwing[weapon] = swingDamage;
				}
			}
		}

		private void AddItemDamage(string item, double dmg)
		{
			if (!string.IsNullOrEmpty(item))
			{
				itemDamage.TryGetValue(item, out var value);
				itemDamage[item] = value + dmg;
				itemMaxHit.TryGetValue(item, out var value2);
				if (dmg > value2)
				{
					itemMaxHit[item] = dmg;
				}
			}
		}

		private void AddBossDamage(string boss, double dmg)
		{
			if (!string.IsNullOrEmpty(boss))
			{
				bossDamage.TryGetValue(boss, out var value);
				bossDamage[boss] = value + dmg;
				float time = Time.time;
				if (bossLastHit.TryGetValue(boss, out var value2) && time - value2 <= 15f)
				{
					bossEngageSec.TryGetValue(boss, out var value3);
					bossEngageSec[boss] = value3 + (double)(time - value2);
				}
				bossLastHit[boss] = time;
			}
		}

		private void CreditWeaponKill(int victimId)
		{
			lastWeaponByVictim.TryGetValue(victimId, out var value);
			lastItemByVictim.TryGetValue(victimId, out var value2);
			lastAttackerByVictim.TryGetValue(victimId, out var value3);
			if (string.IsNullOrEmpty(value3))
			{
				if (value != null)
				{
					weaponKills.TryGetValue(value, out var value4);
					weaponKills[value] = value4 + 1;
				}
				if (value2 != null)
				{
					itemKills.TryGetValue(value2, out var value5);
					itemKills[value2] = value5 + 1;
				}
			}
			else
			{
				Combat combat = Other(value3);
				if (value != null)
				{
					combat.wKill.TryGetValue(value, out var value6);
					combat.wKill[value] = value6 + 1;
				}
				if (value2 != null)
				{
					combat.iKill.TryGetValue(value2, out var value7);
					combat.iKill[value2] = value7 + 1;
				}
			}
			lastWeaponByVictim.Remove(victimId);
			lastItemByVictim.Remove(victimId);
			lastAttackerByVictim.Remove(victimId);
			dirty = true;
			SaveWeapons();
		}

		private void LoadWeapons()
		{
			try
			{
				if (!File.Exists(weaponsPath))
				{
					return;
				}
				string[] array = File.ReadAllLines(weaponsPath);
				for (int i = 0; i < array.Length; i++)
				{
					string[] array2 = array[i].Split('\t');
					if (array2.Length == 3)
					{
						if (array2[0] == "wdmg")
						{
							weaponDamage[array2[1]] = double.Parse(array2[2], CultureInfo.InvariantCulture);
						}
						else if (array2[0] == "wkill")
						{
							weaponKills[array2[1]] = int.Parse(array2[2]);
						}
						else if (array2[0] == "wmax")
						{
							weaponMaxHit[array2[1]] = double.Parse(array2[2], CultureInfo.InvariantCulture);
						}
						else if (array2[0] == "wsw")
						{
							weaponBiggestSwing[array2[1]] = double.Parse(array2[2], CultureInfo.InvariantCulture);
						}
						else if (array2[0] == "bdmg")
						{
							bossDamage[array2[1]] = double.Parse(array2[2], CultureInfo.InvariantCulture);
						}
						else if (array2[0] == "beng")
						{
							bossEngageSec[array2[1]] = double.Parse(array2[2], CultureInfo.InvariantCulture);
						}
						else if (array2[0] == "idmg")
						{
							itemDamage[array2[1]] = double.Parse(array2[2], CultureInfo.InvariantCulture);
						}
						else if (array2[0] == "ikill")
						{
							itemKills[array2[1]] = int.Parse(array2[2]);
						}
						else if (array2[0] == "imax")
						{
							itemMaxHit[array2[1]] = double.Parse(array2[2], CultureInfo.InvariantCulture);
						}
					}
					else if (array2.Length == 4)
					{
						Combat combat = Other(array2[1]);
						if (array2[0] == "owdmg")
						{
							combat.wDmg[array2[2]] = double.Parse(array2[3], CultureInfo.InvariantCulture);
						}
						else if (array2[0] == "owkill")
						{
							combat.wKill[array2[2]] = int.Parse(array2[3]);
						}
						else if (array2[0] == "owmax")
						{
							combat.wMax[array2[2]] = double.Parse(array2[3], CultureInfo.InvariantCulture);
						}
						else if (array2[0] == "owsw")
						{
							combat.wSwing[array2[2]] = double.Parse(array2[3], CultureInfo.InvariantCulture);
						}
						else if (array2[0] == "oidmg")
						{
							combat.iDmg[array2[2]] = double.Parse(array2[3], CultureInfo.InvariantCulture);
						}
						else if (array2[0] == "oikill")
						{
							combat.iKill[array2[2]] = int.Parse(array2[3]);
						}
						else if (array2[0] == "oimax")
						{
							combat.iMax[array2[2]] = double.Parse(array2[3], CultureInfo.InvariantCulture);
						}
						else if (array2[0] == "obdmg")
						{
							combat.bDmg[array2[2]] = double.Parse(array2[3], CultureInfo.InvariantCulture);
						}
						else if (array2[0] == "obeng")
						{
							combat.bEng[array2[2]] = double.Parse(array2[3], CultureInfo.InvariantCulture);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[gs] load weapons: " + ex.Message));
			}
		}

		private void SaveWeapons()
		{
			try
			{
				List<string> list = new List<string>();
				foreach (KeyValuePair<string, double> item in weaponDamage)
				{
					list.Add("wdmg\t" + item.Key + "\t" + item.Value.ToString(CultureInfo.InvariantCulture));
				}
				foreach (KeyValuePair<string, int> weaponKill in weaponKills)
				{
					list.Add($"wkill\t{weaponKill.Key}\t{weaponKill.Value}");
				}
				foreach (KeyValuePair<string, double> item2 in weaponMaxHit)
				{
					list.Add("wmax\t" + item2.Key + "\t" + item2.Value.ToString(CultureInfo.InvariantCulture));
				}
				foreach (KeyValuePair<string, double> item3 in weaponBiggestSwing)
				{
					list.Add("wsw\t" + item3.Key + "\t" + item3.Value.ToString(CultureInfo.InvariantCulture));
				}
				foreach (KeyValuePair<string, double> item4 in bossDamage)
				{
					list.Add("bdmg\t" + item4.Key + "\t" + item4.Value.ToString(CultureInfo.InvariantCulture));
				}
				foreach (KeyValuePair<string, double> item5 in bossEngageSec)
				{
					list.Add("beng\t" + item5.Key + "\t" + item5.Value.ToString(CultureInfo.InvariantCulture));
				}
				foreach (KeyValuePair<string, double> item6 in itemDamage)
				{
					list.Add("idmg\t" + item6.Key + "\t" + item6.Value.ToString(CultureInfo.InvariantCulture));
				}
				foreach (KeyValuePair<string, int> itemKill in itemKills)
				{
					list.Add($"ikill\t{itemKill.Key}\t{itemKill.Value}");
				}
				foreach (KeyValuePair<string, double> item7 in itemMaxHit)
				{
					list.Add("imax\t" + item7.Key + "\t" + item7.Value.ToString(CultureInfo.InvariantCulture));
				}
				foreach (KeyValuePair<string, Combat> item8 in othersCombat)
				{
					string key = item8.Key;
					Combat value = item8.Value;
					foreach (KeyValuePair<string, double> item9 in value.wDmg)
					{
						list.Add("owdmg\t" + key + "\t" + item9.Key + "\t" + item9.Value.ToString(CultureInfo.InvariantCulture));
					}
					foreach (KeyValuePair<string, int> item10 in value.wKill)
					{
						list.Add($"owkill\t{key}\t{item10.Key}\t{item10.Value}");
					}
					foreach (KeyValuePair<string, double> item11 in value.wMax)
					{
						list.Add("owmax\t" + key + "\t" + item11.Key + "\t" + item11.Value.ToString(CultureInfo.InvariantCulture));
					}
					foreach (KeyValuePair<string, double> item12 in value.wSwing)
					{
						list.Add("owsw\t" + key + "\t" + item12.Key + "\t" + item12.Value.ToString(CultureInfo.InvariantCulture));
					}
					foreach (KeyValuePair<string, double> item13 in value.iDmg)
					{
						list.Add("oidmg\t" + key + "\t" + item13.Key + "\t" + item13.Value.ToString(CultureInfo.InvariantCulture));
					}
					foreach (KeyValuePair<string, int> item14 in value.iKill)
					{
						list.Add($"oikill\t{key}\t{item14.Key}\t{item14.Value}");
					}
					foreach (KeyValuePair<string, double> item15 in value.iMax)
					{
						list.Add("oimax\t" + key + "\t" + item15.Key + "\t" + item15.Value.ToString(CultureInfo.InvariantCulture));
					}
					foreach (KeyValuePair<string, double> item16 in value.bDmg)
					{
						list.Add("obdmg\t" + key + "\t" + item16.Key + "\t" + item16.Value.ToString(CultureInfo.InvariantCulture));
					}
					foreach (KeyValuePair<string, double> item17 in value.bEng)
					{
						list.Add("obeng\t" + key + "\t" + item17.Key + "\t" + item17.Value.ToString(CultureInfo.InvariantCulture));
					}
				}
				File.WriteAllLines(weaponsPath, list);
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[gs] save weapons: " + ex.Message));
			}
		}

		private static string CleanPrefab(string n)
		{
			if (string.IsNullOrEmpty(n))
			{
				return "unknown";
			}
			int num = n.IndexOf('(');
			if (num > 0)
			{
				n = n.Substring(0, num);
			}
			return n.Trim();
		}

		private void OnLocalDeath(Player p)
		{
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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)
			double num = ReadStat("EnemyKills");
			int num2 = (int)Math.Max(0.0, num - enemyKillsAtSpawn);
			int num3 = (lifeActive ? Mathf.RoundToInt(Time.time - lifeStart) : 0);
			if (num3 > longestLifeSec)
			{
				longestLifeSec = num3;
			}
			if (num2 > bestKillsBeforeDeath)
			{
				bestKillsBeforeDeath = num2;
			}
			string text = lastKiller ?? MapHitType(lastHitType);
			string v = "unknown";
			try
			{
				Biome val = Heightmap.FindBiome(((Component)p).transform.position);
				v = ((object)(Biome)(ref val)).ToString();
			}
			catch
			{
			}
			Vector3 position = ((Component)p).transform.position;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append('{');
			J(stringBuilder, "playerName", p.GetPlayerName());
			C(stringBuilder);
			J(stringBuilder, "killer", text);
			C(stringBuilder);
			J(stringBuilder, "biome", v);
			C(stringBuilder);
			stringBuilder.Append("\"posX\":").Append(Mathf.RoundToInt(position.x));
			C(stringBuilder);
			stringBuilder.Append("\"posY\":").Append(Mathf.RoundToInt(position.y));
			C(stringBuilder);
			stringBuilder.Append("\"posZ\":").Append(Mathf.RoundToInt(position.z));
			C(stringBuilder);
			stringBuilder.Append("\"lifeSec\":").Append(num3);
			C(stringBuilder);
			stringBuilder.Append("\"killsThisLife\":").Append(num2);
			C(stringBuilder);
			J(stringBuilder, "tsUtc", DateTime.UtcNow.ToString("O"));
			stringBuilder.Append('}');
			pendingDeaths.Add(stringBuilder.ToString());
			lifeActive = false;
			Log.LogInfo((object)$"[gs] death by {text} after {num3}s, {num2} kills this life");
			try
			{
				Emit();
			}
			catch
			{
			}
		}

		private static string MapHitType(string h)
		{
			if (string.IsNullOrEmpty(h))
			{
				return "unknown";
			}
			return h switch
			{
				"Fall" => "fall", 
				"Drowning" => "drowning", 
				"Burning" => "fire", 
				"Freezing" => "freezing", 
				"Poisoned" => "poison", 
				"Smoke" => "smoke", 
				"Tree" => "tree", 
				"Self" => "self", 
				"EdgeOfWorld" => "edge of world", 
				"Water" => "water", 
				_ => h.ToLowerInvariant(), 
			};
		}

		private void Update()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			if (!lifeActive && !((Character)localPlayer).IsDead())
			{
				lifeActive = true;
				lifeStart = Time.time;
				enemyKillsAtSpawn = ReadStat("EnemyKills");
				currentLifeStartedUtc = DateTime.UtcNow.ToString("O");
			}
			float unscaledTime = Time.unscaledTime;
			if ((!dirty || !(unscaledTime - lastEmit > 15f)) && !(unscaledTime - lastEmit > (float)Mathf.Max(30, cfgEmitInterval.Value)))
			{
				return;
			}
			lastEmit = unscaledTime;
			dirty = false;
			try
			{
				Emit();
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"[gs] emit: {arg}");
			}
		}

		private static double ToD(object o)
		{
			try
			{
				return Convert.ToDouble(o);
			}
			catch
			{
				return 0.0;
			}
		}

		private static IDictionary GetDict(object obj, string field)
		{
			try
			{
				return obj?.GetType().GetField(field, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) as IDictionary;
			}
			catch
			{
				return null;
			}
		}

		private static void ReadProfile(Dictionary<string, long> statsKv, Dictionary<string, int> enemyKills, Dictionary<string, int> craftStats, Dictionary<string, int> pickups)
		{
			try
			{
				Game instance = Game.instance;
				PlayerProfile val = ((instance != null) ? instance.GetPlayerProfile() : null);
				if (val == null)
				{
					return;
				}
				IDictionary dict = GetDict(((object)val).GetType().GetField("m_playerStats", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(val), "m_stats");
				if (dict != null)
				{
					foreach (DictionaryEntry item in dict)
					{
						double num = ToD(item.Value);
						if (num > 0.0)
						{
							statsKv["vh_" + item.Key] = (long)num;
						}
					}
				}
				IDictionary dict2 = GetDict(val, "m_enemyStats");
				if (dict2 != null)
				{
					foreach (DictionaryEntry item2 in dict2)
					{
						int num2 = (int)ToD(item2.Value);
						if (num2 > 0)
						{
							enemyKills[(string)item2.Key] = num2;
						}
					}
				}
				IDictionary dict3 = GetDict(val, "m_itemCraftStats");
				if (dict3 != null)
				{
					foreach (DictionaryEntry item3 in dict3)
					{
						int num3 = (int)ToD(item3.Value);
						if (num3 > 0)
						{
							craftStats[(string)item3.Key] = num3;
						}
					}
				}
				IDictionary dict4 = GetDict(val, "m_itemPickupStats");
				if (dict4 == null)
				{
					return;
				}
				foreach (DictionaryEntry item4 in dict4)
				{
					int num4 = (int)ToD(item4.Value);
					if (num4 > 0)
					{
						pickups[(string)item4.Key] = num4;
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[gs] profile read: " + ex.Message));
			}
		}

		private static double ReadStat(string statName)
		{
			try
			{
				Game instance = Game.instance;
				PlayerProfile val = ((instance != null) ? instance.GetPlayerProfile() : null);
				IDictionary dict = GetDict(((object)val)?.GetType().GetField("m_playerStats", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(val), "m_stats");
				if (dict != null)
				{
					foreach (DictionaryEntry item in dict)
					{
						if (item.Key.ToString() == statName)
						{
							return ToD(item.Value);
						}
					}
				}
			}
			catch
			{
			}
			return 0.0;
		}

		private void Emit()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			string playerName = localPlayer.GetPlayerName();
			if (string.IsNullOrEmpty(playerName) || playerName == "...")
			{
				return;
			}
			Dictionary<string, long> dictionary = new Dictionary<string, long>();
			Dictionary<string, int> dictionary2 = new Dictionary<string, int>();
			Dictionary<string, int> dictionary3 = new Dictionary<string, int>();
			Dictionary<string, int> dictionary4 = new Dictionary<string, int>();
			ReadProfile(dictionary, dictionary2, dictionary3, dictionary4);
			long value;
			long value2 = (dictionary.TryGetValue("vh_Deaths", out value) ? value : 0);
			int num = 0;
			int num2 = 0;
			foreach (KeyValuePair<string, int> item in dictionary2)
			{
				num += item.Value;
				if (BossNames.Contains(item.Key))
				{
					num2 += item.Value;
				}
			}
			StringBuilder stringBuilder = new StringBuilder(2048);
			stringBuilder.Append('{');
			J(stringBuilder, "schemaVersion", 1);
			C(stringBuilder);
			J(stringBuilder, "game", "valheim");
			C(stringBuilder);
			J(stringBuilder, "source", "client");
			C(stringBuilder);
			J(stringBuilder, "reporter", playerName);
			C(stringBuilder);
			J(stringBuilder, "world", cfgWorld.Value);
			C(stringBuilder);
			J(stringBuilder, "emittedAtUtc", DateTime.UtcNow.ToString("O"));
			C(stringBuilder);
			J(stringBuilder, "snapshotIdLocal", Guid.NewGuid().ToString("N"));
			C(stringBuilder);
			stringBuilder.Append("\"players\":[{");
			J(stringBuilder, "name", playerName);
			C(stringBuilder);
			try
			{
				J(stringBuilder, "platformId", localPlayer.GetPlayerID().ToString());
				C(stringBuilder);
			}
			catch
			{
			}
			stringBuilder.Append("\"kills\":").Append(num);
			C(stringBuilder);
			stringBuilder.Append("\"bossKills\":").Append(num2);
			C(stringBuilder);
			stringBuilder.Append("\"deaths\":").Append(value2);
			C(stringBuilder);
			stringBuilder.Append("\"longestLifeSec\":").Append(longestLifeSec);
			C(stringBuilder);
			stringBuilder.Append("\"bestKillsBeforeDeath\":").Append(bestKillsBeforeDeath);
			C(stringBuilder);
			J(stringBuilder, "currentLifeStartedUtc", currentLifeStartedUtc);
			C(stringBuilder);
			stringBuilder.Append("\"stats\":{");
			bool flag = true;
			foreach (KeyValuePair<string, long> item2 in dictionary)
			{
				if (!flag)
				{
					stringBuilder.Append(',');
				}
				flag = false;
				stringBuilder.Append('"').Append(Esc(item2.Key)).Append("\":")
					.Append(item2.Value);
			}
			stringBuilder.Append("},");
			stringBuilder.Append("\"skills\":[");
			bool flag2 = true;
			try
			{
				foreach (Skill skill in ((Character)localPlayer).GetSkills().GetSkillList())
				{
					if (!flag2)
					{
						stringBuilder.Append(',');
					}
					flag2 = false;
					stringBuilder.Append('{');
					J(stringBuilder, "skill", ((object)(SkillType)(ref skill.m_info.m_skill)).ToString());
					C(stringBuilder);
					stringBuilder.Append("\"level\":").Append(Mathf.FloorToInt(skill.m_level));
					stringBuilder.Append('}');
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[gs] skills: " + ex.Message));
			}
			stringBuilder.Append("],");
			stringBuilder.Append("\"creatureKills\":[");
			bool flag3 = true;
			foreach (KeyValuePair<string, int> item3 in dictionary2)
			{
				if (!flag3)
				{
					stringBuilder.Append(',');
				}
				flag3 = false;
				stringBuilder.Append('{');
				J(stringBuilder, "creature", item3.Key);
				C(stringBuilder);
				stringBuilder.Append("\"kills\":").Append(item3.Value);
				stringBuilder.Append('}');
			}
			stringBuilder.Append("],");
			stringBuilder.Append("\"crafts\":[");
			bool flag4 = true;
			foreach (KeyValuePair<string, int> item4 in dictionary3)
			{
				if (!flag4)
				{
					stringBuilder.Append(',');
				}
				flag4 = false;
				stringBuilder.Append('{');
				J(stringBuilder, "item", item4.Key);
				C(stringBuilder);
				stringBuilder.Append("\"count\":").Append(item4.Value);
				stringBuilder.Append('}');
			}
			stringBuilder.Append("],");
			stringBuilder.Append("\"materials\":[");
			bool flag5 = true;
			foreach (KeyValuePair<string, long> material in materials)
			{
				if (!flag5)
				{
					stringBuilder.Append(',');
				}
				flag5 = false;
				stringBuilder.Append('{');
				J(stringBuilder, "material", material.Key);
				C(stringBuilder);
				stringBuilder.Append("\"amount\":").Append(material.Value);
				stringBuilder.Append('}');
			}
			stringBuilder.Append("],");
			stringBuilder.Append("\"weapons\":[");
			bool flag6 = true;
			HashSet<string> hashSet = new HashSet<string>();
			foreach (string key in weaponDamage.Keys)
			{
				hashSet.Add(key);
			}
			foreach (string key2 in weaponKills.Keys)
			{
				hashSet.Add(key2);
			}
			foreach (string item5 in hashSet)
			{
				double value3;
				double a = (weaponDamage.TryGetValue(item5, out value3) ? value3 : 0.0);
				int value4;
				int value5 = (weaponKills.TryGetValue(item5, out value4) ? value4 : 0);
				if (!flag6)
				{
					stringBuilder.Append(',');
				}
				flag6 = false;
				double value6;
				double a2 = (weaponMaxHit.TryGetValue(item5, out value6) ? value6 : 0.0);
				double value7;
				double a3 = (weaponBiggestSwing.TryGetValue(item5, out value7) ? value7 : 0.0);
				stringBuilder.Append('{');
				J(stringBuilder, "weapon", item5);
				C(stringBuilder);
				stringBuilder.Append("\"damageDealt\":").Append(Math.Round(a));
				C(stringBuilder);
				stringBuilder.Append("\"kills\":").Append(value5);
				C(stringBuilder);
				stringBuilder.Append("\"hardestHit\":").Append(Math.Round(a2));
				C(stringBuilder);
				stringBuilder.Append("\"biggestSwing\":").Append(Math.Round(a3));
				stringBuilder.Append('}');
			}
			stringBuilder.Append("],");
			stringBuilder.Append("\"weaponItems\":[");
			bool flag7 = true;
			HashSet<string> hashSet2 = new HashSet<string>();
			foreach (string key3 in itemDamage.Keys)
			{
				hashSet2.Add(key3);
			}
			foreach (string key4 in itemKills.Keys)
			{
				hashSet2.Add(key4);
			}
			foreach (string item6 in hashSet2)
			{
				double value8;
				double a4 = (itemDamage.TryGetValue(item6, out value8) ? value8 : 0.0);
				int value9;
				int value10 = (itemKills.TryGetValue(item6, out value9) ? value9 : 0);
				double value11;
				double a5 = (itemMaxHit.TryGetValue(item6, out value11) ? value11 : 0.0);
				if (!flag7)
				{
					stringBuilder.Append(',');
				}
				flag7 = false;
				stringBuilder.Append('{');
				J(stringBuilder, "item", item6);
				C(stringBuilder);
				stringBuilder.Append("\"damageDealt\":").Append(Math.Round(a4));
				C(stringBuilder);
				stringBuilder.Append("\"kills\":").Append(value10);
				C(stringBuilder);
				stringBuilder.Append("\"hardestHit\":").Append(Math.Round(a5));
				stringBuilder.Append('}');
			}
			stringBuilder.Append("],");
			stringBuilder.Append("\"pickups\":[");
			bool flag8 = true;
			foreach (KeyValuePair<string, int> item7 in dictionary4)
			{
				if (!flag8)
				{
					stringBuilder.Append(',');
				}
				flag8 = false;
				stringBuilder.Append('{');
				J(stringBuilder, "item", item7.Key);
				C(stringBuilder);
				stringBuilder.Append("\"count\":").Append(item7.Value);
				stringBuilder.Append('}');
			}
			stringBuilder.Append("],");
			stringBuilder.Append("\"boss\":[");
			bool flag9 = true;
			foreach (KeyValuePair<string, double> item8 in bossDamage)
			{
				if (!flag9)
				{
					stringBuilder.Append(',');
				}
				flag9 = false;
				double value12;
				int value13 = (bossEngageSec.TryGetValue(item8.Key, out value12) ? ((int)Math.Round(value12)) : 0);
				stringBuilder.Append('{');
				J(stringBuilder, "boss", item8.Key);
				C(stringBuilder);
				stringBuilder.Append("\"damageDealt\":").Append(Math.Round(item8.Value));
				C(stringBuilder);
				stringBuilder.Append("\"fightSec\":").Append(value13);
				stringBuilder.Append('}');
			}
			stringBuilder.Append("]");
			stringBuilder.Append('}');
			foreach (KeyValuePair<string, Combat> item9 in othersCombat)
			{
				stringBuilder.Append(",{");
				J(stringBuilder, "name", item9.Key);
				C(stringBuilder);
				AppendCombat(stringBuilder, item9.Value);
				stringBuilder.Append('}');
			}
			stringBuilder.Append("],");
			stringBuilder.Append("\"deathEvents\":[").Append(string.Join(",", pendingDeaths)).Append(']');
			stringBuilder.Append('}');
			SaveWeapons();
			Post(stringBuilder.ToString());
		}

		private static void AppendCombat(StringBuilder sb, Combat c)
		{
			sb.Append("\"weapons\":[");
			bool flag = true;
			HashSet<string> hashSet = new HashSet<string>();
			foreach (string key in c.wDmg.Keys)
			{
				hashSet.Add(key);
			}
			foreach (string key2 in c.wKill.Keys)
			{
				hashSet.Add(key2);
			}
			foreach (string item in hashSet)
			{
				double value;
				double a = (c.wDmg.TryGetValue(item, out value) ? value : 0.0);
				int value2;
				int value3 = (c.wKill.TryGetValue(item, out value2) ? value2 : 0);
				double value4;
				double a2 = (c.wMax.TryGetValue(item, out value4) ? value4 : 0.0);
				double value5;
				double a3 = (c.wSwing.TryGetValue(item, out value5) ? value5 : 0.0);
				if (!flag)
				{
					sb.Append(',');
				}
				flag = false;
				sb.Append('{');
				J(sb, "weapon", item);
				C(sb);
				sb.Append("\"damageDealt\":").Append(Math.Round(a));
				C(sb);
				sb.Append("\"kills\":").Append(value3);
				C(sb);
				sb.Append("\"hardestHit\":").Append(Math.Round(a2));
				C(sb);
				sb.Append("\"biggestSwing\":").Append(Math.Round(a3));
				sb.Append('}');
			}
			sb.Append("],");
			sb.Append("\"weaponItems\":[");
			bool flag2 = true;
			HashSet<string> hashSet2 = new HashSet<string>();
			foreach (string key3 in c.iDmg.Keys)
			{
				hashSet2.Add(key3);
			}
			foreach (string key4 in c.iKill.Keys)
			{
				hashSet2.Add(key4);
			}
			foreach (string item2 in hashSet2)
			{
				double value6;
				double a4 = (c.iDmg.TryGetValue(item2, out value6) ? value6 : 0.0);
				int value7;
				int value8 = (c.iKill.TryGetValue(item2, out value7) ? value7 : 0);
				double value9;
				double a5 = (c.iMax.TryGetValue(item2, out value9) ? value9 : 0.0);
				if (!flag2)
				{
					sb.Append(',');
				}
				flag2 = false;
				sb.Append('{');
				J(sb, "item", item2);
				C(sb);
				sb.Append("\"damageDealt\":").Append(Math.Round(a4));
				C(sb);
				sb.Append("\"kills\":").Append(value8);
				C(sb);
				sb.Append("\"hardestHit\":").Append(Math.Round(a5));
				sb.Append('}');
			}
			sb.Append("],");
			sb.Append("\"boss\":[");
			bool flag3 = true;
			foreach (KeyValuePair<string, double> item3 in c.bDmg)
			{
				if (!flag3)
				{
					sb.Append(',');
				}
				flag3 = false;
				double value10;
				int value11 = (c.bEng.TryGetValue(item3.Key, out value10) ? ((int)Math.Round(value10)) : 0);
				sb.Append('{');
				J(sb, "boss", item3.Key);
				C(sb);
				sb.Append("\"damageDealt\":").Append(Math.Round(item3.Value));
				C(sb);
				sb.Append("\"fightSec\":").Append(value11);
				sb.Append('}');
			}
			sb.Append("]");
		}

		private void Post(string json)
		{
			try
			{
				StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
				HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, cfgUrl.Value)
				{
					Content = content
				};
				if (!string.IsNullOrEmpty(cfgToken.Value))
				{
					httpRequestMessage.Headers.TryAddWithoutValidation("Authorization", "Bearer " + cfgToken.Value);
				}
				http.SendAsync(httpRequestMessage).ContinueWith(delegate(Task<HttpResponseMessage> t)
				{
					if (t.IsFaulted)
					{
						Log.LogWarning((object)("[gs] post failed: " + t.Exception?.GetBaseException().Message));
					}
					else
					{
						int statusCode = (int)t.Result.StatusCode;
						if (statusCode >= 200 && statusCode < 300)
						{
							pendingDeaths.Clear();
						}
						Log.LogInfo((object)$"[gs] ingest status: {statusCode}");
					}
				});
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[gs] post: " + ex.Message));
			}
		}

		private static void J(StringBuilder sb, string k, string v)
		{
			sb.Append('"').Append(Esc(k)).Append("\":");
			if (v == null)
			{
				sb.Append("null");
			}
			else
			{
				sb.Append('"').Append(Esc(v)).Append('"');
			}
		}

		private static void J(StringBuilder sb, string k, int v)
		{
			sb.Append('"').Append(Esc(k)).Append("\":")
				.Append(v);
		}

		private static void C(StringBuilder sb)
		{
			sb.Append(',');
		}

		private static string Esc(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return s ?? "";
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length + 8);
			foreach (char c in s)
			{
				switch (c)
				{
				case '"':
					stringBuilder.Append("\\\"");
					continue;
				case '\\':
					stringBuilder.Append("\\\\");
					continue;
				case '\n':
					stringBuilder.Append("\\n");
					continue;
				case '\r':
					stringBuilder.Append("\\r");
					continue;
				case '\t':
					stringBuilder.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4"));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}
	}
}