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 RealmForgeStats v1.1.1
plugins/RealmForgeStats/RealmForgeStats.dll
Decompiled a month agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RealmForgeStats")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+169585884d24fa6066e4c873136bb6cb6dd9421d")] [assembly: AssemblyProduct("RealmForgeStats")] [assembly: AssemblyTitle("RealmForgeStats")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace RealmForgeStats { internal static class Feats { internal static readonly KeyValuePair<string, PlayerStatType>[] Counters = new KeyValuePair<string, PlayerStatType>[35] { P("kills", (PlayerStatType)6), P("hits_dealt", (PlayerStatType)5), P("hits_taken", (PlayerStatType)10), P("arrows", (PlayerStatType)51), P("boss_kills", (PlayerStatType)85), P("boss_lasthits", (PlayerStatType)86), P("boss_solo", (PlayerStatType)147), P("boss_group", (PlayerStatType)146), P("tree_chops", (PlayerStatType)27), P("trees", (PlayerStatType)28), P("logs", (PlayerStatType)36), P("walked", (PlayerStatType)17), P("ran", (PlayerStatType)18), P("sailed", (PlayerStatType)19), P("airborne", (PlayerStatType)20), P("portals", (PlayerStatType)15), P("deepest_dungeon", (PlayerStatType)149), P("pieces_built", (PlayerStatType)168), P("tallest_build", (PlayerStatType)130), P("max_comfort", (PlayerStatType)132), P("mine_hits", (PlayerStatType)37), P("ore_mined", (PlayerStatType)38), P("bees", (PlayerStatType)76), P("sap", (PlayerStatType)77), P("buried_treasure", (PlayerStatType)133), P("fish_caught", (PlayerStatType)160), P("fish_hooked", (PlayerStatType)158), P("fish_lost", (PlayerStatType)159), P("days_survived", (PlayerStatType)128), P("days_record", (PlayerStatType)129), P("sleeps", (PlayerStatType)23), P("tamed", (PlayerStatType)48), P("pets_given", (PlayerStatType)191), P("jumps", (PlayerStatType)3), P("raven_talks", (PlayerStatType)46) }; internal static readonly KeyValuePair<string, PlayerStatType>[] TreeDeaths = new KeyValuePair<string, PlayerStatType>[10] { P("Fir", (PlayerStatType)193), P("Oak", (PlayerStatType)194), P("Pine", (PlayerStatType)195), P("Beech", (PlayerStatType)199), P("Birch", (PlayerStatType)200), P("Swamp", (PlayerStatType)198), P("Snow fir", (PlayerStatType)201), P("Snow pine", (PlayerStatType)202), P("Ashlands", (PlayerStatType)196), P("Yggdrasil", (PlayerStatType)197) }; internal const int TopCreatures = 30; private static KeyValuePair<string, PlayerStatType> P(string k, PlayerStatType v) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return new KeyValuePair<string, PlayerStatType>(k, v); } } [BepInPlugin("org.zrikzinc.realmforgestats", "RealmForge Stats", "1.1.1")] public class RealmForgeStatsPlugin : BaseUnityPlugin { public const string GUID = "org.zrikzinc.realmforgestats"; internal const string RPC = "RealmForgeStats"; private const float SendIntervalSec = 120f; internal static ManualLogSource Log; private float _next; private string _lastPayload = ""; private const float RetryWhenNotReadySec = 5f; internal static readonly Dictionary<PlayerStatType, string> DeathStats = new Dictionary<PlayerStatType, string> { { (PlayerStatType)0, "total" }, { (PlayerStatType)56, "enemy" }, { (PlayerStatType)57, "player" }, { (PlayerStatType)58, "fall" }, { (PlayerStatType)59, "drowning" }, { (PlayerStatType)60, "burning" }, { (PlayerStatType)61, "freezing" }, { (PlayerStatType)62, "poison" }, { (PlayerStatType)63, "smoke" }, { (PlayerStatType)65, "edge_of_world" }, { (PlayerStatType)66, "impact" }, { (PlayerStatType)67, "cart" }, { (PlayerStatType)68, "tree" }, { (PlayerStatType)69, "self" }, { (PlayerStatType)70, "structure" }, { (PlayerStatType)71, "turret" }, { (PlayerStatType)72, "boat" }, { (PlayerStatType)73, "stalactite" }, { (PlayerStatType)203, "drawbridge" }, { (PlayerStatType)204, "lava" } }; private void Awake() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; new Harmony("org.zrikzinc.realmforgestats").PatchAll(); Log.LogInfo((object)"RealmForgeStats 1.1.1 loaded"); } private void Update() { if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && !(Time.time < _next)) { if ((Object)(object)Player.m_localPlayer == (Object)null || ZRoutedRpc.instance == null || (Object)(object)Game.instance == (Object)null) { _next = Time.time + 5f; return; } _next = Time.time + 120f; TrySend(); } } private void TrySend() { try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ZRoutedRpc.instance == null) { return; } string text = Collect(localPlayer); if (!string.IsNullOrEmpty(text) && !(text == _lastPayload)) { _lastPayload = text; ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null) { ZRoutedRpc.instance.InvokeRoutedRPC(serverPeer.m_uid, "RealmForgeStats", new object[1] { text }); } } } catch (Exception ex) { Log.LogWarning((object)("stats send failed: " + ex.Message)); } } private static string Collect(Player player) { //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected I4, but got Unknown PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null); if (val == null) { return null; } StringBuilder stringBuilder = new StringBuilder(512); stringBuilder.Append('{'); Json.Str(stringBuilder, "name", player.GetPlayerName()); stringBuilder.Append(','); Json.Num(stringBuilder, "playerId", val.GetPlayerID()); stringBuilder.Append(",\"skills\":{"); Skills skills = ((Character)player).GetSkills(); bool flag = true; if ((Object)(object)skills != (Object)null) { foreach (Skill skill in skills.GetSkillList()) { if (skill != null && skill.m_info != null && !(skill.m_level <= 0f)) { if (!flag) { stringBuilder.Append(','); } flag = false; Json.Num(stringBuilder, ((int)skill.m_info.m_skill).ToString(CultureInfo.InvariantCulture), skill.m_level); } } } stringBuilder.Append("},\"deaths\":{"); flag = true; foreach (KeyValuePair<PlayerStatType, string> deathStat in DeathStats) { float stat = val.GetStat(deathStat.Key); if (!(stat <= 0f)) { if (!flag) { stringBuilder.Append(','); } flag = false; Json.Num(stringBuilder, deathStat.Value, stat); } } stringBuilder.Append("},\"feats\":{"); flag = AppendCounters(stringBuilder, val, Feats.Counters, first: true); stringBuilder.Append("},\"tree_deaths\":{"); AppendCounters(stringBuilder, val, Feats.TreeDeaths, first: true); stringBuilder.Append("},\"kills\":{"); AppendKills(stringBuilder, val); stringBuilder.Append("}}"); return stringBuilder.ToString(); } private static bool AppendCounters(StringBuilder sb, PlayerProfile profile, KeyValuePair<string, PlayerStatType>[] table, bool first) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < table.Length; i++) { KeyValuePair<string, PlayerStatType> keyValuePair = table[i]; float stat = profile.GetStat(keyValuePair.Value); if (!(stat <= 0f)) { if (!first) { sb.Append(','); } first = false; Json.Num(sb, keyValuePair.Key, stat); } } return first; } private static void AppendKills(StringBuilder sb, PlayerProfile profile) { PlayerStats val = profile.m_playerStats[0]; Dictionary<string, float> dictionary = ((val != null && val.m_enemyStats != null && val.m_enemyStats.Length != 0) ? val.m_enemyStats[0] : null); if (dictionary == null || dictionary.Count == 0) { return; } List<KeyValuePair<string, float>> list = new List<KeyValuePair<string, float>>(dictionary); list.Sort((KeyValuePair<string, float> a, KeyValuePair<string, float> b) => b.Value.CompareTo(a.Value)); int num = 0; foreach (KeyValuePair<string, float> item in list) { if (num >= 30) { break; } if (!(item.Value <= 0f) && !string.IsNullOrEmpty(item.Key)) { if (num > 0) { sb.Append(','); } Json.Num(sb, item.Key, item.Value); num++; } } } } [HarmonyPatch(typeof(Game), "Start")] public static class RegisterRpcPatch { private static void Postfix() { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register<string>("RealmForgeStats", (Action<long, string>)StatsStore.OnStats); RealmForgeStatsPlugin.Log.LogInfo((object)"RealmForgeStats RPC registered"); } } } internal static class StatsStore { private const int MaxPayload = 16384; private static readonly object Lock = new object(); private static readonly Dictionary<long, string> Rows = new Dictionary<long, string>(); internal static void OnStats(long sender, string payload) { try { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || string.IsNullOrEmpty(payload) || payload.Length > 16384) { return; } payload = Stamp(payload); if (payload != null) { bool flag; lock (Lock) { flag = !Rows.ContainsKey(sender); Rows[sender] = payload; Write(); } if (flag) { RealmForgeStatsPlugin.Log.LogInfo((object)("stats received from peer " + sender + " (" + payload.Length + " bytes) -> " + Path.Combine(Dir(), "live-stats.json"))); } } } catch (Exception ex) { RealmForgeStatsPlugin.Log.LogWarning((object)("stats receive failed: " + ex.Message)); } } private static string Stamp(string payload) { string text = payload.TrimStart(); if (!text.StartsWith("{") || text.Length < 2) { return null; } string text2 = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture); return "{\"received\":\"" + text2 + "\"," + text.Substring(1); } private static string Dir() { string environmentVariable = Environment.GetEnvironmentVariable("REALMFORGE_STATS_DIR"); if (!string.IsNullOrEmpty(environmentVariable)) { return environmentVariable; } return Path.Combine(Directory.GetCurrentDirectory(), "..", "stats"); } private static void Write() { string text = Dir(); Directory.CreateDirectory(text); StringBuilder stringBuilder = new StringBuilder(1024); stringBuilder.Append("{\"source\":\"RealmForgeStats\",\"players\":["); bool flag = true; foreach (string value in Rows.Values) { if (!flag) { stringBuilder.Append(','); } flag = false; stringBuilder.Append(value); } stringBuilder.Append("]}"); string text2 = Path.Combine(text, "live-stats.json"); string text3 = text2 + ".tmp"; File.WriteAllText(text3, stringBuilder.ToString()); if (File.Exists(text2)) { File.Delete(text2); } File.Move(text3, text2); } } internal static class Json { internal static void Str(StringBuilder sb, string key, string value) { sb.Append('"').Append(Escape(key)).Append("\":\"") .Append(Escape(value ?? "")) .Append('"'); } internal static void Num(StringBuilder sb, string key, double value) { sb.Append('"').Append(Escape(key)).Append("\":") .Append(value.ToString("0.###", CultureInfo.InvariantCulture)); } private static string Escape(string 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(); } } }