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 GsValheimStatsEmitter v0.2.0
GsValheimStatsEmitter.dll
Decompiled 2 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; 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.Bootstrap; 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("GsValheimStatsEmitter")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.0.0")] [assembly: AssemblyInformationalVersion("0.2.0")] [assembly: AssemblyProduct("GsValheimStatsEmitter")] [assembly: AssemblyTitle("GsValheimStatsEmitter")] [assembly: AssemblyVersion("0.2.0.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 GsValheimStatsEmitter { [BepInPlugin("net.cproudlock.gsvalheimstats", "GsValheimStatsEmitter", "0.2.0")] public class Plugin : BaseUnityPlugin { private class Fight { public float start; public string firstBlood; public Dictionary<string, double> dmg = new Dictionary<string, double>(); } [HarmonyPatch(typeof(Character), "Damage")] private static class Patch_Damage { private static void Postfix(Character __instance, HitData hit) { if (!IsServer() || (Object)(object)__instance == (Object)null || hit == null) { return; } try { string text = ResolvePlayerName(hit); if (Time.unscaledTime - lastDbg > 2f) { lastDbg = Time.unscaledTime; ManualLogSource log = Log; if (log != null) { object[] obj = new object[4] { CleanName(__instance), ((Object)(object)hit.GetAttacker() == (Object)null) ? "null" : ((object)hit.GetAttacker()).GetType().Name, text ?? "?", null }; ZNet instance = ZNet.instance; obj[3] = ((instance == null) ? null : instance.GetPeers()?.Count).GetValueOrDefault(-1); log.LogInfo((object)string.Format("[gs-dbg] dmg victim={0} attacker={1} resolved={2} peers={3}", obj)); } } if (text == null || IsPlayerCharacter(__instance)) { return; } float totalDamage = hit.GetTotalDamage(); int instanceID = ((Object)__instance).GetInstanceID(); lastHitter[instanceID] = text; string text2 = SafeSkill(hit); lastWeapon[instanceID] = text2; Instance?.AddWeaponDamage(text, text2, totalDamage); string key = CleanName(__instance); if (Bosses.TryGetValue(key, out var value)) { if (!fights.TryGetValue(value, out var value2)) { value2 = new Fight { start = Time.time, firstBlood = text }; fights[value] = value2; } value2.dmg.TryGetValue(text, out var value3); value2.dmg[text] = value3 + (double)totalDamage; Instance?.AddBossDamage(value, text, totalDamage); } } catch (Exception ex) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("[gs] dmg patch: " + ex.Message)); } } } } [HarmonyPatch(typeof(Character), "OnDeath")] private static class Patch_OnDeath { private static void Postfix(Character __instance) { if (!IsServer() || (Object)(object)__instance == (Object)null) { return; } try { if (IsPlayerCharacter(__instance)) { return; } int instanceID = ((Object)__instance).GetInstanceID(); lastHitter.TryGetValue(instanceID, out var value); lastWeapon.TryGetValue(instanceID, out var value2); string text = CleanName(__instance); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("[gs-dbg] death victim=" + text + " killer=" + (value ?? "?") + " weapon=" + (value2 ?? "?"))); } if (Bosses.TryGetValue(text, out var value3)) { if (!string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(value2)) { Instance?.AddWeaponKill(value, value2); } Instance?.OnBossKilled(value3); } else if (!string.IsNullOrEmpty(value)) { Instance?.AddCreatureKill(value, text); if (!string.IsNullOrEmpty(value2)) { Instance?.AddWeaponKill(value, value2); } } lastHitter.Remove(instanceID); lastWeapon.Remove(instanceID); } catch (Exception ex) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("[gs] death patch: " + ex.Message)); } } } } public const string GUID = "net.cproudlock.gsvalheimstats"; public const string NAME = "GsValheimStatsEmitter"; public const string VERSION = "0.2.0"; 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 readonly Dictionary<string, string> Bosses = new Dictionary<string, string> { { "Eikthyr", "Eikthyr" }, { "gd_king", "gd_king" }, { "Bonemass", "Bonemass" }, { "Dragon", "Dragon" }, { "GoblinKing", "GoblinKing" }, { "SeekerQueen", "SeekerQueen" }, { "Fader", "Fader" } }; private static readonly Dictionary<string, string> KeyLabels = new Dictionary<string, string> { { "defeated_eikthyr", "Eikthyr defeated" }, { "defeated_gdking", "The Elder defeated" }, { "defeated_bonemass", "Bonemass defeated" }, { "defeated_dragon", "Moder defeated" }, { "defeated_goblinking", "Yagluth defeated" }, { "defeated_queen", "The Queen defeated" }, { "defeated_fader", "Fader defeated" }, { "KilledTroll", "First troll slain" }, { "killed_surtling", "First surtling slain" }, { "killed_bonemass", "Bonemass slain" }, { "Hildir1", "Hildir's bounty: Howling Cavern" }, { "Hildir2", "Hildir's bounty: Plains Fortress" }, { "Hildir3", "Hildir's bounty: Crypt" } }; private readonly Dictionary<string, Dictionary<string, double>> bossDamage = new Dictionary<string, Dictionary<string, double>>(); private readonly Dictionary<string, Dictionary<string, int>> bossKills = new Dictionary<string, Dictionary<string, int>>(); private readonly Dictionary<string, Dictionary<string, int>> creatureKills = new Dictionary<string, Dictionary<string, int>>(); private readonly Dictionary<string, Dictionary<string, double>> weaponDamage = new Dictionary<string, Dictionary<string, double>>(); private readonly Dictionary<string, Dictionary<string, int>> weaponKills = new Dictionary<string, Dictionary<string, int>>(); private readonly Dictionary<string, Dictionary<string, double>> weaponMaxHit = new Dictionary<string, Dictionary<string, double>>(); private readonly HashSet<string> knownKeys = new HashSet<string>(); private static readonly Dictionary<string, Fight> fights = new Dictionary<string, Fight>(); private static readonly Dictionary<int, string> lastHitter = new Dictionary<int, string>(); private static readonly Dictionary<int, string> lastWeapon = new Dictionary<int, string>(); private readonly List<string> pendingBossKills = new List<string>(); private readonly List<string> pendingMilestones = new List<string>(); private static Plugin Instance; private string statePath; private string serverStartedUtc; private float lastEmit; private float lastKeyPoll; private static float lastDbg; private void Awake() { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; cfgUrl = ((BaseUnityPlugin)this).Config.Bind<string>("Ingest", "Url", "http://localhost:3001/api/valheim/ingest", "gs dashboard ingest endpoint."); cfgToken = ((BaseUnityPlugin)this).Config.Bind<string>("Ingest", "Token", "", "Bearer token required by the gs dashboard."); cfgWorld = ((BaseUnityPlugin)this).Config.Bind<string>("General", "World", "vhserver3", "World name this server runs (scopes all stats in the dashboard)."); cfgEmitInterval = ((BaseUnityPlugin)this).Config.Bind<int>("General", "EmitIntervalSeconds", 120, "How often to POST the cumulative snapshot."); serverStartedUtc = DateTime.UtcNow.ToString("O"); statePath = Path.Combine(Paths.ConfigPath, "net.cproudlock.gsvalheimstats.state.tsv"); LoadState(); Harmony val = new Harmony("net.cproudlock.gsvalheimstats"); try { MethodInfo methodInfo = AccessTools.Method(typeof(Character), "RPC_Damage", new Type[2] { typeof(long), typeof(HitData) }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(Character), "OnDeath", (Type[])null, (Type[])null); if (methodInfo != null) { val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(Patch_Damage), "Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } if (methodInfo2 != null) { val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(Patch_OnDeath), "Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } Log.LogInfo((object)$"[gs] patched Character.RPC_Damage={methodInfo != null} Character.OnDeath={methodInfo2 != null}"); } catch (Exception arg) { Log.LogError((object)$"[gs] patch error: {arg}"); } Log.LogInfo((object)("GsValheimStatsEmitter 0.2.0 loaded, posting to " + cfgUrl.Value + " (token " + (string.IsNullOrEmpty(cfgToken.Value) ? "MISSING" : "set") + ")")); } private static bool IsServer() { try { return (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer(); } catch { return false; } } private static string CleanName(Character c) { if ((Object)(object)c == (Object)null) { return "unknown"; } string text = (((Object)(object)((Component)c).gameObject != (Object)null) ? ((Object)((Component)c).gameObject).name : ((Object)c).name); if (string.IsNullOrEmpty(text)) { return "unknown"; } int num = text.IndexOf('('); if (num > 0) { text = text.Substring(0, num); } return text.Trim(); } private static string ResolvePlayerName(HitData hit) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) try { Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if (val != null) { string playerName = val.GetPlayerName(); if (!string.IsNullOrEmpty(playerName) && playerName != "...") { return playerName; } } } catch { } try { ZDOID attacker2 = hit.m_attacker; if ((Object)(object)ZNet.instance != (Object)null && attacker2 != ZDOID.None) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.m_characterID == attacker2 && !string.IsNullOrEmpty(peer.m_playerName)) { return peer.m_playerName; } } } } catch { } return null; } private static bool IsPlayerCharacter(Character c) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) try { if (c is Player) { return true; } ZNetView component = ((Component)c).GetComponent<ZNetView>(); ZDO val = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (val != null && (Object)(object)ZNet.instance != (Object)null) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.m_characterID == val.m_uid) { return true; } } } } catch { } return false; } private void AddBossDamage(string boss, string player, double dmg) { if (!bossDamage.TryGetValue(boss, out var value)) { value = new Dictionary<string, double>(); bossDamage[boss] = value; } value.TryGetValue(player, out var value2); value[player] = value2 + dmg; } private void AddCreatureKill(string player, string creature) { if (!creatureKills.TryGetValue(player, out var value)) { value = new Dictionary<string, int>(); creatureKills[player] = value; } value.TryGetValue(creature, out var value2); value[creature] = value2 + 1; } 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 void AddWeaponDamage(string player, string weapon, double dmg) { if (!string.IsNullOrEmpty(weapon)) { if (!weaponDamage.TryGetValue(player, out var value)) { value = new Dictionary<string, double>(); weaponDamage[player] = value; } value.TryGetValue(weapon, out var value2); value[weapon] = value2 + dmg; if (!weaponMaxHit.TryGetValue(player, out var value3)) { value3 = new Dictionary<string, double>(); weaponMaxHit[player] = value3; } value3.TryGetValue(weapon, out var value4); if (dmg > value4) { value3[weapon] = dmg; } } } private void AddWeaponKill(string player, string weapon) { if (!string.IsNullOrEmpty(weapon)) { if (!weaponKills.TryGetValue(player, out var value)) { value = new Dictionary<string, int>(); weaponKills[player] = value; } value.TryGetValue(weapon, out var value2); value[weapon] = value2 + 1; } } private void OnBossKilled(string boss) { fights.TryGetValue(boss, out var value); Dictionary<string, double> dictionary = value?.dmg ?? new Dictionary<string, double>(); string v = value?.firstBlood; float num = ((value != null) ? (Time.time - value.start) : 0f); string text = null; double num2 = 0.0; foreach (KeyValuePair<string, double> item in dictionary) { if (item.Value > num2) { num2 = item.Value; text = item.Key; } } if (!bossKills.TryGetValue(boss, out var value2)) { value2 = new Dictionary<string, int>(); bossKills[boss] = value2; } foreach (string key in dictionary.Keys) { value2.TryGetValue(key, out var value3); value2[key] = value3 + 1; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('{'); J(stringBuilder, "boss", boss); C(stringBuilder); stringBuilder.Append("\"fightSec\":").Append(Mathf.RoundToInt(num)); C(stringBuilder); J(stringBuilder, "firstBlood", v); C(stringBuilder); J(stringBuilder, "topDamagePlayer", text); C(stringBuilder); stringBuilder.Append("\"topDamage\":").Append(Math.Round(num2)); C(stringBuilder); stringBuilder.Append("\"participants\":").Append(dictionary.Count); C(stringBuilder); J(stringBuilder, "tsUtc", DateTime.UtcNow.ToString("O")); stringBuilder.Append('}'); pendingBossKills.Add(stringBuilder.ToString()); fights.Remove(boss); Log.LogInfo((object)$"[gs] boss killed: {boss} (MVP {text}, {dictionary.Count} participant(s))"); SaveState(); } private void Update() { if (!IsServer()) { return; } float unscaledTime = Time.unscaledTime; if (unscaledTime - lastKeyPoll > 10f) { lastKeyPoll = unscaledTime; try { List<string> list = (((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.GetGlobalKeys() : null); if (list != null) { foreach (string item in list) { if (!string.IsNullOrEmpty(item) && !knownKeys.Contains(item)) { knownKeys.Add(item); string value; string text = (KeyLabels.TryGetValue(item, out value) ? value : item); string v = (item.StartsWith("defeated_") ? "boss" : (item.StartsWith("Hildir") ? "bounty" : "progression")); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('{'); J(stringBuilder, "key", item); C(stringBuilder); J(stringBuilder, "label", text); C(stringBuilder); J(stringBuilder, "kind", v); C(stringBuilder); J(stringBuilder, "tsUtc", DateTime.UtcNow.ToString("O")); stringBuilder.Append('}'); pendingMilestones.Add(stringBuilder.ToString()); Log.LogInfo((object)("[gs] milestone: " + item + " (" + text + ")")); } } } } catch (Exception ex) { Log.LogWarning((object)("[gs] key poll: " + ex.Message)); } } if (!(unscaledTime - lastEmit > (float)Mathf.Max(15, cfgEmitInterval.Value))) { return; } lastEmit = unscaledTime; try { Emit(); } catch (Exception arg) { Log.LogError((object)$"[gs] emit: {arg}"); } } private void Emit() { HashSet<string> hashSet = new HashSet<string>(); foreach (Dictionary<string, double> value12 in bossDamage.Values) { foreach (string key in value12.Keys) { hashSet.Add(key); } } foreach (Dictionary<string, int> value13 in bossKills.Values) { foreach (string key2 in value13.Keys) { hashSet.Add(key2); } } foreach (string key3 in creatureKills.Keys) { hashSet.Add(key3); } foreach (string key4 in weaponDamage.Keys) { hashSet.Add(key4); } foreach (string key5 in weaponKills.Keys) { hashSet.Add(key5); } StringBuilder stringBuilder = new StringBuilder(2048); stringBuilder.Append('{'); J(stringBuilder, "schemaVersion", 1); C(stringBuilder); J(stringBuilder, "game", "valheim"); C(stringBuilder); J(stringBuilder, "source", "server"); C(stringBuilder); J(stringBuilder, "world", cfgWorld.Value); C(stringBuilder); J(stringBuilder, "hostName", "Proudlock VH Server 3"); C(stringBuilder); J(stringBuilder, "serverStartedAtUtc", serverStartedUtc); C(stringBuilder); J(stringBuilder, "emittedAtUtc", DateTime.UtcNow.ToString("O")); C(stringBuilder); J(stringBuilder, "snapshotIdLocal", Guid.NewGuid().ToString("N")); C(stringBuilder); stringBuilder.Append("\"players\":["); bool flag = true; foreach (string item in hashSet) { if (!flag) { stringBuilder.Append(','); } flag = false; stringBuilder.Append('{'); J(stringBuilder, "name", item); C(stringBuilder); stringBuilder.Append("\"boss\":["); bool flag2 = true; foreach (string item2 in Bosses.Values.Distinct()) { Dictionary<string, double> value; double value2; double num = ((bossDamage.TryGetValue(item2, out value) && value.TryGetValue(item, out value2)) ? value2 : 0.0); Dictionary<string, int> value3; int value4; int num2 = ((bossKills.TryGetValue(item2, out value3) && value3.TryGetValue(item, out value4)) ? value4 : 0); if (!(num <= 0.0) || num2 > 0) { if (!flag2) { stringBuilder.Append(','); } flag2 = false; stringBuilder.Append('{'); J(stringBuilder, "boss", item2); C(stringBuilder); stringBuilder.Append("\"kills\":").Append(num2); C(stringBuilder); stringBuilder.Append("\"damageDealt\":").Append(Math.Round(num)); stringBuilder.Append('}'); } } stringBuilder.Append("],"); stringBuilder.Append("\"weapons\":["); bool flag3 = true; Dictionary<string, double> value5; Dictionary<string, double> dictionary = (weaponDamage.TryGetValue(item, out value5) ? value5 : null); Dictionary<string, int> value6; Dictionary<string, int> dictionary2 = (weaponKills.TryGetValue(item, out value6) ? value6 : null); Dictionary<string, double> value7; Dictionary<string, double> dictionary3 = (weaponMaxHit.TryGetValue(item, out value7) ? value7 : null); HashSet<string> hashSet2 = new HashSet<string>(); if (dictionary != null) { foreach (string key6 in dictionary.Keys) { hashSet2.Add(key6); } } if (dictionary2 != null) { foreach (string key7 in dictionary2.Keys) { hashSet2.Add(key7); } } if (dictionary3 != null) { foreach (string key8 in dictionary3.Keys) { hashSet2.Add(key8); } } foreach (string item3 in hashSet2) { double value8; double a = ((dictionary != null && dictionary.TryGetValue(item3, out value8)) ? value8 : 0.0); int value9; int value10 = ((dictionary2 != null && dictionary2.TryGetValue(item3, out value9)) ? value9 : 0); double value11; double a2 = ((dictionary3 != null && dictionary3.TryGetValue(item3, out value11)) ? value11 : 0.0); if (!flag3) { stringBuilder.Append(','); } flag3 = false; stringBuilder.Append('{'); J(stringBuilder, "weapon", item3); C(stringBuilder); stringBuilder.Append("\"damageDealt\":").Append(Math.Round(a)); C(stringBuilder); stringBuilder.Append("\"kills\":").Append(value10); C(stringBuilder); stringBuilder.Append("\"hardestHit\":").Append(Math.Round(a2)); stringBuilder.Append('}'); } stringBuilder.Append("]"); stringBuilder.Append('}'); } stringBuilder.Append("],"); stringBuilder.Append("\"bossKillEvents\":[").Append(string.Join(",", pendingBossKills)).Append("],"); stringBuilder.Append("\"milestones\":[").Append(string.Join(",", pendingMilestones)).Append("],"); stringBuilder.Append("\"mods\":["); AppendMods(stringBuilder); stringBuilder.Append(']'); stringBuilder.Append('}'); Post(stringBuilder.ToString()); } 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) { pendingBossKills.Clear(); pendingMilestones.Clear(); } Log.LogInfo((object)$"[gs] ingest status: {statusCode}"); } }); } catch (Exception ex) { Log.LogWarning((object)("[gs] post: " + ex.Message)); } } private void AppendMods(StringBuilder sb) { bool flag = true; try { foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos) { PluginInfo value = pluginInfo.Value; if (((value != null) ? value.Metadata : null) == null) { continue; } string text = ""; string v = ""; try { string directoryName = Path.GetDirectoryName(value.Location ?? ""); if (!string.IsNullOrEmpty(directoryName)) { text = Path.GetFileName(directoryName); int num = text.IndexOf('-'); if (num > 0) { v = text.Substring(0, num); } } } catch { } if (!flag) { sb.Append(','); } flag = false; sb.Append('{'); J(sb, "guid", value.Metadata.GUID ?? ""); C(sb); J(sb, "name", value.Metadata.Name ?? ""); C(sb); J(sb, "version", value.Metadata.Version?.ToString() ?? ""); C(sb); J(sb, "author", v); C(sb); J(sb, "folder", text); sb.Append('}'); } } catch (Exception ex) { Log.LogWarning((object)("[gs] mods: " + ex.Message)); } } private void SaveState() { try { List<string> list = new List<string>(); foreach (KeyValuePair<string, Dictionary<string, double>> item in bossDamage) { foreach (KeyValuePair<string, double> item2 in item.Value) { list.Add("bossdmg\t" + item.Key + "\t" + item2.Key + "\t" + item2.Value.ToString(CultureInfo.InvariantCulture)); } } foreach (KeyValuePair<string, Dictionary<string, int>> bossKill in bossKills) { foreach (KeyValuePair<string, int> item3 in bossKill.Value) { list.Add($"bosskill\t{bossKill.Key}\t{item3.Key}\t{item3.Value}"); } } foreach (KeyValuePair<string, Dictionary<string, int>> creatureKill in creatureKills) { foreach (KeyValuePair<string, int> item4 in creatureKill.Value) { list.Add($"ckill\t{creatureKill.Key}\t{item4.Key}\t{item4.Value}"); } } foreach (KeyValuePair<string, Dictionary<string, double>> item5 in weaponDamage) { foreach (KeyValuePair<string, double> item6 in item5.Value) { list.Add("wdmg\t" + item5.Key + "\t" + item6.Key + "\t" + item6.Value.ToString(CultureInfo.InvariantCulture)); } } foreach (KeyValuePair<string, Dictionary<string, int>> weaponKill in weaponKills) { foreach (KeyValuePair<string, int> item7 in weaponKill.Value) { list.Add($"wkill\t{weaponKill.Key}\t{item7.Key}\t{item7.Value}"); } } foreach (KeyValuePair<string, Dictionary<string, double>> item8 in weaponMaxHit) { foreach (KeyValuePair<string, double> item9 in item8.Value) { list.Add("wmax\t" + item8.Key + "\t" + item9.Key + "\t" + item9.Value.ToString(CultureInfo.InvariantCulture)); } } foreach (string knownKey in knownKeys) { list.Add("gkey\t" + knownKey); } File.WriteAllLines(statePath, list); } catch (Exception ex) { Log.LogWarning((object)("[gs] save state: " + ex.Message)); } } private void LoadState() { try { if (!File.Exists(statePath)) { return; } string[] array = File.ReadAllLines(statePath); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split('\t'); if (array2.Length == 0) { continue; } switch (array2[0]) { case "bossdmg": if (array2.Length == 4) { if (!bossDamage.TryGetValue(array2[1], out var value5)) { value5 = new Dictionary<string, double>(); bossDamage[array2[1]] = value5; } value5[array2[2]] = double.Parse(array2[3], CultureInfo.InvariantCulture); } break; case "bosskill": if (array2.Length == 4) { if (!bossKills.TryGetValue(array2[1], out var value3)) { value3 = new Dictionary<string, int>(); bossKills[array2[1]] = value3; } value3[array2[2]] = int.Parse(array2[3]); } break; case "ckill": if (array2.Length == 4) { if (!creatureKills.TryGetValue(array2[1], out var value)) { value = new Dictionary<string, int>(); creatureKills[array2[1]] = value; } value[array2[2]] = int.Parse(array2[3]); } break; case "wdmg": if (array2.Length == 4) { if (!weaponDamage.TryGetValue(array2[1], out var value4)) { value4 = new Dictionary<string, double>(); weaponDamage[array2[1]] = value4; } value4[array2[2]] = double.Parse(array2[3], CultureInfo.InvariantCulture); } break; case "wkill": if (array2.Length == 4) { if (!weaponKills.TryGetValue(array2[1], out var value6)) { value6 = new Dictionary<string, int>(); weaponKills[array2[1]] = value6; } value6[array2[2]] = int.Parse(array2[3]); } break; case "wmax": if (array2.Length == 4) { if (!weaponMaxHit.TryGetValue(array2[1], out var value2)) { value2 = new Dictionary<string, double>(); weaponMaxHit[array2[1]] = value2; } value2[array2[2]] = double.Parse(array2[3], CultureInfo.InvariantCulture); } break; case "gkey": if (array2.Length == 2) { knownKeys.Add(array2[1]); } break; } } Log.LogInfo((object)$"[gs] loaded state: {bossDamage.Count} boss(es), {creatureKills.Count} player(s), {knownKeys.Count} key(s)"); } catch (Exception ex) { Log.LogWarning((object)("[gs] load state: " + 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(); } } }