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 GsValheimStatsClient v0.2.1
GsValheimStatsClient.dll
Decompiled 2 hours agousing 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(); } } }