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 StatsLogger v1.2.6
gnp_StatsLogger/ValheimStatsLogger.dll
Decompiled a year ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using HarmonyLib; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ValheimStatsLogger")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ValheimStatsLogger")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("5c29271f-5dca-4909-aabc-9558286a0409")] [assembly: AssemblyFileVersion("1.2.6.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.2.6.0")] namespace ValheimStatsLogger; [HarmonyPatch] public static class BlockAndStagger { [HarmonyPatch(typeof(Humanoid), "BlockAttack")] public static class Humanoid_BlockAttack_BlockAndStagger { private static MethodInfo miBlockDamage = AccessTools.Method(typeof(HitData), "BlockDamage", (Type[])null, (Type[])null); private static MethodInfo miStagger = AccessTools.Method(typeof(Character), "Stagger", (Type[])null, (Type[])null); private static MethodInfo miBlockTrigger = AccessTools.Method(typeof(Humanoid_BlockAttack_BlockAndStagger), "BlockTrigger", (Type[])null, (Type[])null); private static MethodInfo miStaggerTrigger = AccessTools.Method(typeof(Humanoid_BlockAttack_BlockAndStagger), "StaggerTrigger", (Type[])null, (Type[])null); private static readonly int Inserts = 2; [HarmonyTranspiler] public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Expected O, but got Unknown //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Expected O, but got Unknown //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Expected O, but got Unknown //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Expected O, but got Unknown int num = 0; List<CodeInstruction> list = new List<CodeInstruction>(instructions); for (int i = 0; i < list.Count; i++) { if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == miBlockDamage) { list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)miBlockTrigger)); list.Insert(i + 1, list[i - 1]); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_2, (object)null)); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_1, (object)null)); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null)); DebugLogger.Log($"{typeof(Humanoid_BlockAttack_BlockAndStagger)} [Humanoid BlockAttack {miBlockTrigger.Name}] applied."); num++; } if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == miStagger && list[i - 4].opcode == OpCodes.Ldarg_2) { list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)miStaggerTrigger)); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_2, (object)null)); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_1, (object)null)); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null)); DebugLogger.Log($"{typeof(Humanoid_BlockAttack_BlockAndStagger)} [Humanoid BlockAttack {miStaggerTrigger.Name}] applied."); num++; } } if (num == 0) { DebugLogger.LogError($"{typeof(Humanoid_BlockAttack_BlockAndStagger)} [Humanoid BlockAttack] failed."); } else if (num != Inserts) { DebugLogger.LogWarning($"{typeof(Humanoid_BlockAttack_BlockAndStagger)} [Humanoid BlockAttack] applied {num}/{Inserts}."); } return list.AsEnumerable(); } public static void BlockTrigger(Humanoid target, HitData hit, Character attacker, float dmg) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (StatsLogger.IsLocalPlayer((Character)(object)target)) { ItemData weapon = null; if (((object)attacker).GetType() == typeof(Humanoid)) { weapon = ((Humanoid)attacker).GetCurrentWeapon(); } StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Blocks, attacker, weapon, 1f); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.BlockedDamage, attacker, weapon, dmg); StatsLogger.Stats.UpdateTotal(Settings.LogActions.Blocks, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.BlockedDamage, dmg); } } public static void StaggerTrigger(Humanoid target, HitData hit, Character attacker) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (StatsLogger.IsLocalPlayer((Character)(object)target)) { ItemData weapon = null; if (((object)attacker).GetType() == typeof(Humanoid)) { weapon = ((Humanoid)attacker).GetCurrentWeapon(); } StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Staggers, attacker, weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.Staggers, 1f); } } } } internal class ConsoleAndChat { [HarmonyPatch(typeof(Terminal), "InitTerminal")] private class AddCommands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; internal void <Postfix>b__0_0(ConsoleEventArgs args) { //IL_0480: Unknown result type (might be due to invalid IL or missing references) if (args.Args.Length == 1) { args.Context.AddString("<color=purple>Stats Logger (by givenameplz)</color> [1.2.6]"); args.Context.AddString("Stats Logging for your Characters!"); args.Context.AddString(""); args.Context.AddString("statslogger chat <color=#66ff66>[value:bool]</color> <color=#666666>(Announce Milestones in the Chatbox, this is LOCAL to the player only.)</color>"); args.Context.AddString("statslogger console <color=#66ff66>[value:bool]</color> <color=#666666>(Announce Milestones in the Console.)</color>"); args.Context.AddString("statslogger effect <color=#66ff66>[value:bool]</color> <color=#666666>(Announce Milestones with a level up effect.)</color>"); args.Context.AddString("statslogger cooldown <color=#6666ff>[minutes:int]</color> <color=#666666>(Announce Milestones only every <n> minutes so you don't get spammed.)</color>"); args.Context.AddString("statslogger retail <color=#666666>(Outputs the Stats tracked by Valheim: Kills, Deaths, Crafts, Builds and MANY MORE!)</color>"); args.Context.AddString("statslogger reset <color=#666666>(Reset all stats on the current character.)</color>"); args.Context.AddString("statslogger hidedeaths <color=#66ff66>[value:bool]</color> <color=#666666>(Hides any Player Death related stats.)</color>"); args.Context.AddString(""); args.Context.AddString("<color=orange>EXAMPLE:</color> statslogger chat false"); args.Context.AddString("<color=orange>EXAMPLE:</color> statslogger cooldown 5"); args.Context.AddString("<color=orange>EXAMPLE:</color> statslogger reset"); args.Context.AddString(""); args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>")); args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>")); args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>")); args.Context.AddString($"Announce Stats Milestones Cooldown: <color=green>{StatsLogger.Stats.AnnounceIntervalMinutes}</color>"); args.Context.AddString("Hide Player Death Stats: " + (StatsLogger.Stats.HideDeathStats ? "<color=green>ON</color>" : "<color=red>OFF</color>")); } else if (args.HasArgumentAnywhere("debug", 0, true)) { StatsLogger.Stats.DebugMessagesInUnityConsole = !StatsLogger.Stats.DebugMessagesInUnityConsole; args.Context.AddString($"Debug output in UnityConsole. ({StatsLogger.Stats.DebugMessagesInUnityConsole})"); } else if (args.HasArgumentAnywhere("announce", 0, true)) { if (args.Args.Length == 3) { if (bool.TryParse(args.Args[2], out var result)) { StatsLogger.Stats.AnnounceChat = result; StatsLogger.Stats.AnnounceConsole = result; StatsLogger.Stats.AnnounceEffect = result; args.Context.AddString("Announce Stats Milestones: " + (result ? "<color=green>ON</color>" : "<color=red>OFF</color>")); StatsLogger.Stats.SaveSettings(); } else { args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones'"); } } else { args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>")); args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>")); args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>")); } } else if (args.HasArgumentAnywhere("reset", 0, true)) { if ((Object)(object)Player.m_localPlayer != (Object)null) { if (StatsLogger.Stats.ResetFlag) { if (args.HasArgumentAnywhere(Player.m_localPlayer.GetPlayerName().ToLower(), 0, true)) { StatsLogger.Stats.ResetData(Player.m_localPlayer.GetPlayerName()); args.Context.AddString("All StatsLogger Stats have been reset for '" + Player.m_localPlayer.GetPlayerName() + "'."); } StatsLogger.Stats.ResetFlag = false; } else { StatsLogger.Stats.ResetFlag = true; args.Context.AddString("<color=red>WARNING!</color> Do you want to reset ALL stats logged by the StatsLogger Mod for this Character? Type '/statslogger reset " + Player.m_localPlayer.GetPlayerName() + "' to confirm."); } } else { args.Context.AddString("<color=red>Not logged in with any character.</color>"); } } else { if (args.HasArgumentAnywhere("retail", 0, true)) { if (!((Object)(object)Game.instance != (Object)null)) { return; } PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); if (playerProfile == null || playerProfile.m_playerStats == null) { return; } { foreach (KeyValuePair<PlayerStatType, float> stat in playerProfile.m_playerStats.m_stats) { args.Context.AddString($"{stat.Key}: {stat.Value}"); } return; } } if (args.HasArgumentAnywhere("hidedeaths", 0, true)) { if (args.Args.Length == 3) { if (bool.TryParse(args.Args[2], out var result2)) { StatsLogger.Stats.HideDeathStats = result2; args.Context.AddString("Hide Player Death Stats: " + (StatsLogger.Stats.HideDeathStats ? "<color=green>ON</color>" : "<color=red>OFF</color>")); StatsLogger.Stats.SaveSettings(); } else { args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Hide Player Death Stats'"); } } else { args.Context.AddString("Hide Player Death Stats: " + (StatsLogger.Stats.HideDeathStats ? "<color=green>ON</color>" : "<color=red>OFF</color>")); } } else if (args.HasArgumentAnywhere("chat", 0, true)) { if (args.Args.Length == 3) { if (bool.TryParse(args.Args[2], out var result3)) { StatsLogger.Stats.AnnounceChat = result3; args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>")); StatsLogger.Stats.SaveSettings(); } else { args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones in Chatbox (Local)'"); } } else { args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>")); } } else if (args.HasArgumentAnywhere("console", 0, true)) { if (args.Args.Length == 3) { if (bool.TryParse(args.Args[2], out var result4)) { StatsLogger.Stats.AnnounceConsole = result4; args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>")); StatsLogger.Stats.SaveSettings(); } else { args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones in Console'"); } } else { args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>")); } } else if (args.HasArgumentAnywhere("effect", 0, true)) { if (args.Args.Length == 3) { if (bool.TryParse(args.Args[2], out var result5)) { StatsLogger.Stats.AnnounceEffect = result5; args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>")); StatsLogger.Stats.SaveSettings(); } else { args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones with Effect'"); } } else { args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>")); } } else if (args.HasArgumentAnywhere("cooldown", 0, true)) { if (args.Args.Length == 3) { if (int.TryParse(args.Args[2], out var result6) && result6 >= 0) { StatsLogger.Stats.AnnounceIntervalMinutes = result6; args.Context.AddString($"Announce Stats Milestones Cooldown: <color=green>{StatsLogger.Stats.AnnounceIntervalMinutes}</color>"); StatsLogger.Stats.SaveSettings(); } else { args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones Cooldown'"); } } else { args.Context.AddString($"Announce Stats Milestones Cooldown: <color=green>{StatsLogger.Stats.AnnounceIntervalMinutes}</color>"); } } else { args.Context.AddString("<color=red>Error:</color> '" + args.FullLine + "' is not a recognized command for StatsLogger! Type 'statslogger' to see a list of valid commands."); } } } } private static void Postfix(Terminal __instance) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { //IL_0480: Unknown result type (might be due to invalid IL or missing references) if (args.Args.Length == 1) { args.Context.AddString("<color=purple>Stats Logger (by givenameplz)</color> [1.2.6]"); args.Context.AddString("Stats Logging for your Characters!"); args.Context.AddString(""); args.Context.AddString("statslogger chat <color=#66ff66>[value:bool]</color> <color=#666666>(Announce Milestones in the Chatbox, this is LOCAL to the player only.)</color>"); args.Context.AddString("statslogger console <color=#66ff66>[value:bool]</color> <color=#666666>(Announce Milestones in the Console.)</color>"); args.Context.AddString("statslogger effect <color=#66ff66>[value:bool]</color> <color=#666666>(Announce Milestones with a level up effect.)</color>"); args.Context.AddString("statslogger cooldown <color=#6666ff>[minutes:int]</color> <color=#666666>(Announce Milestones only every <n> minutes so you don't get spammed.)</color>"); args.Context.AddString("statslogger retail <color=#666666>(Outputs the Stats tracked by Valheim: Kills, Deaths, Crafts, Builds and MANY MORE!)</color>"); args.Context.AddString("statslogger reset <color=#666666>(Reset all stats on the current character.)</color>"); args.Context.AddString("statslogger hidedeaths <color=#66ff66>[value:bool]</color> <color=#666666>(Hides any Player Death related stats.)</color>"); args.Context.AddString(""); args.Context.AddString("<color=orange>EXAMPLE:</color> statslogger chat false"); args.Context.AddString("<color=orange>EXAMPLE:</color> statslogger cooldown 5"); args.Context.AddString("<color=orange>EXAMPLE:</color> statslogger reset"); args.Context.AddString(""); args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>")); args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>")); args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>")); args.Context.AddString($"Announce Stats Milestones Cooldown: <color=green>{StatsLogger.Stats.AnnounceIntervalMinutes}</color>"); args.Context.AddString("Hide Player Death Stats: " + (StatsLogger.Stats.HideDeathStats ? "<color=green>ON</color>" : "<color=red>OFF</color>")); } else if (args.HasArgumentAnywhere("debug", 0, true)) { StatsLogger.Stats.DebugMessagesInUnityConsole = !StatsLogger.Stats.DebugMessagesInUnityConsole; args.Context.AddString($"Debug output in UnityConsole. ({StatsLogger.Stats.DebugMessagesInUnityConsole})"); } else if (args.HasArgumentAnywhere("announce", 0, true)) { if (args.Args.Length == 3) { if (bool.TryParse(args.Args[2], out var result)) { StatsLogger.Stats.AnnounceChat = result; StatsLogger.Stats.AnnounceConsole = result; StatsLogger.Stats.AnnounceEffect = result; args.Context.AddString("Announce Stats Milestones: " + (result ? "<color=green>ON</color>" : "<color=red>OFF</color>")); StatsLogger.Stats.SaveSettings(); } else { args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones'"); } } else { args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>")); args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>")); args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>")); } } else if (args.HasArgumentAnywhere("reset", 0, true)) { if ((Object)(object)Player.m_localPlayer != (Object)null) { if (StatsLogger.Stats.ResetFlag) { if (args.HasArgumentAnywhere(Player.m_localPlayer.GetPlayerName().ToLower(), 0, true)) { StatsLogger.Stats.ResetData(Player.m_localPlayer.GetPlayerName()); args.Context.AddString("All StatsLogger Stats have been reset for '" + Player.m_localPlayer.GetPlayerName() + "'."); } StatsLogger.Stats.ResetFlag = false; } else { StatsLogger.Stats.ResetFlag = true; args.Context.AddString("<color=red>WARNING!</color> Do you want to reset ALL stats logged by the StatsLogger Mod for this Character? Type '/statslogger reset " + Player.m_localPlayer.GetPlayerName() + "' to confirm."); } } else { args.Context.AddString("<color=red>Not logged in with any character.</color>"); } } else if (args.HasArgumentAnywhere("retail", 0, true)) { if ((Object)(object)Game.instance != (Object)null) { PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); if (playerProfile != null && playerProfile.m_playerStats != null) { foreach (KeyValuePair<PlayerStatType, float> stat in playerProfile.m_playerStats.m_stats) { args.Context.AddString($"{stat.Key}: {stat.Value}"); } } } } else if (args.HasArgumentAnywhere("hidedeaths", 0, true)) { if (args.Args.Length == 3) { if (bool.TryParse(args.Args[2], out var result2)) { StatsLogger.Stats.HideDeathStats = result2; args.Context.AddString("Hide Player Death Stats: " + (StatsLogger.Stats.HideDeathStats ? "<color=green>ON</color>" : "<color=red>OFF</color>")); StatsLogger.Stats.SaveSettings(); } else { args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Hide Player Death Stats'"); } } else { args.Context.AddString("Hide Player Death Stats: " + (StatsLogger.Stats.HideDeathStats ? "<color=green>ON</color>" : "<color=red>OFF</color>")); } } else if (args.HasArgumentAnywhere("chat", 0, true)) { if (args.Args.Length == 3) { if (bool.TryParse(args.Args[2], out var result3)) { StatsLogger.Stats.AnnounceChat = result3; args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>")); StatsLogger.Stats.SaveSettings(); } else { args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones in Chatbox (Local)'"); } } else { args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>")); } } else if (args.HasArgumentAnywhere("console", 0, true)) { if (args.Args.Length == 3) { if (bool.TryParse(args.Args[2], out var result4)) { StatsLogger.Stats.AnnounceConsole = result4; args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>")); StatsLogger.Stats.SaveSettings(); } else { args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones in Console'"); } } else { args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>")); } } else if (args.HasArgumentAnywhere("effect", 0, true)) { if (args.Args.Length == 3) { if (bool.TryParse(args.Args[2], out var result5)) { StatsLogger.Stats.AnnounceEffect = result5; args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>")); StatsLogger.Stats.SaveSettings(); } else { args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones with Effect'"); } } else { args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>")); } } else if (args.HasArgumentAnywhere("cooldown", 0, true)) { if (args.Args.Length == 3) { if (int.TryParse(args.Args[2], out var result6) && result6 >= 0) { StatsLogger.Stats.AnnounceIntervalMinutes = result6; args.Context.AddString($"Announce Stats Milestones Cooldown: <color=green>{StatsLogger.Stats.AnnounceIntervalMinutes}</color>"); StatsLogger.Stats.SaveSettings(); } else { args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones Cooldown'"); } } else { args.Context.AddString($"Announce Stats Milestones Cooldown: <color=green>{StatsLogger.Stats.AnnounceIntervalMinutes}</color>"); } } else { args.Context.AddString("<color=red>Error:</color> '" + args.FullLine + "' is not a recognized command for StatsLogger! Type 'statslogger' to see a list of valid commands."); } }; <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("statslogger", "<color=red>[MOD]</color> Stats Logging for your Characters!", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } } [HarmonyPatch] public static class DamageAndHits { public class Target { public Character Character; public HitData Hit; public ItemData Weapon; public DateTime Time = DateTime.Now; public DeathTypes DeathType; } public enum DeathTypes { Unknown, FallDamage, Drowning, EdgeOfTheWorld } public static DateTime LastCleanupAtkPlayer = DateTime.Now; public static readonly int IntervalSecondsAtkPlayer = 60; public static readonly int TTLSecondsAtkPlayer = 30; public static DateTime LastCleanupAtkByPlayer = DateTime.Now; public static readonly int IntervalSecondsAtkByPlayer = 60; public static readonly int TTLSecondsAtkByPlayer = 30; public static Dictionary<int, Target> RecentlyAttackedByPlayer = new Dictionary<int, Target>(); public static Target LastAttackedPlayer = null; public static void SpawnOnDamaged<TypeOfObject>(Character __instance, HitData hit) { //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) if (StatsLogger.IsLocalPlayer(__instance)) { Character attacker = hit.GetAttacker(); if ((Object)(object)attacker != (Object)null) { ItemData weapon = null; if (typeof(TypeOfObject) == typeof(Humanoid)) { weapon = ((Humanoid)attacker).GetCurrentWeapon(); } LastAttackedPlayer = new Target { Character = attacker, Hit = hit, Weapon = weapon }; StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.TakingHits, attacker, weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.TakingHits, 1f); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.DamageTaken, attacker, weapon, hit.GetTotalDamage()); StatsLogger.Stats.UpdateTotal(Settings.LogActions.DamageTaken, hit.GetTotalDamage()); StatsLogger.Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.LastDamageTaken.ToString(), 0f, useTimeBuffer: false, set: true); return; } Vector3 val = (Vector3)typeof(Character).GetField("m_lastGroundPoint", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(__instance); Vector3 val2 = (Vector3)typeof(Character).GetField("m_lastGroundNormal", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(__instance); if (hit.m_point == val && hit.m_dir == val2) { LastAttackedPlayer = new Target { DeathType = DeathTypes.FallDamage, Hit = hit }; StatsLogger.Stats.UpdateSingle(Settings.LogActions.DamageTaken, Settings.StringDetailsMisc.Gravity.ToString(), hit.GetTotalDamage()); StatsLogger.Stats.UpdateTotal(Settings.LogActions.DamageTaken, hit.GetTotalDamage()); } else if (hit.m_point == __instance.GetCenterPoint() && hit.m_dir == Vector3.down) { LastAttackedPlayer = new Target { DeathType = DeathTypes.Drowning, Hit = hit }; StatsLogger.Stats.UpdateSingle(Settings.LogActions.DamageTaken, Settings.StringDetailsMisc.Drowning.ToString(), hit.GetTotalDamage()); StatsLogger.Stats.UpdateTotal(Settings.LogActions.DamageTaken, hit.GetTotalDamage()); } else if (hit.m_point == Vector3.zero && hit.m_dir == Vector3.zero) { LastAttackedPlayer = new Target { DeathType = DeathTypes.EdgeOfTheWorld, Hit = hit }; } else { LastAttackedPlayer = new Target { DeathType = DeathTypes.Unknown, Hit = hit }; StatsLogger.Stats.UpdateSingle(Settings.LogActions.DamageTaken, Settings.StringDetailsMisc.Unknown.ToString(), hit.GetTotalDamage()); StatsLogger.Stats.UpdateTotal(Settings.LogActions.DamageTaken, hit.GetTotalDamage()); } StatsLogger.Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.LastDamageTaken.ToString(), 0f, useTimeBuffer: false, set: true); } else { if (!hit.HaveAttacker()) { return; } Character attacker2 = hit.GetAttacker(); if ((Object)(object)attacker2 != (Object)null && StatsLogger.IsLocalPlayer(attacker2)) { ItemData currentWeapon = ((Humanoid)Player.m_localPlayer).GetCurrentWeapon(); if (!RecentlyAttackedByPlayer.ContainsKey(((Object)__instance).GetInstanceID())) { RecentlyAttackedByPlayer.Add(((Object)__instance).GetInstanceID(), new Target { Character = __instance, Hit = hit, Weapon = currentWeapon }); } else { RecentlyAttackedByPlayer[((Object)__instance).GetInstanceID()].Time = DateTime.Now; RecentlyAttackedByPlayer[((Object)__instance).GetInstanceID()].Hit = hit; RecentlyAttackedByPlayer[((Object)__instance).GetInstanceID()].Weapon = currentWeapon; } StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Attacks, __instance, currentWeapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.Attacks, 1f); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.DamageDone, __instance, currentWeapon, hit.GetTotalDamage()); StatsLogger.Stats.UpdateTotal(Settings.LogActions.DamageDone, hit.GetTotalDamage()); } } } [HarmonyPrefix] [HarmonyPatch(typeof(Humanoid), "OnDamaged")] private static void HumanoidOnDamaged(ref Humanoid __instance, HitData hit) { SpawnOnDamaged<Humanoid>((Character)(object)__instance, hit); } [HarmonyPrefix] [HarmonyPatch(typeof(Character), "OnDamaged")] private static void CharacterOnDamaged(ref Character __instance, HitData hit) { SpawnOnDamaged<Character>(__instance, hit); } [HarmonyPrefix] [HarmonyPatch(typeof(Character), "OnDeath")] private static void CharacterOnDeath(Character __instance) { if (RecentlyAttackedByPlayer.ContainsKey(((Object)__instance).GetInstanceID())) { ItemData weapon = RecentlyAttackedByPlayer[((Object)__instance).GetInstanceID()].Weapon; StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Kills, __instance, weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.Kills, 1f); RecentlyAttackedByPlayer.Remove(((Object)__instance).GetInstanceID()); } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "OnDeath")] private static void PlayerOnDeath(Player __instance) { if (!StatsLogger.IsLocalPlayer((Character)(object)__instance)) { return; } if (LastAttackedPlayer != null) { if ((Object)(object)LastAttackedPlayer.Character != (Object)null) { ItemData weapon = LastAttackedPlayer.Weapon; StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Deaths, LastAttackedPlayer.Character, weapon, 1f); } else if (LastAttackedPlayer.DeathType.HasFlag(DeathTypes.FallDamage)) { StatsLogger.Stats.UpdateSingle(Settings.LogActions.Deaths, Settings.StringDetailsMisc.Gravity.ToString(), 1f); } else if (LastAttackedPlayer.DeathType.HasFlag(DeathTypes.Drowning)) { StatsLogger.Stats.UpdateSingle(Settings.LogActions.Deaths, Settings.StringDetailsMisc.Drowning.ToString(), 1f); } else if (LastAttackedPlayer.DeathType.HasFlag(DeathTypes.EdgeOfTheWorld)) { StatsLogger.Stats.UpdateSingle(Settings.LogActions.Deaths, Settings.StringDetailsMisc.EdgeOfTheWorld.ToString(), 1f); } else { StatsLogger.Stats.UpdateSingle(Settings.LogActions.Deaths, Settings.StringDetailsMisc.Unknown.ToString(), 1f); } } else { DebugLogger.LogWarning("No 'LastAttackedPlayer', this should not be possible."); StatsLogger.Stats.UpdateSingle(Settings.LogActions.Deaths, Settings.StringDetailsMisc.Unknown.ToString(), 1f); } StatsLogger.Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.LastDeath.ToString(), 0f, useTimeBuffer: false, set: true); StatsLogger.Stats.UpdateTotal(Settings.LogActions.Deaths, 1f); LastAttackedPlayer = null; StatsLogger.Stats.SaveFile(Player.m_localPlayer.GetPlayerName()); } [HarmonyPostfix] [HarmonyPatch(typeof(Raven), "Damage")] private static void RavenDamage(ref Raven __instance, HitData hit) { if (hit.HaveAttacker()) { Character attacker = hit.GetAttacker(); if ((Object)(object)attacker != (Object)null && StatsLogger.IsLocalPlayer(attacker)) { StatsLogger.Stats.UpdateSingle(Settings.LogActions.Dismissed, __instance.m_name, 1f); } } } [HarmonyPrefix] [HarmonyPatch(typeof(Character), "Damage")] private static void Damage(ref Character __instance, HitData hit, ZNetView ___m_nview) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (hit.HaveAttacker()) { Character attacker = hit.GetAttacker(); if (StatsLogger.IsLocalPlayer(attacker) && !StatsLogger.IsLocalOwner(___m_nview)) { LocalHitCollector.Hits.Add(new LocalHitCollector.LocalHit { Hit = hit, Target = __instance, Weapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(), Type = ((object)__instance).GetType(), UID = ___m_nview.GetZDO().m_uid, TTL = TTLSecondsAtkByPlayer }); } } } } public static class DebugLogger { public enum LogTypes { Debug, Default, Warning, Error } public static bool Enabled = true; public static string Prefix = ""; public static LogTypes MinLogLevel = LogTypes.Warning; public static void LogDebug(string str) { Log(str, LogTypes.Debug); } public static void Log(string str) { Log(str, LogTypes.Default); } public static void LogWarning(string str) { Log(str, LogTypes.Warning); } public static void LogError(string str) { Log(str, LogTypes.Error); } private static void Log(string str, LogTypes type = LogTypes.Default) { if (Enabled && type >= MinLogLevel) { switch (type) { case LogTypes.Debug: Debug.Log((object)(Prefix + " [DEBUG] " + str)); break; case LogTypes.Default: Debug.Log((object)(Prefix + " " + str)); break; case LogTypes.Warning: Debug.LogWarning((object)(Prefix + " " + str)); break; case LogTypes.Error: Debug.LogError((object)(Prefix + " " + str)); break; } } } } [HarmonyPatch] public static class DestructibleObjects { [HarmonyPatch(typeof(Destructible), "RPC_Damage")] public static class Destructible_RPC_Damage_DamageAndDeath { private static MethodInfo miCreate = AccessTools.Method(typeof(EffectList), "Create", (Type[])null, (Type[])null); private static MethodInfo miDestroy = AccessTools.Method(typeof(Destructible), "Destroy", (Type[])null, (Type[])null); private static MethodInfo miDamageTrigger = AccessTools.Method(typeof(Destructible_RPC_Damage_DamageAndDeath), "DamageTrigger", (Type[])null, (Type[])null); private static MethodInfo miDeathTrigger = AccessTools.Method(typeof(Destructible_RPC_Damage_DamageAndDeath), "DeathTrigger", (Type[])null, (Type[])null); private static readonly int Inserts = 2; [HarmonyTranspiler] public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected O, but got Unknown //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Expected O, but got Unknown int num = 0; List<CodeInstruction> list = new List<CodeInstruction>(instructions); for (int i = 0; i < list.Count; i++) { if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == miCreate) { list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)miDamageTrigger)); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_2, (object)null)); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null)); DebugLogger.Log($"{typeof(Destructible_RPC_Damage_DamageAndDeath)} [Destructible RPC_Damage {miDamageTrigger.Name}] applied."); num++; } if (list[i].opcode == OpCodes.Call && (MethodInfo)list[i].operand == miDestroy) { list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)miDeathTrigger)); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_2, (object)null)); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null)); DebugLogger.Log($"{typeof(Destructible_RPC_Damage_DamageAndDeath)} [Destructible RPC_Damage {miDeathTrigger.Name}] applied."); num++; } } if (num == 0) { DebugLogger.LogError($"{typeof(Destructible_RPC_Damage_DamageAndDeath)} [Destructible RPC_Damage] failed."); } else if (num != Inserts) { DebugLogger.LogWarning($"{typeof(Destructible_RPC_Damage_DamageAndDeath)} [Destructible RPC_Damage] applied {num}/{Inserts}."); } return list.AsEnumerable(); } public static void DamageTrigger(Destructible target, HitData hit) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) float totalDamage = hit.GetTotalDamage(); if (hit.HaveAttacker()) { Character attacker = hit.GetAttacker(); if (StatsLogger.IsLocalPlayer(attacker)) { ItemData currentWeapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectAttacks, (object)target, currentWeapon, 1f, useTimeBuffer: false); StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectAttacks, 1f); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectDamageDone, (object)target, currentWeapon, totalDamage, useTimeBuffer: false); StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectDamageDone, totalDamage); } } } public static void DeathTrigger(Destructible target, HitData hit) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (hit.HaveAttacker()) { Character attacker = hit.GetAttacker(); if (StatsLogger.IsLocalPlayer(attacker)) { ItemData currentWeapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectsDestroyed, (object)target, currentWeapon, 1f, useTimeBuffer: false); StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectsDestroyed, 1f); } } } } [HarmonyPrefix] [HarmonyPatch(typeof(Destructible), "Damage")] private static void Damage(ref Destructible __instance, HitData hit, ZNetView ___m_nview) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance == (Object)null) && hit.HaveAttacker()) { Character attacker = hit.GetAttacker(); if (StatsLogger.IsLocalPlayer(attacker) && !StatsLogger.IsLocalOwner(___m_nview)) { LocalHitCollector.Hits.Add(new LocalHitCollector.LocalHit { Hit = hit, Target = __instance, Weapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(), Type = ((object)__instance).GetType(), UID = ___m_nview.GetZDO().m_uid }); } } } } [HarmonyPatch] public static class GuardianPower { [HarmonyPatch(typeof(Player))] [HarmonyPatch("ActivateGuardianPower")] public static class Player_ActivateGuardianPower_ReturnFix { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = new List<CodeInstruction>(instructions); if (list.Count > 2) { if (list[list.Count - 1].opcode == OpCodes.Ret) { if (list[list.Count - 2].opcode == OpCodes.Ldc_I4_0) { list[list.Count - 2].opcode = OpCodes.Ldc_I4_1; DebugLogger.Log($"{typeof(Player_ActivateGuardianPower_ReturnFix)} applied."); } else { DebugLogger.LogWarning($"{typeof(Player_ActivateGuardianPower_ReturnFix)} failed: Return value is not as expected. ({list[list.Count - 2].opcode})"); } } else { DebugLogger.LogError($"{typeof(Player_ActivateGuardianPower_ReturnFix)} failed: No last return found. ({list[list.Count - 1].opcode})"); } } else { DebugLogger.LogError($"{typeof(Player_ActivateGuardianPower_ReturnFix)} failed: Not enough Code Instructions. ({list.Count})"); } return list.AsEnumerable(); } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "ActivateGuardianPower")] private static void PlayerUsePower(ref Player __instance, StatusEffect ___m_guardianSE, bool __result) { if (StatsLogger.IsLocalPlayer((Character)(object)__instance) && __result) { StatsLogger.Stats.UpdateSingle(Settings.LogActions.UsedPower, ___m_guardianSE.m_name, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.UsedPower, 1f); } } } [HarmonyPatch] public static class Interactions { [HarmonyPatch(typeof(Player), "Interact")] public static class Player_Interact_GameObjects { private static MethodInfo miInteract = AccessTools.Method(typeof(Interactable), "Interact", (Type[])null, (Type[])null); private static MethodInfo miInteractTrigger = AccessTools.Method(typeof(Interactions), "InteractTrigger", (Type[])null, (Type[])null); private static readonly int Inserts = 1; [HarmonyTranspiler] public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown int num = 0; List<CodeInstruction> list = new List<CodeInstruction>(instructions); for (int i = 0; i < list.Count; i++) { if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == miInteract) { list.Insert(i - 3, new CodeInstruction(OpCodes.Ldarg_1, (object)null)); i++; list.Insert(i - 3, new CodeInstruction(OpCodes.Call, (object)miInteractTrigger)); i++; DebugLogger.Log($"{typeof(Player_Interact_GameObjects)} [Player Interact {miInteractTrigger.Name}] applied."); num++; } } if (num == 0) { DebugLogger.LogError($"{typeof(Player_Interact_GameObjects)} [Player Interact] failed."); } else if (num != Inserts) { DebugLogger.LogWarning($"{typeof(Player_Interact_GameObjects)} [Player Interact] applied {num}/{Inserts}."); } return list.AsEnumerable(); } } public static void InteractTrigger(GameObject go) { Hoverable componentInParent = go.GetComponentInParent<Hoverable>(); if (componentInParent != null) { StatsLogger.Stats.UpdateSingle(Settings.LogActions.Interacted, componentInParent.GetHoverName(), 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.Interacted, 1f); } } } [HarmonyPatch] public static class Items { [HarmonyPostfix] [HarmonyPatch(typeof(Player), "ConsumeItem")] private static void PlayerConsume(ref Player __instance, Inventory inventory, ItemData item, bool __result) { if (StatsLogger.IsLocalPlayer((Character)(object)__instance) && __result) { StatsLogger.Stats.UpdateSingle(Settings.LogActions.Consumed, item.m_shared.m_name, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.Consumed, 1f); } } [HarmonyPostfix] [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(int), typeof(long), typeof(string), typeof(bool) })] private static void PlayerCraft(ref Inventory __instance, string name, int stack, int quality, int variant, long crafterID, string crafterName, bool pickedUp, ItemData __result) { if (StatsLogger.IsLocalPlayerInventory(__instance) && __result != null && ((crafterName != null) & (crafterName.Length > 0)) && crafterName == Player.m_localPlayer.GetPlayerName()) { StatsLogger.Stats.UpdateSingle(Settings.LogActions.Crafted, __result.m_shared.m_name, stack); StatsLogger.Stats.UpdateTotal(Settings.LogActions.Crafted, stack); } } [HarmonyPostfix] [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] private static void PlayerPickup(ref Inventory __instance, ItemData item, ref bool __result) { if (StatsLogger.IsLocalPlayerInventory(__instance) && __result && item != null) { StatsLogger.Stats.UpdateSingle(Settings.LogActions.PickedUp, item.m_shared.m_name, item.m_stack); StatsLogger.Stats.UpdateTotal(Settings.LogActions.PickedUp, item.m_stack); } } [HarmonyPostfix] [HarmonyPatch(typeof(Fish), "Pickup", new Type[] { typeof(Humanoid) })] private static void FishPickup(ref Fish __instance, Humanoid character, ref bool __result) { if (StatsLogger.IsLocalPlayer((Character)(object)character) && __result && (Object)(object)__instance != (Object)null) { DebugLogger.Log("Picked up Fish: " + __instance.m_name); } } [HarmonyPostfix] [HarmonyPatch(typeof(Humanoid), "DropItem")] private static void PlayerDropItem(ref Humanoid __instance, Inventory inventory, ItemData item, int amount, ref bool __result) { if (StatsLogger.IsLocalPlayer((Character)(object)__instance) && __result && item != null) { StatsLogger.Stats.UpdateSingle(Settings.LogActions.Dropped, item.m_shared.m_name, amount); StatsLogger.Stats.UpdateTotal(Settings.LogActions.Dropped, amount); } } } [HarmonyPatch] public static class LoadAndSave { [HarmonyPostfix] [HarmonyPatch(typeof(PlayerProfile), "LoadPlayerData")] private static void PlayerLoaded(ref PlayerProfile __instance) { DebugLogger.Log("PlayerProfile: LoadPlayerData"); if ((Object)(object)Player.m_localPlayer != (Object)null) { StatsLogger.Stats.LoadFile(Player.m_localPlayer.GetPlayerName()); DebugLogger.Log("PLAYERSTATS ARE LOADED"); } } [HarmonyPrefix] [HarmonyPatch(typeof(Game), "SavePlayerProfile")] private static void PlayerSaved(ref Game __instance) { DebugLogger.Log("Game: SavePlayerProfile"); if ((Object)(object)Player.m_localPlayer != (Object)null) { StatsLogger.Stats.SaveFile(Player.m_localPlayer.GetPlayerName()); DebugLogger.Log("PLAYERSTATS ARE SAVED"); } } } [HarmonyPatch] public class LocalHitCollector { public class LocalHit { public ZDOID UID; public object Target; public ItemData Weapon; public HitData Hit; public Type Type; public float TTL = 5f; public DateTime Time = DateTime.Now; public bool HitRegistered; } public static List<LocalHit> Hits = new List<LocalHit>(); public static DateTime LastCleanup = DateTime.Now; public static readonly int IntervalSeconds = 10; [HarmonyPrefix] [HarmonyPatch(typeof(DamageText), "AddInworldText")] private static void Damaged(ref DamageText __instance, TextType type, Vector3 pos, float distance, string text, bool mySelf) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Expected O, but got Unknown //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Expected O, but got Unknown if (Hits.Count <= 0) { return; } LocalHit localHit = null; foreach (LocalHit hit in Hits) { Vector3 point = hit.Hit.m_point; if (((Vector3)(ref point)).Equals(pos) && !hit.HitRegistered) { localHit = hit; DebugLogger.Log($"[LocalHitCollector] HIT FOUND: {localHit.Type}"); break; } } if (localHit == null) { return; } float num = 0f; try { num = float.Parse(text, CultureInfo.InvariantCulture.NumberFormat); DebugLogger.LogDebug($"[LocalHitCollector] CONVERSION: {text} to {num}"); } catch (Exception ex) { DebugLogger.LogError("[LocalHitCollector] CONVERSION ERROR: " + text + " (" + ex.Message + ")"); return; } if (num <= 0f) { Hits.Remove(localHit); return; } int index = Hits.IndexOf(localHit); Hits[index].HitRegistered = true; Hits[index].Time = DateTime.Now; Type type2 = localHit.Type; DebugLogger.Log($"[LocalHitCollector] HIT REGISTERED: {type2} {num}"); if (type2 == typeof(Character) || type2 == typeof(Humanoid) || type2 == typeof(Player)) { StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Attacks, (Character)localHit.Target, localHit.Weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.Attacks, 1f); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.DamageDone, (Character)localHit.Target, localHit.Weapon, num); StatsLogger.Stats.UpdateTotal(Settings.LogActions.DamageDone, num); } else if (type2 == typeof(TreeBase)) { StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.TreeAttacks, localHit.Target, localHit.Weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.TreeAttacks, 1f); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.TreeDamageDone, localHit.Target, localHit.Weapon, num); StatsLogger.Stats.UpdateTotal(Settings.LogActions.TreeDamageDone, num); } else if (type2 == typeof(TreeLog)) { StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectAttacks, localHit.Target, localHit.Weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectAttacks, 1f); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectDamageDone, localHit.Target, localHit.Weapon, num); StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectDamageDone, num); } else if (type2 == typeof(WearNTear)) { StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.StructureAttacks, localHit.Target, localHit.Weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.StructureAttacks, 1f); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.StructureDamageDone, localHit.Target, localHit.Weapon, num); StatsLogger.Stats.UpdateTotal(Settings.LogActions.StructureDamageDone, num); } else if (type2 == typeof(Destructible)) { StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectAttacks, localHit.Target, localHit.Weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectAttacks, 1f); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectDamageDone, localHit.Target, localHit.Weapon, num); StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectDamageDone, num); } else if (type2 == typeof(MineRock5)) { StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.RockAttacks, localHit.Target, localHit.Weapon, 1f); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.RockDamageDone, localHit.Target, localHit.Weapon, num); StatsLogger.Stats.UpdateTotal(Settings.LogActions.RockAttacks, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.RockDamageDone, num); } } [HarmonyPrefix] [HarmonyPatch(typeof(ZDOMan), "HandleDestroyedZDO")] private static void Destroyed(ZDOID uid) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown if (Hits.Count <= 0) { return; } LocalHit localHit = Hits.Find((LocalHit item) => ((ZDOID)(ref item.UID)).ID == ((ZDOID)(ref uid)).ID); if (localHit != null && localHit.HitRegistered) { DebugLogger.Log($"[LocalHitCollector] ZDO DESTROYED: {localHit.Type}"); Hits.Remove(localHit); Type type = localHit.Type; if (type == typeof(Character) || type == typeof(Humanoid) || type == typeof(Player)) { StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Kills, (Character)localHit.Target, localHit.Weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.Kills, 1f); } else if (type == typeof(TreeBase)) { StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.TreesCut, localHit.Target, localHit.Weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.TreesCut, 1f); } else if (type == typeof(TreeLog)) { StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectsDestroyed, localHit.Target, localHit.Weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectsDestroyed, 1f); } else if (type == typeof(WearNTear)) { StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.StructuresDestroyed, localHit.Target, localHit.Weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.StructuresDestroyed, 1f); } else if (type == typeof(Destructible)) { StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectsDestroyed, localHit.Target, localHit.Weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectsDestroyed, 1f); } else if (type == typeof(MineRock5)) { StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.RocksMined, localHit.Target, localHit.Weapon, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.RocksMined, 1f); } } } } [HarmonyPatch] public static class Mining { [HarmonyPatch(typeof(MineRock5), "DamageArea")] public static class MineRock5_DamageArea_DamageAndDestroy { private static MethodInfo miSaveHealth = AccessTools.Method(typeof(MineRock5), "SaveHealth", (Type[])null, (Type[])null); private static MethodInfo miDamageTrigger = AccessTools.Method(typeof(MineRock5_DamageArea_DamageAndDestroy), "DamageTrigger", (Type[])null, (Type[])null); private static MethodInfo miDestroyTrigger = AccessTools.Method(typeof(MineRock5_DamageArea_DamageAndDestroy), "DestroyTrigger", (Type[])null, (Type[])null); private static int Inserts = 2; [HarmonyTranspiler] public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Expected O, but got Unknown int num = 0; List<CodeInstruction> list = new List<CodeInstruction>(instructions); for (int i = 0; i < list.Count; i++) { if (list[i].opcode == OpCodes.Call && (MethodInfo)list[i].operand == miSaveHealth) { list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)miDamageTrigger)); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_2, (object)null)); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null)); DebugLogger.Log($"{typeof(MineRock5_DamageArea_DamageAndDestroy)} [MineRock5 DamageArea {miDamageTrigger.Name}] applied."); num++; } if (list[i].opcode == OpCodes.Ret && list[i - 1].opcode == OpCodes.Ldc_I4_1) { list.Insert(i - 2, new CodeInstruction(OpCodes.Call, (object)miDestroyTrigger)); list.Insert(i - 2, new CodeInstruction(OpCodes.Ldarg_2, (object)null)); list.Insert(i - 2, new CodeInstruction(OpCodes.Ldarg_0, (object)null)); i += 3; DebugLogger.Log($"{typeof(MineRock5_DamageArea_DamageAndDestroy)} [MineRock5 DamageArea {miDestroyTrigger.Name}] applied."); num++; } } if (num == 0) { DebugLogger.LogError($"{typeof(MineRock5_DamageArea_DamageAndDestroy)} [MineRock5 DamageArea] failed."); } else if (num != Inserts) { DebugLogger.LogWarning($"{typeof(MineRock5_DamageArea_DamageAndDestroy)} [MineRock5 DamageArea] applied {num}/{Inserts}."); } return list.AsEnumerable(); } public static void DamageTrigger(MineRock5 target, HitData hit) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (hit.HaveAttacker()) { Character attacker = hit.GetAttacker(); if (StatsLogger.IsLocalPlayer(attacker)) { ItemData currentWeapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(); float totalDamage = hit.GetTotalDamage(); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.RockAttacks, (object)target, currentWeapon, 1f, useTimeBuffer: false); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.RockDamageDone, (object)target, currentWeapon, totalDamage, useTimeBuffer: false); StatsLogger.Stats.UpdateTotal(Settings.LogActions.RockAttacks, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.RockDamageDone, totalDamage); } } } public static void DestroyTrigger(MineRock5 target, HitData hit) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (hit.HaveAttacker()) { Character attacker = hit.GetAttacker(); if (StatsLogger.IsLocalPlayer(attacker)) { ItemData currentWeapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.RocksMined, (object)target, currentWeapon, 1f, useTimeBuffer: false); StatsLogger.Stats.UpdateTotal(Settings.LogActions.RocksMined, 1f); } } } } [HarmonyPostfix] [HarmonyPatch(typeof(MineRock), "RPC_Hit")] private static void TestMine(ref MineRock __instance, HitData hit) { DebugLogger.Log($"MineRock {__instance.m_name} {hit.GetAttacker().m_name} {hit.GetTotalDamage()}"); } [HarmonyPrefix] [HarmonyPatch(typeof(MineRock5), "Damage")] private static void Damage(ref MineRock5 __instance, HitData hit, ZNetView ___m_nview) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (hit.HaveAttacker()) { Character attacker = hit.GetAttacker(); if (StatsLogger.IsLocalPlayer(attacker) && !StatsLogger.IsLocalOwner(___m_nview)) { LocalHitCollector.Hits.Add(new LocalHitCollector.LocalHit { Hit = hit, Target = __instance, Weapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(), Type = ((object)__instance).GetType(), UID = ___m_nview.GetZDO().m_uid }); } } } } [HarmonyPatch] public static class Misc { [HarmonyPostfix] [HarmonyPatch(typeof(Player), "PlacePiece")] private static void PlayerPlacePiece(ref Player __instance, Piece piece) { if (StatsLogger.IsLocalPlayer((Character)(object)__instance)) { StatsLogger.Stats.UpdateSingle(Settings.LogActions.Built, piece.m_name, 1f); StatsLogger.Stats.UpdateTotal(Settings.LogActions.Built, 1f); } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "OnJump")] private static void PlayerJump(ref Player __instance) { if (StatsLogger.IsLocalPlayer((Character)(object)__instance)) { StatsLogger.Stats.UpdateTotal(Settings.LogActions.Jumps, 1f); } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "TeleportTo")] private static void PlayerTeleport(ref Player __instance, bool __result) { if (StatsLogger.IsLocalPlayer((Character)(object)__instance) && __result) { StatsLogger.Stats.UpdateTotal(Settings.LogActions.Teleported, 1f); } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "SetSleeping")] private static void PlayerSleep(ref Player __instance, bool sleep) { if (StatsLogger.IsLocalPlayer((Character)(object)__instance) && sleep) { StatsLogger.Stats.UpdateTotal(Settings.LogActions.Sleeps, 1f); } } } [HarmonyPatch] public static class Projectiles { public class Target { public GameObject Object; public Collider Collider; public ItemData Item; public DateTime Time = DateTime.Now; } public static Dictionary<int, Target> FiredProjectiles = new Dictionary<int, Target>(); [HarmonyPrefix] [HarmonyPatch(typeof(Attack), "FireProjectileBurst")] private static void AttackFireProjectileBurst(ref Attack __instance, ItemData ___m_ammoItem, ItemData ___m_weapon, Humanoid ___m_character) { if (StatsLogger.IsLocalPlayer((Character)(object)___m_character)) { DebugLogger.LogDebug($"{___m_weapon.m_shared.m_name} using {___m_ammoItem?.m_shared?.m_name} x{__instance?.m_projectiles}"); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ProjectilesFired, (object)___m_ammoItem, ___m_weapon, 1f, useTimeBuffer: false); StatsLogger.Stats.UpdateTotal(Settings.LogActions.ProjectilesFired, 1f); } } } [HarmonyPatch] public static class ShipTriggers { public static bool IsShipping; [HarmonyPostfix] [HarmonyPatch(typeof(Ship), "OnTriggerEnter")] private static void Enter(ref Ship __instance) { if ((Object)(object)Player.m_localPlayer != (Object)null) { IsShipping = __instance.IsPlayerInBoat(Player.m_localPlayer); } else { IsShipping = false; } } [HarmonyPostfix] [HarmonyPatch(typeof(Ship), "OnTriggerExit")] private static void Exit(ref Ship __instance) { if ((Object)(object)Player.m_localPlayer != (Object)null) { IsShipping = __instance.IsPlayerInBoat(Player.m_localPlayer); } else { IsShipping = false; } } } [BepInPlugin("de.givenameplz.statslogger", "Stats Logger (by givenameplz)", "1.2.6")] public class StatsLogger : BaseUnityPlugin { public const string GUID = "de.givenameplz.statslogger"; public const string DisplayName = "Stats Logger (by givenameplz)"; public const string Version = "1.2.6"; public const string Description = "Stats Logging for your Characters!"; public const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; public static Settings Stats = new Settings(); public static bool Debugging = false; private static Random RNG = new Random(); public static string RandomString(int length, string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") { return new string((from s in Enumerable.Repeat(chars, length) select s[RNG.Next(s.Length)]).ToArray()); } public static DateTime UnixTimeStampToDateTime(ulong unixTimeStamp, bool outputAsLocalTime = false) { DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); if (outputAsLocalTime) { return dateTime.AddSeconds(unixTimeStamp).ToLocalTime(); } return dateTime.AddSeconds(unixTimeStamp).ToUniversalTime(); } public static ulong DateTimeToUnixTimeStamp(DateTime dateTime) { DateTime dateTime2 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); return (ulong)(dateTime.ToUniversalTime() - dateTime2).TotalSeconds; } private void Awake() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) DebugLogger.Prefix = "de.givenameplz.statslogger"; Stats.LoadSettings(); new Harmony("de.givenameplz.statslogger").PatchAll(); UserInterface.Init(); } private void Update() { //IL_0563: Unknown result type (might be due to invalid IL or missing references) //IL_0568: Unknown result type (might be due to invalid IL or missing references) //IL_05e2: Unknown result type (might be due to invalid IL or missing references) if (DamageAndHits.LastCleanupAtkByPlayer.CompareTo(DateTime.Now.AddSeconds(-DamageAndHits.IntervalSecondsAtkByPlayer)) < 0) { DamageAndHits.LastCleanupAtkByPlayer = DateTime.Now; if (DamageAndHits.RecentlyAttackedByPlayer.Count > 0) { foreach (int item in DamageAndHits.RecentlyAttackedByPlayer.Keys.Where((int key) => DamageAndHits.RecentlyAttackedByPlayer[key].Time < DateTime.Now.AddSeconds(-DamageAndHits.TTLSecondsAtkByPlayer)).ToList()) { DebugLogger.Log($"[DamageAndHits.RecentlyAttackedByPlayer] REM {item}"); DamageAndHits.RecentlyAttackedByPlayer.Remove(item); } } } if (DamageAndHits.LastCleanupAtkPlayer.CompareTo(DateTime.Now.AddSeconds(-DamageAndHits.IntervalSecondsAtkPlayer)) < 0) { DamageAndHits.LastCleanupAtkPlayer = DateTime.Now; if (DamageAndHits.LastAttackedPlayer != null && DamageAndHits.LastAttackedPlayer.Time < DateTime.Now.AddSeconds(-DamageAndHits.TTLSecondsAtkPlayer)) { DebugLogger.Log("[DamageAndHits.LastAttackedPlayer] REM " + (Object.op_Implicit((Object)(object)DamageAndHits.LastAttackedPlayer.Character) ? DamageAndHits.LastAttackedPlayer.Character.m_name : "unknown")); DamageAndHits.LastAttackedPlayer = null; } } if (Structures.LastCleanup.CompareTo(DateTime.Now.AddSeconds(-Structures.IntervalSeconds)) < 0) { Structures.LastCleanup = DateTime.Now; if (Structures.LastAttackedByPlayer.Count > 0) { foreach (int item2 in Structures.LastAttackedByPlayer.Keys.Where((int key) => Structures.LastAttackedByPlayer[key].Time < DateTime.Now.AddSeconds(-Structures.TTLSeconds)).ToList()) { DebugLogger.Log($"[Structures.LastAttackedByPlayer] REM {item2}"); Structures.LastAttackedByPlayer.Remove(item2); } } } if (LocalHitCollector.LastCleanup.CompareTo(DateTime.Now.AddSeconds(-LocalHitCollector.IntervalSeconds)) < 0 && LocalHitCollector.Hits.Count > 0) { foreach (LocalHitCollector.LocalHit item3 in LocalHitCollector.Hits.Where((LocalHitCollector.LocalHit item) => item.Time < DateTime.Now.AddSeconds(0f - item.TTL)).ToList()) { DebugLogger.Log($"[LocalHitCollector.Hits] REM {((ZDOID)(ref item3.UID)).ID}"); LocalHitCollector.Hits.Remove(item3); } } if (!((Object)(object)Player.m_localPlayer != (Object)null)) { return; } Stats.AutoSave(Player.m_localPlayer.GetPlayerName()); float deltaTime = Time.deltaTime; Stats.BurstUpdate(deltaTime); if (ShipTriggers.IsShipping) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Shipping.ToString(), deltaTime, useTimeBuffer: true); } if (((Character)Player.m_localPlayer).IsSwimming()) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Swimming.ToString(), deltaTime, useTimeBuffer: true); } if (((Character)Player.m_localPlayer).IsSneaking()) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Sneaking.ToString(), deltaTime, useTimeBuffer: true); } if (((Character)Player.m_localPlayer).IsCrouching()) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Crouching.ToString(), deltaTime, useTimeBuffer: true); } if (((Character)Player.m_localPlayer).IsSitting()) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Sitting.ToString(), deltaTime, useTimeBuffer: true); } if (Player.m_localPlayer.IsSafeInHome()) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.SafeAtHome.ToString(), deltaTime, useTimeBuffer: true); } if (((Character)Player.m_localPlayer).IsPVPEnabled()) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.PvPModeEnabled.ToString(), deltaTime, useTimeBuffer: true); } if (((Character)Player.m_localPlayer).IsRunning()) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Running.ToString(), deltaTime, useTimeBuffer: true); } if (((Character)Player.m_localPlayer).InBed()) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Sleeping.ToString(), deltaTime, useTimeBuffer: true); } if (VagonCheck.IsVagoning) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Carting.ToString(), deltaTime, useTimeBuffer: true); } if (((Character)Player.m_localPlayer).IsEncumbered()) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Encumbered.ToString(), deltaTime, useTimeBuffer: true); } if (Player.m_localPlayer.InShelter()) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Sheltered.ToString(), deltaTime, useTimeBuffer: true); } if (((Character)Player.m_localPlayer).InInterior()) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Inside.ToString(), deltaTime, useTimeBuffer: true); } if (((Character)Player.m_localPlayer).InPlaceMode()) { Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Building.ToString(), deltaTime, useTimeBuffer: true); } Settings stats = Stats; Biome currentBiome = Player.m_localPlayer.GetCurrentBiome(); stats.UpdateSingle(Settings.LogActions.Biome, ((object)(Biome)(ref currentBiome)).ToString(), deltaTime, useTimeBuffer: true); Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.LastDeath.ToString(), deltaTime, useTimeBuffer: true); Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.LastDamageTaken.ToString(), deltaTime, useTimeBuffer: true); foreach (StatusEffect statusEffect in ((SEMan)typeof(Player).GetField("m_seman", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(Player.m_localPlayer)).GetStatusEffects()) { Stats.UpdateSingle(Settings.LogActions.StatusEffect, statusEffect.m_name, deltaTime, useTimeBuffer: true); } Stats.UpdateTotal(Settings.LogActions.Time, deltaTime, useTimeBuffer: true); } public static bool IsLocalOwner(ZNetView nw) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { return false; } ZDOID zDOID = ((Character)Player.m_localPlayer).GetZDOID(); if ((Object)(object)nw == (Object)null) { return false; } ZDO zDO = nw.GetZDO(); if (zDO == null) { return false; } if (!zDO.HasOwner()) { return false; } if (zDO.IsOwner()) { return true; } return ((ZDOID)(ref zDOID)).UserID == zDO.GetOwner(); } public static bool IsLocalPlayer(Character character) { if ((Object)(object)Player.m_localPlayer == (Object)null) { return false; } if ((Object)(object)character == (Object)null) { return false; } return ((Object)Player.m_localPlayer).GetInstanceID() == ((Object)character).GetInstanceID(); } public static bool IsLocalPlayerInventory(Inventory inventory) { if ((Object)(object)Player.m_localPlayer == (Object)null) { return false; } if (inventory == null) { return false; } return ((object)((Humanoid)Player.m_localPlayer).GetInventory())?.Equals((object?)inventory) ?? false; } } public class Settings { public enum LogTypes { Single, Total } public enum LogDetails { Target, Level, Weapon, Player } public enum StringDetailsTime { Shipping, Swimming, Sneaking, Sitting, SafeAtHome, PvPModeEnabled, Running, Sleeping, Crouching, Carting, Encumbered, Sheltered, Inside, Building, LastDeath, LastDamageTaken } public enum StringDetailsMisc { Unknown, Tree, TreeLog, EdgeOfTheWorld, Drowning, Gravity } public enum LogActions { Dismissed, Kills, UsedPower, Consumed, Deaths, Crafted, Built, Jumps, Sleeps, Attacks, TakingHits, PickedUp, Dropped, Teleported, DamageDone, DamageTaken, Destroyed, ObjectDamageDone, ObjectAttacks, ObjectsDestroyed, StructureDamageDone, StructureAttacks, StructuresDestroyed, TreeDamageDone, TreeAttacks, TreesCut, RockDamageDone, RockAttacks, RocksMined, Time, StatusEffect, Biome, Blocks, BlockedDamage, Staggers, Interacted, ProjectilesFired } public class CharacterStats { public int Version = 2; public string CharacterName; public List<StatsEntry> Entries = new List<StatsEntry>(); public CharacterStats(string character) { CharacterName = character; } } public class StatsEntry { private string _name; private LogActions _action; private LogTypes _type; private Dictionary<LogDetails, string> _details; public string Name { get { return _name; } set { string[] array = value.Split(new char[1] { '|' }); _type = (LogTypes)Enum.Parse(typeof(LogTypes), array[0], ignoreCase: true); _action = (LogActions)Enum.Parse(typeof(LogActions), array[2], ignoreCase: true); _details = new Dictionary<LogDetails, string>(); string[] array2 = array[1].Split(new char[1] { ',' }); for (int i = 0; i < array2.Length; i++) { string[] array3 = array2[i].Split(new char[1] { ':' }); if (array3.Length == 2 && Enum.TryParse<LogDetails>(array3[0], ignoreCase: true, out var result)) { _details.Add(result, array3[1]); } } _name = value; } } public DateTime FirstLogged { get; set; } public DateTime LastLogged { get; set; } public float Amount { get; set; } public LogActions GetLogAction() { return _action; } public LogTypes GetLogType() { return _type; } public Dictionary<LogDetails, string> GetDetails() { return _details; } public bool IsDetail(LogDetails detail, string value) { if (_details.ContainsKey(detail)) { return _details[detail] == value; } return false; } public string GetDetail(LogDetails detail) { if (_details.ContainsKey(detail)) { return _details[detail]; } return null; } public bool HasOnlyDetail(LogDetails detail) { if (_details.Count == 1 && _details.ContainsKey(detail)) { return true; } return false; } public bool HasDetail(LogDetails detail) { if (_details.ContainsKey(detail)) { return true; } return false; } public string GetPrettyName() { string text = ""; if (_type == LogTypes.Total) { text += "Total "; } if (_details.ContainsKey(LogDetails.Weapon) && !_details.ContainsKey(LogDetails.Target)) { text = text + _details[LogDetails.Weapon] + " "; } if (_details.ContainsKey(LogDetails.Target)) { text = text + _details[LogDetails.Target] + " "; if (_details.ContainsKey(LogDetails.Level)) { text = text + "(Level " + _details[LogDetails.Level] + ") "; } if (_details.ContainsKey(LogDetails.Weapon)) { text = text + "with " + _details[LogDetails.Weapon] + " "; } } return text + _action; } public string GetFormattedAmount() { string text = ""; if (_action.HasFlag(LogActions.Time) || _action.HasFlag(LogActions.Biome) || _action.HasFlag(LogActions.StatusEffect)) { float num = Amount % 60f; float num2 = Amount / 60f % 60f; float num3 = Amount / 3600f; return $"{num3:00}:{num2:00}:{num:00}"; } return Math.Round(Amount, 1).ToString(); } } private static readonly string AppDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "givenameplz", "ValheimStatsLogger"); private static readonly string FilenameSettings = Path.Combine(AppDataPath, "settings.cfg"); private static readonly string FileExt = "dat"; private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private const int CurrentDataVersion = 2; private CharacterStats StatsInformation; private bool HasChanges; private int BackupsMax = 3; private int BackupsPointer = 1; private DateTime LastSave = DateTime.Now; private readonly int AutoSaveIntervalMinutes = 15; private Dictionary<string, float> TimeBuffer = new Dictionary<string, float>(); private float BurstBuffer; private readonly float BurstLimitSeconds = 5f; public int AnnounceIntervalMinutes = 5; public DateTime LastAnnounce = DateTime.Now; public bool AnnounceChat; public bool AnnounceConsole = true; public bool AnnounceEffect = true; public bool HideDeathStats; public bool DebugMessagesInUnityConsole; public bool ResetFlag; public bool HiddenLogLevelFound; public Settings() { Directory.CreateDirectory(AppDataPath); DebugLogger.Log("[STATS] Initialized."); } public void ResetData(string character) { StatsInformation = new CharacterStats(character); } public DateTime ParseALotOfDateTimeFormats(string rawdate, bool assumeLocal = true) { if (DateTime.TryParse(rawdate, out var result)) { return result; } string[] formats = new string[4] { "dd.MM.yyyy hh:mm:ss", "dd/MM/yyyy hh:mm:ss", "yyyy-MM-dd hh:mm:ss", "MM/dd/yyyy hh:mm:ss tt" }; if (DateTime.TryParseExact(rawdate, formats, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces | (assumeLocal ? DateTimeStyles.AssumeLocal : DateTimeStyles.AssumeUniversal), out result)) { return result; } return DateTime.Now; } public void LoadFile(string character) { string text = StatsLogger.RandomString(5); string text2 = Path.Combine(AppDataPath, character + "." + FileExt); string destFileName = Path.Combine(AppDataPath, character + "." + FileExt + ".fkt-" + text); StatsInformation = new CharacterStats(character); if (!File.Exists(text2)) { return; } FileStream fileStream = null; try { fileStream = new FileStream(text2, FileMode.Open); BinaryReader binaryReader = new BinaryReader(fileStream); int num = binaryReader.ReadInt32(); string destFileName2 = Path.Combine(AppDataPath, $"{character}.{FileExt}.v{num}-{text}"); switch (num) { case 1: { DebugLogger.LogWarning($"[STATS] Converting version '{num}' to '{2}'"); StatsInformation.Version = 2; StatsInformation.CharacterName = binaryReader.ReadString(); int num5 = binaryReader.ReadInt32(); for (int j = 0; j < num5; j++) { StatsInformation.Entries.Add(new StatsEntry { Name = binaryReader.ReadString(), FirstLogged = ParseALotOfDateTimeFormats(binaryReader.ReadString()), LastLogged = ParseALotOfDateTimeFormats(binaryReader.ReadString()), Amount = binaryReader.ReadSingle() }); ulong num6 = StatsLogger.DateTimeToUnixTimeStamp(StatsInformation.Entries[j].FirstLogged); ulong num7 = StatsLogger.DateTimeToUnixTimeStamp(StatsInformation.Entries[j].LastLogged); DebugLogger.Log($"{StatsInformation.Entries[j].Name} ({StatsInformation.Entries[j].Amount})"); DebugLogger.Log($"FIRST: {StatsInformation.Entries[j].FirstLogged} | {num6} | UTC: {StatsLogger.UnixTimeStampToDateTime(num6)} | Local: {StatsLogger.UnixTimeStampToDateTime(num6, outputAsLocalTime: true)}"); DebugLogger.Log($"LAST: {StatsInformation.Entries[j].LastLogged} | {num7} | UTC: {StatsLogger.UnixTimeStampToDateTime(num7)} | Local: {StatsLogger.UnixTimeStampToDateTime(num7, outputAsLocalTime: true)}"); } fileStream.Close(); File.Move(text2, destFileName2); HasChanges = true; SaveFile(character); break; } default: DebugLogger.LogError($"[STATS] Wrong version '{num}'"); fileStream.Close(); File.Move(text2, destFileName2); break; case 2: { StatsInformation.Version = num; StatsInformation.CharacterName = binaryReader.ReadString(); int num2 = binaryReader.ReadInt32(); for (int i = 0; i < num2; i++) { StatsInformation.Entries.Add(new StatsEntry { Name = binaryReader.ReadString(), FirstLogged = StatsLogger.UnixTimeStampToDateTime(binaryReader.ReadUInt64(), outputAsLocalTime: true), LastLogged = StatsLogger.UnixTimeStampToDateTime(binaryReader.ReadUInt64(), outputAsLocalTime: true), Amount = binaryReader.ReadSingle() }); ulong num3 = StatsLogger.DateTimeToUnixTimeStamp(StatsInformation.Entries[i].FirstLogged); ulong num4 = StatsLogger.DateTimeToUnixTimeStamp(StatsInformation.Entries[i].LastLogged); DebugLogger.Log($"{StatsInformation.Entries[i].Name} ({StatsInformation.Entries[i].Amount})"); DebugLogger.Log($"FIRST: {StatsInformation.Entries[i].FirstLogged} | {num3} | UTC: {StatsLogger.UnixTimeStampToDateTime(num3)} | Local: {StatsLogger.UnixTimeStampToDateTime(num3, outputAsLocalTime: true)}"); DebugLogger.Log($"LAST: {StatsInformation.Entries[i].LastLogged} | {num4} | UTC: {StatsLogger.UnixTimeStampToDateTime(num4)} | Local: {StatsLogger.UnixTimeStampToDateTime(num4, outputAsLocalTime: true)}"); } fileStream.Close(); break; } } } catch (Exception ex) { DebugLogger.LogError("[STATS] Error loading: " + ex.Message); StatsInformation = new CharacterStats(character); if (File.Exists(text2) && fileStream != null) { fileStream.Close(); File.Move(text2, destFileName); } } } public void SaveFile(string character) { LastSave = DateTime.Now; ApplyTimeBuffers(); if (!HasChanges) { return; } HasChanges = false; string text = Path.Combine(AppDataPath, character + "." + FileExt); string destFileName = Path.Combine(AppDataPath, $"{character}.{FileExt}.bkp{BackupsPointer}"); if (File.Exists(text)) { if (File.Exists(text)) { File.Copy(text, destFileName, overwrite: true); } if (++BackupsPointer > BackupsMax) { BackupsPointer = 1; } } try { FileStream fileStream = new FileStream(text, FileMode.Create); BinaryWriter binaryWriter = new BinaryWriter(fileStream); binaryWriter.Write(StatsInformation.Version); binaryWriter.Write(StatsInformation.CharacterName); binaryWriter.Write(StatsInformation.Entries.Count); foreach (StatsEntry entry in StatsInformation.Entries) { binaryWriter.Write(entry.Name); binaryWriter.Write(StatsLogger.DateTimeToUnixTimeStamp(entry.FirstLogged)); binaryWriter.Write(StatsLogger.DateTimeToUnixTimeStamp(entry.LastLogged)); binaryWriter.Write(entry.Amount); } binaryWriter.Flush(); binaryWriter.Close(); fileStream.Close(); } catch (Exception ex) { DebugLogger.LogError("[STATS] Error saving: " + ex.Message); } } public void AutoSave(string character) { if (HasChanges && LastSave.CompareTo(DateTime.Now.AddMinutes(-AutoSaveIntervalMinutes)) < 0) { SaveFile(character); } } public void LoadSettings() { DebugLogger.Log("[STATS] Loading Settings..."); AnnounceChat = false; AnnounceConsole = true; AnnounceEffect = true; if (!File.Exists(FilenameSettings)) { return; } string[] array = File.ReadAllLines(FilenameSettings); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { '=' }, 2); if (array2.Length != 2) { continue; } DebugLogger.LogTypes result6; if (array2[0] == "announcechat") { if (bool.TryParse(array2[1], out var result)) { AnnounceChat = result; DebugLogger.Log("[STATS] Setting: " + array2[0] + "=" + array2[1]); } } else if (array2[0] == "announceconsole") { if (bool.TryParse(array2[1], out var result2)) { AnnounceConsole = result2; DebugLogger.Log("[STATS] Setting: " + array2[0] + "=" + array2[1]); } } else if (array2[0] == "announceeffect") { if (bool.TryParse(array2[1], out var result3)) { AnnounceEffect = result3; DebugLogger.Log("[STATS] Setting: " + array2[0] + "=" + array2[1]); } } else if (array2[0] == "announceinterval") { if (int.TryParse(array2[1], out var result4)) { AnnounceIntervalMinutes = result4; DebugLogger.Log("[STATS] Setting: " + array2[0] + "=" + array2[1]); } } else if (array2[0] == "hidedeathstats") { if (bool.TryParse(array2[1], out var result5)) { HideDeathStats = result5; DebugLogger.Log("[STATS] Setting: " + array2[0] + "=" + array2[1]); } } else if (array2[0] == "loglevel" && Enum.TryParse<DebugLogger.LogTypes>(array2[1], out result6)) { HiddenLogLevelFound = true; DebugLogger.MinLogLevel = result6; DebugLogger.Log("[STATS] Setting: " + array2[0] + "=" + array2[1]); } } } public void SaveSettings() { DebugLogger.Log("[STATS] Saving Settings..."); List<string> list = new List<string> { "announcechat=" + (AnnounceChat ? "true" : "false"), "announceconsole=" + (AnnounceConsole ? "true" : "false"), "announceeffect=" + (AnnounceEffect ? "true" : "false"), "announceinterval=" + AnnounceIntervalMinutes, "hidedeathstats=" + (HideDeathStats ? "true" : "false") }; if (HiddenLogLevelFound) { list.Add($"loglevel={DebugLogger.MinLogLevel}"); } File.WriteAllLines(FilenameSettings, list); } public void BurstUpdate(float dt) { BurstBuffer += dt; if (BurstBuffer > BurstLimitSeconds) { ApplyTimeBuffers(); } } private void ApplyTimeBuffers() { BurstBuffer = 0f; string[] array = TimeBuffer.Keys.ToArray(); foreach (string text in array) { Update(text, TimeBuffer[text]); TimeBuffer.Remove(text); } } public void UpdateTotal(LogActions action, float value, bool useTimeBuffer = false) { InternalUpdate(LogTypes.Total, "", action, value, useTimeBuffer); } public void UpdateCombinedSingle(LogActions action, Character target, ItemData weapon, float value, bool useTimeBuffer = false) { string text = (((Object)(object)target != (Object)null) ? target.m_name : StringDetailsMisc.Unknown.ToString()); string text2 = ((weapon != null) ? weapon.m_shared.m_name : StringDetailsMisc.Unknown.ToString()); string target2 = $"target:{text},level:{target.GetLevel()},weapon:{text2}"; InternalUpdateSingle(action, target2, value, useTimeBuffer); target2 = $"target:{text},level:{target.GetLevel()}"; InternalUpdateSingle(action, target2, value, useTimeBuffer); target2 = "target:" + text + ",weapon:" + text2; InternalUpdateSingle(action, target2, value, useTimeBuffer); target2 = "target:" + text; InternalUpdateSingle(action, target2, value, useTimeBuffer); target2 = "weapon:" + text2; InternalUpdateSingle(action, target2, value, useTimeBuffer); } public void UpdateCombinedSingle(LogActions action, object target, ItemData weapon, float value, bool useTimeBuffer = false) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: 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_017f: Unknown result type (might be due to invalid IL or missing references) string text; if (target == null) { text = StringDetailsMisc.Unknown.ToString(); } else if (target.GetType() == typeof(TreeBase)) { text = ((Object)(TreeBase)target).name.Replace("(Clone)", "").Trim(); } else if (target.GetType() == typeof(TreeLog)) { text = ((Object)(TreeLog)target).name.Replace("(Clone)", "").Trim(); } else if (!(target.GetType() == typeof(WearNTear))) { text = ((target.GetType() == typeof(Destructible)) ? ((Object)(Destructible)target).name.Replace("(Clone)", "").Trim() : ((target.GetType() == typeof(MineRock5)) ? ((MineRock5)target).m_name : ((target == null || !(target.GetType() == typeof(ItemData))) ? StringDetailsMisc.Unknown.ToString() : ((ItemData)target).m_shared.m_name))); } else { Piece val = (Piece)typeof(WearNTear).GetField("m_piece", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(target); text = ((!((Object)(object)val == (Object)null)) ? val.m_name : ((Object)(WearNTear)target).name.Replace("(Clone)", "").Trim()); } string text2 = ((weapon != null && weapon.m_shared != null) ? weapon.m_shared.m_name : StringDetailsMisc.Unknown.ToString()); string target2; if (target != null) { target2 = "target:" + text + ",weapon:" + text2; InternalUpdateSingle(action, target2, value, useTimeBuffer); target2 = "target:" + text; InternalUpdateSingle(action, target2, value, useTimeBuffer); } target2 = "weapon:" + text2; InternalUpdateSingle(action, target2, value, useTimeBuffer); } public void UpdateSingle(LogActions action, string target, float value, bool useTimeBuffer = false, bool set = false) { string target2 = "target:" + target; InternalUpdateSingle(action, target2, value, useTimeBuffer, set); } private void InternalUpdateSingle(LogActions action, string target, float value, bool useTimeBuffer = false, bool set = false) { InternalUpdate(LogTypes.Single, target, action, value, useTimeBuffer, set); } private void InternalUpdate(LogTypes type, string target, LogActions action, float value, bool useTimeBuffer = false, bool set = false) { string text = type.ToString().ToLower() + "|" + target + "|" + action.ToString().ToLower(); if (useTimeBuffer) { if (!TimeBuffer.ContainsKey(text)) { TimeBuffer.Add(text, 0f); } TimeBuffer[text] += value; } else { if (set && TimeBuffer.ContainsKey(text)) { TimeBuffer.Remove(text); } Update(text, value, set); } } private void Update(string name, float value, bool set = false) { if (StatsInformation == null) { DebugLogger.LogWarning("[STATS] NO CHARACTER LOADED"); return; } HasChanges = true; float oldAmount = 0f; StatsEntry statsEntry = StatsInformation.Entries.Find((StatsEntry x) => x.Name == name); if (statsEntry == null) { statsEntry = new StatsEntry { Name = name, Amount = value, FirstLogged = DateTime.Now, LastLogged = DateTime.Now }; StatsInformation.Entries.Add(statsEntry); } else { oldAmount = statsEntry.Amount; if (set) { statsEntry.Amount = value; } else { statsEntry.Amount += value; } statsEntry.LastLogged = DateTime.Now; } CheckAnnounce(statsEntry, oldAmount); if (DebugMessagesInUnityConsole) { DebugLogger.Log($"[STATS] {statsEntry.Name}: {statsEntry.Amount}"); } } public List<StatsEntry> GetStatsList() { if (StatsInformation == null) { DebugLogger.LogWarning("[STATS] StatsInformation not loaded!"); return new List<StatsEntry>(); } return StatsInformation.Entries; } private void CheckAnnounce(StatsEntry stat, float oldAmount) { //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) if (LastAnnounce.CompareTo(DateTime.Now.AddMinutes(-AnnounceIntervalMinutes)) > 0) { return; } int[] array = new int[7] { 1, 5, 10, 25, 50, 75, 100 }; bool flag = false; int num = 100; if (oldAmount > (float)num) { while (oldAmount > (float)(num / 10)) { num *= 10; } num /= 10; if (stat.Amount - oldAmount >= (float)num) { flag = true; } else if (oldAmount % (float)num > stat.Amount % (float)num) { flag = true; } } else { int[] array2 = array; foreach (int num2 in array2) { if (oldAmount < (float)num2 && stat.Amount >= (float)num2) { flag = true; break; } } } if (flag && (Object)(object)Chat.instance != (Object)null && (!HideDeathStats || (stat.GetLogAction() != LogActions.Deaths && (stat.GetDetail(LogDetails.Target) == null || !(stat.GetDetail(LogDetails.Target).ToLower() == StringDetailsTime.LastDeath.ToString().ToLower()))))) { LastAnnounce = DateTime.Now; string text = "<color=purple>[STATS]</color> " + stat.GetPrettyName() + ": " + stat.GetFormattedAmount(); if (AnnounceEffect && (Object)(object)Player.m_localPlayer != (Object)null) { Transform val = (Transform)typeof(Player).GetField("m_head", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(Player.m_localPlayer); Player.m_localPlayer.m_skillLevelupEffects.Create(val.position, val.rotation, val, 0.2f, -1); MessageHud.instance.QueueUnlockMsg(UserInterface.StatsLoggerIcon, "Character Stats", stat.GetPrettyName() + ": " + stat.GetFormattedAmount()); } if (AnnounceChat && (Object)(object)Chat.instance != (Object)null) { typeof(Chat).GetMethod("AddString", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null).Invoke(Chat.instance, new object[1] { text }); } if (AnnounceConsole && (Object)(object)Console.instance != (Object)null) { typeof(Console).GetMethod("AddString", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null).Invoke(Console.instance, new object[1] { text }); } } } } [HarmonyPatch] public static class Structures { public class Target { public HitData Hit; public ItemData Weapon; public DateTime Time = DateTime.Now; } [HarmonyPatch(typeof(WearNTear), "RPC_Damage")] public static class WearNTear_RPC_Damage_Damage { private static MethodInfo miApplyDamage = AccessTools.Method(typeof(WearNTear), "ApplyDamage", (Type[])null, (Type[])null); private static MethodInfo miDamageTrigger = AccessTools.Method(typeof(WearNTear_RPC_Damage_Damage), "DamageTrigger", (Type[])null, (Type[])null); private static readonly int Inserts = 1; [HarmonyTranspiler] public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown int num = 0; List<CodeInstruction> list = new List<CodeInstruction>(instructions); for (int i = 0; i < list.Count; i++) { if (list[i].opcode == OpCodes.Call && (MethodInfo)list[i].operand == miApplyDamage) { int num2 = 2; list.Insert(i + num2, new CodeInstruction(OpCodes.Call, (object)miDamageTrigger)); list.Insert(i + num2, list[i - 2]); list.Insert(i + num2, list[i - 1]); list.Insert(i + num2, list[i - 3]); DebugLogger.Log($"{typeof(WearNTear_RPC_Damage_Damage)} [WearNTear RPC_Damage {miDamageTrigger.Name}] applied."); num++; } } if (num == 0) { DebugLogger.LogError($"{typeof(WearNTear_RPC_Damage_Damage)} [WearNTear RPC_Damage] failed."); } else if (num != Inserts) { DebugLogger.LogWarning($"{typeof(WearNTear_RPC_Damage_Damage)} [WearNTear RPC_Damage] applied {num}/{Inserts}."); } return list.AsEnumerable(); } public static void DamageTrigger(WearNTear target, HitData hit, float dmg) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!hit.HaveAttacker()) { return; } Character attacker = hit.GetAttacker(); if (StatsLogger.IsLocalPlayer(attacker)) { ItemData currentWeapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(); if (LastAttackedByPlayer.ContainsKey(((Object)target).GetInstanceID())) { LastAttackedByPlayer.Remove(((Object)target).GetInstanceID()); } LastAttackedByPlayer.Add(((Object)target).GetInstanceID(), new Target { Hit = hit, Time = DateTime.Now, Weapon = currentWeapon }); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.StructureAttacks, (object)target, currentWeapon, 1f, useTimeBuffer: false); StatsLogger.Stats.UpdateTotal(Settings.LogActions.StructureAttacks, 1f); StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.StructureDamageDone, (object)target, currentWeapon, dmg, useTimeBuffer: false); StatsLogger.Stats.UpdateTotal(Settings.LogActions.StructureDamageDone, dmg); } else if (LastAttackedByPlayer.ContainsKey(((Object)target).GetInstanceID())) { LastAttackedByPlayer.Remove(((Object)target).GetInstanceID()); } } } public static Dictionary<int, Target> LastAttackedByPlayer = new Dictionary<int, Target>(); public static DateTime LastCleanup = DateTime.Now; public static readonly int IntervalSeconds = 10; public static readonly int TTLSeconds = 5; [HarmonyPrefix] [HarmonyPatch(typeof(WearNTear), "Destroy")] private static void DestroyPieces(ref WearNTear __instance, Piece ___m_piece) { if (LastAttackedByPlayer.ContainsKey(((Object)__instance).GetInstanceID())) { ItemData weapon = LastAttackedByPlayer[((Object)__instance).GetInstanceID()].Weapon; StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.StructuresDestroyed, (object)__instance, weapon, 1f, useTimeBuffer: false); StatsLogger.Stats.UpdateTotal(Settings.LogActions.StructuresDestroyed, 1f); LastAttackedByPlayer.Remove(((Object)__instance).GetInstanceID()); } } [HarmonyPrefix] [HarmonyPatch(typeof(WearNTear), "Damage")] private static void Damage(ref WearNTear __instance, ref HitData hit, ZNetView ___m_nview) { //IL_003d: 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) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (hit.HaveAttacker()) { Character attacker = hit.GetAttacker(); if (StatsLogger.IsLocalPlayer(attacker) && !StatsLogger.IsLocalOwner(___m_nview)) { LocalHitCollector.Hits.Add(new LocalHitCollector.LocalHit { Hit = hit, Target = __instance, Weapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(), Type = ((object)__instance).GetType(), UID = ___m_nview.GetZDO().m_uid }); } } } } [HarmonyPatch] public static class Trees { [HarmonyPatch(typeof(TreeBase), "RPC_Damage")] public static class TreeBase_RPC_Damage_DamageAndDeath { private static MethodInfo miShake = AccessTools.Method(typeof(TreeBase), "Shake", (Type[])null, (Type[])null); private static MethodInfo miSpawnLog = AccessTools.Method(typeof(TreeBase), "SpawnLo