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 wDmg = new Dictionary(); public readonly Dictionary wKill = new Dictionary(); public readonly Dictionary wMax = new Dictionary(); public readonly Dictionary wSwing = new Dictionary(); public readonly Dictionary iDmg = new Dictionary(); public readonly Dictionary iKill = new Dictionary(); public readonly Dictionary iMax = new Dictionary(); public readonly Dictionary bDmg = new Dictionary(); public readonly Dictionary bEng = new Dictionary(); public readonly Dictionary bLast = new Dictionary(); 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(); 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 cfgUrl; private static ConfigEntry cfgToken; private static ConfigEntry cfgWorld; private static ConfigEntry cfgEmitInterval; private static Plugin Instance; private readonly Dictionary crafts = new Dictionary(); private readonly Dictionary materials = new Dictionary(); private readonly Dictionary weaponDamage = new Dictionary(); private readonly Dictionary weaponKills = new Dictionary(); private readonly Dictionary weaponMaxHit = new Dictionary(); private readonly Dictionary weaponBiggestSwing = new Dictionary(); private static string swingWeapon; private static float swingTime; private static double swingDamage; private readonly Dictionary bossDamage = new Dictionary(); private readonly Dictionary bossEngageSec = new Dictionary(); private static readonly Dictionary bossLastHit = new Dictionary(); private readonly Dictionary itemDamage = new Dictionary(); private readonly Dictionary itemKills = new Dictionary(); private readonly Dictionary itemMaxHit = new Dictionary(); private static readonly Dictionary lastWeaponByVictim = new Dictionary(); private static readonly Dictionary lastItemByVictim = new Dictionary(); private string weaponsPath; private readonly Dictionary othersCombat = new Dictionary(); private static readonly Dictionary lastAttackerByVictim = new Dictionary(); private static readonly HashSet BossNames = new HashSet { "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 pendingDeaths = new List(); 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("Ingest", "Url", "https://gs.proudtech.net/api/valheim/ingest", "gs dashboard ingest endpoint."); cfgToken = ((BaseUnityPlugin)this).Config.Bind("Ingest", "Token", "", "Bearer token from the dashboard owner."); cfgWorld = ((BaseUnityPlugin)this).Config.Bind("General", "World", "vhserver3", "World name to file your stats under (must match the server's world)."); cfgEmitInterval = ((BaseUnityPlugin)this).Config.Bind("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 list = new List(); foreach (KeyValuePair item in weaponDamage) { list.Add("wdmg\t" + item.Key + "\t" + item.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair weaponKill in weaponKills) { list.Add($"wkill\t{weaponKill.Key}\t{weaponKill.Value}"); } foreach (KeyValuePair item2 in weaponMaxHit) { list.Add("wmax\t" + item2.Key + "\t" + item2.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair item3 in weaponBiggestSwing) { list.Add("wsw\t" + item3.Key + "\t" + item3.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair item4 in bossDamage) { list.Add("bdmg\t" + item4.Key + "\t" + item4.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair item5 in bossEngageSec) { list.Add("beng\t" + item5.Key + "\t" + item5.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair item6 in itemDamage) { list.Add("idmg\t" + item6.Key + "\t" + item6.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair itemKill in itemKills) { list.Add($"ikill\t{itemKill.Key}\t{itemKill.Value}"); } foreach (KeyValuePair item7 in itemMaxHit) { list.Add("imax\t" + item7.Key + "\t" + item7.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair item8 in othersCombat) { string key = item8.Key; Combat value = item8.Value; foreach (KeyValuePair item9 in value.wDmg) { list.Add("owdmg\t" + key + "\t" + item9.Key + "\t" + item9.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair item10 in value.wKill) { list.Add($"owkill\t{key}\t{item10.Key}\t{item10.Value}"); } foreach (KeyValuePair item11 in value.wMax) { list.Add("owmax\t" + key + "\t" + item11.Key + "\t" + item11.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair item12 in value.wSwing) { list.Add("owsw\t" + key + "\t" + item12.Key + "\t" + item12.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair item13 in value.iDmg) { list.Add("oidmg\t" + key + "\t" + item13.Key + "\t" + item13.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair item14 in value.iKill) { list.Add($"oikill\t{key}\t{item14.Key}\t{item14.Value}"); } foreach (KeyValuePair item15 in value.iMax) { list.Add("oimax\t" + key + "\t" + item15.Key + "\t" + item15.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair item16 in value.bDmg) { list.Add("obdmg\t" + key + "\t" + item16.Key + "\t" + item16.Value.ToString(CultureInfo.InvariantCulture)); } foreach (KeyValuePair 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 statsKv, Dictionary enemyKills, Dictionary craftStats, Dictionary 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 dictionary = new Dictionary(); Dictionary dictionary2 = new Dictionary(); Dictionary dictionary3 = new Dictionary(); Dictionary dictionary4 = new Dictionary(); 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 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 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 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 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 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 hashSet = new HashSet(); 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 hashSet2 = new HashSet(); 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 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 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 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 hashSet = new HashSet(); 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 hashSet2 = new HashSet(); 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 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 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(); } } }