using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using BoneLib; using BoneLib.BoneMenu; using HarmonyLib; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.Pool; using Il2CppSLZ.Marrow.Warehouse; using LabFusion.Network; using LabFusion.Player; using LabFusion.Utilities; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using SpawnLogger; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(/*Could not decode attribute arguments.*/)] [assembly: MelonInfo(typeof(SpawnLoggerMod), "Spawn Logger", "1.1.0", "Local Utility", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : System.Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(/*Could not decode attribute arguments.*/)] internal sealed class RefSafetyRulesAttribute : System.Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SpawnLogger { public sealed class SpawnLoggerMod : MelonMod { private sealed class PlayerLog { public string DisplayName; public readonly List Entries = new List(); public Page MenuPage; } private sealed class LogEntry { public ulong PlatformID; public string Line; } [CompilerGenerated] private static class <>O { public static PlayerUpdate <0>__OnPlayerJoined; public static PlayerUpdate <1>__OnPlayerLeft; public static UpdateEvent <2>__OnTargetLevelLoaded; public static Action <3>__ClearAll; } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Action <>9__17_0; public static Action <>9__22_0; public static Action <>9__22_1; public static Action <>9__23_0; public static Action <>9__23_1; internal void b__17_0(bool value) { _autoClearOnLeave.Value = value; MelonPreferences.Save(); } internal void b__22_0() { } internal void b__22_1() { } internal void b__23_0() { } internal void b__23_1() { } } private const int MaxLogEntries = 30; private const float LoggingArmDelaySeconds = 5f; private static readonly Dictionary _playerLogs = new Dictionary(); private static readonly List _allEntries = new List(); private static Page _allLogPage; private static Page _playersPage; private static bool _menuCreated; private static bool _loggingArmed; private static float _armTimer; private static MelonPreferences_Category _prefs; private static MelonPreferences_Entry _autoClearOnLeave; public override void OnInitializeMelon() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown _prefs = MelonPreferences.CreateCategory("SpawnLogger"); _autoClearOnLeave = _prefs.CreateEntry("AutoClearOnPlayerLeave", true, "Auto-Clear On Player Leave", "When a player disconnects, automatically clear their per-player log and remove their entries from the merged All Spawns log.", false, false, (ValueValidator)null, (string)null); Hooking.OnUIRigCreated += new Action(CreateMenu); object obj = <>O.<0>__OnPlayerJoined; if (obj == null) { PlayerUpdate val = OnPlayerJoined; <>O.<0>__OnPlayerJoined = val; obj = (object)val; } MultiplayerHooking.OnPlayerJoined += (PlayerUpdate)obj; object obj2 = <>O.<1>__OnPlayerLeft; if (obj2 == null) { PlayerUpdate val2 = OnPlayerLeft; <>O.<1>__OnPlayerLeft = val2; obj2 = (object)val2; } MultiplayerHooking.OnPlayerLeft += (PlayerUpdate)obj2; object obj3 = <>O.<2>__OnTargetLevelLoaded; if (obj3 == null) { UpdateEvent val3 = OnTargetLevelLoaded; <>O.<2>__OnTargetLevelLoaded = val3; obj3 = (object)val3; } MultiplayerHooking.OnTargetLevelLoaded += (UpdateEvent)obj3; } public override void OnDeinitializeMelon() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown Hooking.OnUIRigCreated -= new Action(CreateMenu); object obj = <>O.<0>__OnPlayerJoined; if (obj == null) { PlayerUpdate val = OnPlayerJoined; <>O.<0>__OnPlayerJoined = val; obj = (object)val; } MultiplayerHooking.OnPlayerJoined -= (PlayerUpdate)obj; object obj2 = <>O.<1>__OnPlayerLeft; if (obj2 == null) { PlayerUpdate val2 = OnPlayerLeft; <>O.<1>__OnPlayerLeft = val2; obj2 = (object)val2; } MultiplayerHooking.OnPlayerLeft -= (PlayerUpdate)obj2; object obj3 = <>O.<2>__OnTargetLevelLoaded; if (obj3 == null) { UpdateEvent val3 = OnTargetLevelLoaded; <>O.<2>__OnTargetLevelLoaded = val3; obj3 = (object)val3; } MultiplayerHooking.OnTargetLevelLoaded -= (UpdateEvent)obj3; } public override void OnUpdate() { if (!_loggingArmed && !(_armTimer <= 0f)) { _armTimer -= Time.deltaTime; if (_armTimer <= 0f) { _loggingArmed = true; } } } private static void OnTargetLevelLoaded() { _loggingArmed = false; _armTimer = 5f; } private void CreateMenu() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) if (_menuCreated) { return; } Page val = Page.Root.CreatePage("Spawn Logger", Color.yellow, 0, true); Color red = Color.red; object obj = <>O.<3>__ClearAll; if (obj == null) { Action val2 = ClearAll; <>O.<3>__ClearAll = val2; obj = (object)val2; } val.CreateFunction("Clear All Logs", red, (Action)obj); val.CreateBool("Auto-Clear On Leave", Color.yellow, _autoClearOnLeave.Value, (Action)delegate(bool value) { _autoClearOnLeave.Value = value; MelonPreferences.Save(); }); _allLogPage = val.CreatePage("All Spawns", Color.yellow, 0, true); _playersPage = val.CreatePage("Players", Color.yellow, 0, true); _menuCreated = true; Enumerator enumerator = PlayerIDManager.PlayerIDs.GetEnumerator(); try { while (enumerator.MoveNext()) { PlayerID current = enumerator.Current; GetOrCreatePlayerLog(current); } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } RefreshAllLogPage(); } private static void OnPlayerJoined(PlayerID id) { GetOrCreatePlayerLog(id); } private static void OnPlayerLeft(PlayerID id) { PlayerLog playerLog = default(PlayerLog); if (id == null || !_autoClearOnLeave.Value || !_playerLogs.TryGetValue(id.PlatformID, ref playerLog)) { return; } _playerLogs.Remove(id.PlatformID); if (playerLog.MenuPage != null) { Page playersPage = _playersPage; if (playersPage != null) { playersPage.RemovePage(playerLog.MenuPage); } } _allEntries.RemoveAll((Predicate)((LogEntry entry) => entry.PlatformID == id.PlatformID)); RefreshAllLogPage(); } private static void ClearAll() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) _allEntries.Clear(); RefreshAllLogPage(); Enumerator enumerator = _playerLogs.Values.GetEnumerator(); try { while (enumerator.MoveNext()) { PlayerLog current = enumerator.Current; current.Entries.Clear(); RefreshPlayerPage(current); } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } } private static PlayerLog GetOrCreatePlayerLog(PlayerID id) { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) if (id == null) { return null; } string text2 = default(string); string text = (MetadataHelper.TryGetDisplayName(id, ref text2) ? text2 : $"Player {id.SmallID}"); PlayerLog playerLog = default(PlayerLog); if (!_playerLogs.TryGetValue(id.PlatformID, ref playerLog)) { playerLog = new PlayerLog { DisplayName = text }; _playerLogs[id.PlatformID] = playerLog; } else if (playerLog.DisplayName != text) { playerLog.DisplayName = text; if (playerLog.MenuPage != null) { playerLog.MenuPage.Name = text; } } if (playerLog.MenuPage == null && _playersPage != null) { playerLog.MenuPage = _playersPage.CreatePage(text, Color.white, 0, true); RefreshPlayerPage(playerLog); } return playerLog; } private static void RefreshPlayerPage(PlayerLog log) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown if (log?.MenuPage == null) { return; } log.MenuPage.RemoveAll(); if (log.Entries.Count == 0) { Page menuPage = log.MenuPage; Color gray = Color.gray; object obj = <>c.<>9__22_0; if (obj == null) { Action val = delegate { }; <>c.<>9__22_0 = val; obj = (object)val; } menuPage.CreateFunction("No spawns logged yet", gray, (Action)obj); return; } Enumerator enumerator = log.Entries.GetEnumerator(); try { while (enumerator.MoveNext()) { string current = enumerator.Current; Page menuPage2 = log.MenuPage; Color white = Color.white; object obj2 = <>c.<>9__22_1; if (obj2 == null) { Action val2 = delegate { }; <>c.<>9__22_1 = val2; obj2 = (object)val2; } menuPage2.CreateFunction(current, white, (Action)obj2); } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } } private static void RefreshAllLogPage() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown if (_allLogPage == null) { return; } _allLogPage.RemoveAll(); if (_allEntries.Count == 0) { Page allLogPage = _allLogPage; Color gray = Color.gray; object obj = <>c.<>9__23_0; if (obj == null) { Action val = delegate { }; <>c.<>9__23_0 = val; obj = (object)val; } allLogPage.CreateFunction("No spawns logged yet", gray, (Action)obj); return; } Enumerator enumerator = _allEntries.GetEnumerator(); try { while (enumerator.MoveNext()) { LogEntry current = enumerator.Current; Page allLogPage2 = _allLogPage; string line = current.Line; Color white = Color.white; object obj2 = <>c.<>9__23_1; if (obj2 == null) { Action val2 = delegate { }; <>c.<>9__23_1 = val2; obj2 = (object)val2; } allLogPage2.CreateFunction(line, white, (Action)obj2); } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } } private static string ResolveItemName(string barcode) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown AssetWarehouse instance = AssetWarehouse.Instance; Crate val = default(Crate); if (instance != null && instance.TryGetCrate(new Barcode(barcode), ref val) && (Object)(object)val != (Object)null) { string title = ((Scannable)val).Title; if (!string.IsNullOrWhiteSpace(title)) { return title; } } return barcode; } internal static void LogSpawn(byte ownerId, string barcode) { if (!_loggingArmed || string.IsNullOrEmpty(barcode) || barcode.StartsWith("SLZ.", (StringComparison)5)) { return; } PlayerID playerID = PlayerIDManager.GetPlayerID(ownerId); PlayerLog orCreatePlayerLog = GetOrCreatePlayerLog(playerID); string text = orCreatePlayerLog?.DisplayName ?? $"Unknown (ID {ownerId})"; string text2 = ResolveItemName(barcode); string text3 = $"[{System.DateTime.Now:HH:mm:ss}] {text}: {text2}"; _allEntries.Insert(0, new LogEntry { PlatformID = ((playerID != null) ? playerID.PlatformID : 0), Line = text3 }); if (_allEntries.Count > 30) { _allEntries.RemoveAt(_allEntries.Count - 1); } RefreshAllLogPage(); if (orCreatePlayerLog != null) { orCreatePlayerLog.Entries.Insert(0, text3); if (orCreatePlayerLog.Entries.Count > 30) { orCreatePlayerLog.Entries.RemoveAt(orCreatePlayerLog.Entries.Count - 1); } RefreshPlayerPage(orCreatePlayerLog); } MelonLogger.Msg(text3); } } [HarmonyPatch(typeof(SpawnResponseMessage), "OnSpawnFinished")] internal static class SpawnResponseMessage_OnSpawnFinished_Patch { private static void Postfix(SpawnResponseData data, Poolee poolee) { if (!((Object)(object)poolee != (Object)null) || !((Object)(object)((Component)poolee).gameObject.GetComponentInChildren() != (Object)null)) { SpawnLoggerMod.LogSpawn(data.OwnerID, data.SpawnData.Barcode); } } } }