using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BoneLib;
using BoneLib.BoneMenu;
using HarmonyLib;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.Pool;
using Il2CppSLZ.Marrow.Warehouse;
using LabFusion.Entities;
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", "2.5.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 Identity
{
public byte SmallId;
public string PlatformId;
public string Name;
public bool Departed;
}
private sealed class SpawnSnapshot
{
public Identity ClaimedOwner;
public Identity TransportSender;
public float CapturedAt;
public string AlertReason;
public string Barcode;
public EntitySource Source;
public bool CrateMissing;
}
private sealed class PlayerHistory
{
public Page Page;
public readonly List<FeedItem> Entries = new List<FeedItem>();
}
private sealed class FeedItem
{
public string PlatformId;
public string Owner;
public string Item;
public string Time;
public int Count = 1;
public float LastAt;
public string Text => (Count > 1) ? $"[{Time}] {Owner}: {Item} x{Count}" : $"[{Time}] {Owner}: {Item}";
}
[CompilerGenerated]
private static class <>O
{
public static PlayerUpdate <0>__OnPlayerJoined;
public static PlayerUpdate <1>__OnPlayerLeft;
public static UpdateEvent <2>__OnLevelLoaded;
public static Action <3>__ClearLogs;
public static Action <4>__RefreshCountersPage;
public static Action <5>__LogCounters;
}
[Serializable]
[CompilerGenerated]
private sealed class <>c
{
public static readonly <>c <>9 = new <>c();
public static Action<bool> <>9__36_0;
public static Action<bool> <>9__36_1;
public static Action <>9__37_0;
public static Action <>9__37_1;
public static Action <>9__61_0;
public static Action <>9__61_1;
internal void <CreateMenu>b__36_0(bool value)
{
SavePreference(_clearOnLeave, value);
}
internal void <CreateMenu>b__36_1(bool value)
{
SavePreference(_logSlzContent, value);
}
internal void <RefreshCountersPage>b__37_0()
{
}
internal void <RefreshCountersPage>b__37_1()
{
}
internal void <RenderItems>b__61_0()
{
}
internal void <RenderItems>b__61_1()
{
}
}
private const int MaxEntries = 30;
private const float LevelSettleSeconds = 5f;
private const float SnapshotLifetimeSeconds = 300f;
private const float CleanupIntervalSeconds = 5f;
private const float BurstWindowSeconds = 4f;
private const float RenderIntervalSeconds = 0.5f;
private static readonly Dictionary<ushort, SpawnSnapshot> PendingSpawns = new Dictionary<ushort, SpawnSnapshot>();
private static readonly HashSet<ushort> SeenEntityIds = new HashSet<ushort>();
private static readonly Dictionary<string, PlayerHistory> PlayerHistories = new Dictionary<string, PlayerHistory>();
private static readonly Dictionary<byte, Identity> KnownIdentities = new Dictionary<byte, Identity>();
private static readonly List<FeedItem> SpawnFeed = new List<FeedItem>();
private static readonly HashSet<PlayerHistory> DirtyHistories = new HashSet<PlayerHistory>();
private static readonly SortedDictionary<string, int> Counters = new SortedDictionary<string, int>((IComparer<string>)(object)StringComparer.Ordinal);
private static MelonPreferences_Entry<bool> _clearOnLeave;
private static MelonPreferences_Entry<bool> _logSlzContent;
private static Page _spawnPage;
private static Page _playersPage;
private static Page _countersPage;
private static bool _menuCreated;
private static bool _spawnPageDirty;
private static float _loggingReadyAt = 1f / 0f;
private static float _nextCleanupAt;
private static float _nextRenderAt;
public override void OnInitializeMelon()
{
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Expected O, but got Unknown
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Expected O, but got Unknown
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Unknown result type (might be due to invalid IL or missing references)
//IL_00a4: Expected O, but got Unknown
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Expected O, but got Unknown
MelonPreferences_Category val = MelonPreferences.CreateCategory("SpawnLogger");
_clearOnLeave = val.CreateEntry<bool>("AutoClearOnPlayerLeave", true, "Auto-Clear On Player Leave", "Remove a disconnected player's entries from the in-game log.", false, false, (ValueValidator)null, (string)null);
_logSlzContent = val.CreateEntry<bool>("LogSlzContent", false, "Log SLZ Content", "Include SLZ-authored and internal-pallet spawns. Magazines are always excluded.", false, false, (ValueValidator)null, (string)null);
EosPeerCapture.TryApply(((MelonBase)this).HarmonyInstance);
Hooking.OnUIRigCreated += new Action(CreateMenu);
object obj = <>O.<0>__OnPlayerJoined;
if (obj == null)
{
PlayerUpdate val2 = OnPlayerJoined;
<>O.<0>__OnPlayerJoined = val2;
obj = (object)val2;
}
MultiplayerHooking.OnPlayerJoined += (PlayerUpdate)obj;
object obj2 = <>O.<1>__OnPlayerLeft;
if (obj2 == null)
{
PlayerUpdate val3 = OnPlayerLeft;
<>O.<1>__OnPlayerLeft = val3;
obj2 = (object)val3;
}
MultiplayerHooking.OnPlayerLeft += (PlayerUpdate)obj2;
object obj3 = <>O.<2>__OnLevelLoaded;
if (obj3 == null)
{
UpdateEvent val4 = OnLevelLoaded;
<>O.<2>__OnLevelLoaded = val4;
obj3 = (object)val4;
}
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>__OnLevelLoaded;
if (obj3 == null)
{
UpdateEvent val3 = OnLevelLoaded;
<>O.<2>__OnLevelLoaded = val3;
obj3 = (object)val3;
}
MultiplayerHooking.OnTargetLevelLoaded -= (UpdateEvent)obj3;
LogCounters();
ResetLevelState();
KnownIdentities.Clear();
DirtyHistories.Clear();
}
public override void OnUpdate()
{
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
float realtimeSinceStartup = Time.realtimeSinceStartup;
FlushPages(realtimeSinceStartup);
if (realtimeSinceStartup < _nextCleanupAt || PendingSpawns.Count == 0)
{
return;
}
_nextCleanupAt = realtimeSinceStartup + 5f;
List<ushort> val = null;
Enumerator<ushort, SpawnSnapshot> enumerator = PendingSpawns.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
KeyValuePair<ushort, SpawnSnapshot> current = enumerator.Current;
if (realtimeSinceStartup - current.Value.CapturedAt > 300f)
{
(val ?? (val = new List<ushort>())).Add(current.Key);
}
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
if (val == null)
{
return;
}
Enumerator<ushort> enumerator2 = val.GetEnumerator();
try
{
while (enumerator2.MoveNext())
{
ushort current2 = enumerator2.Current;
SpawnSnapshot snapshot = PendingSpawns[current2];
PendingSpawns.Remove(current2);
try
{
ReportIncomplete(current2, snapshot);
}
catch (System.Exception ex)
{
MelonLogger.Warning("Spawn Logger could not report an unfinished spawn: " + ex.Message);
}
}
}
finally
{
((System.IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
}
}
private static void OnLevelLoaded()
{
LogCounters();
ResetLevelState();
_loggingReadyAt = Time.realtimeSinceStartup + 5f;
}
private static void Count(string key)
{
int num = default(int);
Counters.TryGetValue(key, ref num);
Counters[key] = num + 1;
}
private static void LogCounters()
{
//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_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
if (Counters.Count == 0)
{
return;
}
List<string> val = new List<string>();
Enumerator<string, int> enumerator = Counters.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
KeyValuePair<string, int> current = enumerator.Current;
val.Add($"{current.Key}={current.Value}");
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
MelonLogger.Msg("Counters: " + string.Join("; ", (System.Collections.Generic.IEnumerable<string>)val));
}
private static void FlushPages(float now)
{
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
if (now < _nextRenderAt || (!_spawnPageDirty && DirtyHistories.Count == 0))
{
return;
}
_nextRenderAt = now + 0.5f;
Enumerator<PlayerHistory> enumerator = DirtyHistories.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
PlayerHistory current = enumerator.Current;
RefreshHistory(current);
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
DirtyHistories.Clear();
if (_spawnPageDirty)
{
_spawnPageDirty = false;
RefreshSpawnPage();
}
}
private static void MarkDirty(PlayerHistory history)
{
DirtyHistories.Add(history);
_spawnPageDirty = true;
}
private static void ResetLevelState()
{
PendingSpawns.Clear();
SeenEntityIds.Clear();
_nextCleanupAt = 0f;
}
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_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_011c: 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>__ClearLogs;
if (obj == null)
{
Action val2 = ClearLogs;
<>O.<3>__ClearLogs = val2;
obj = (object)val2;
}
val.CreateFunction("Clear All Logs", red, (Action)obj);
val.CreateBool("Auto-Clear On Leave", Color.yellow, _clearOnLeave.Value, (Action<bool>)delegate(bool value)
{
SavePreference(_clearOnLeave, value);
});
val.CreateBool("Log SLZ Content", Color.yellow, _logSlzContent.Value, (Action<bool>)delegate(bool value)
{
SavePreference(_logSlzContent, value);
});
_spawnPage = val.CreatePage("All Spawns", Color.yellow, 0, true);
_playersPage = val.CreatePage("Players", Color.yellow, 0, true);
_countersPage = val.CreatePage("Debug Counters", Color.gray, 0, true);
_menuCreated = true;
Enumerator<PlayerID> enumerator = PlayerIDManager.PlayerIDs.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
PlayerID current = enumerator.Current;
RefreshHistory(GetOrCreateHistory(CreateIdentity(current, 0)));
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
RefreshSpawnPage();
RefreshCountersPage();
}
private static void RefreshCountersPage()
{
//IL_0027: 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_003c: 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_0047: Expected O, but got Unknown
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Expected O, but got Unknown
//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00bd: Expected O, but got Unknown
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: 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_0137: Unknown result type (might be due to invalid IL or missing references)
//IL_013c: Unknown result type (might be due to invalid IL or missing references)
//IL_0142: Expected O, but got Unknown
if (_countersPage == null)
{
return;
}
_countersPage.RemoveAll();
Page countersPage = _countersPage;
Color yellow = Color.yellow;
object obj = <>O.<4>__RefreshCountersPage;
if (obj == null)
{
Action val = RefreshCountersPage;
<>O.<4>__RefreshCountersPage = val;
obj = (object)val;
}
countersPage.CreateFunction("Refresh", yellow, (Action)obj);
Page countersPage2 = _countersPage;
Color yellow2 = Color.yellow;
object obj2 = <>O.<5>__LogCounters;
if (obj2 == null)
{
Action val2 = LogCounters;
<>O.<5>__LogCounters = val2;
obj2 = (object)val2;
}
countersPage2.CreateFunction("Write To Log", yellow2, (Action)obj2);
if (Counters.Count == 0)
{
Page countersPage3 = _countersPage;
Color gray = Color.gray;
object obj3 = <>c.<>9__37_0;
if (obj3 == null)
{
Action val3 = delegate
{
};
<>c.<>9__37_0 = val3;
obj3 = (object)val3;
}
countersPage3.CreateFunction("Nothing counted yet", gray, (Action)obj3);
return;
}
Enumerator<string, int> enumerator = Counters.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
KeyValuePair<string, int> current = enumerator.Current;
Page countersPage4 = _countersPage;
string obj4 = $"{current.Key}: {current.Value}";
Color white = Color.white;
object obj5 = <>c.<>9__37_1;
if (obj5 == null)
{
Action val4 = delegate
{
};
<>c.<>9__37_1 = val4;
obj5 = (object)val4;
}
countersPage4.CreateFunction(obj4, white, (Action)obj5);
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
}
private static void SavePreference(MelonPreferences_Entry<bool> preference, bool value)
{
preference.Value = value;
MelonPreferences.Save();
}
private static void OnPlayerJoined(PlayerID player)
{
if (player != null)
{
RefreshHistory(GetOrCreateHistory(CreateIdentity(player, 0)));
}
}
private static void OnPlayerLeft(PlayerID player)
{
if (player == null)
{
return;
}
PreserveDepartingIdentity(player);
if (_clearOnLeave != null && _clearOnLeave.Value)
{
string platformId = FusionIds.Of(player);
RemovePlayerHistory(platformId);
SpawnFeed.RemoveAll((Predicate<FeedItem>)((FeedItem item) => string.Equals(item.PlatformId, platformId, (StringComparison)4)));
RefreshSpawnPage();
}
}
private static void PreserveDepartingIdentity(PlayerID player)
{
//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)
Identity claimedOwner = CreateIdentity(player, 0);
Enumerator<ushort, SpawnSnapshot> enumerator = PendingSpawns.Values.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
SpawnSnapshot current = enumerator.Current;
if (current.ClaimedOwner.SmallId == player.SmallID && string.IsNullOrEmpty(current.ClaimedOwner.PlatformId))
{
current.ClaimedOwner = claimedOwner;
}
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
}
private static void RemovePlayerHistory(string platformId)
{
PlayerHistory playerHistory = default(PlayerHistory);
if (!PlayerHistories.TryGetValue(platformId, ref playerHistory))
{
return;
}
PlayerHistories.Remove(platformId);
DirtyHistories.Remove(playerHistory);
if (playerHistory.Page != null)
{
Page playersPage = _playersPage;
if (playersPage != null)
{
playersPage.RemovePage(playerHistory.Page);
}
}
}
private static Identity CreateIdentity(PlayerID player, byte fallbackSmallId = 0)
{
if (player == null)
{
Identity identity = default(Identity);
if (KnownIdentities.TryGetValue(fallbackSmallId, ref identity))
{
return new Identity
{
SmallId = identity.SmallId,
PlatformId = identity.PlatformId,
Name = identity.Name,
Departed = true
};
}
return new Identity
{
SmallId = fallbackSmallId,
Name = $"Unknown (ID {fallbackSmallId})"
};
}
string text = default(string);
Identity identity2 = new Identity
{
SmallId = player.SmallID,
PlatformId = FusionIds.Of(player),
Name = (CleanName(MetadataHelper.TryGetDisplayName(player, ref text) ? text : null) ?? $"Player {player.SmallID}")
};
KnownIdentities[player.SmallID] = identity2;
return identity2;
}
private static string CleanName(string raw)
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Expected O, but got Unknown
if (string.IsNullOrEmpty(raw))
{
return null;
}
StringBuilder val = new StringBuilder(raw.Length);
for (int i = 0; i < raw.Length; i++)
{
if (raw[i] == '<')
{
int num = raw.IndexOf('>', i);
if (num > i)
{
i = num;
continue;
}
}
val.Append(raw[i]);
}
string text = ((object)val).ToString().Trim();
return (text.Length == 0) ? null : text;
}
internal static void CaptureResponse(SpawnResponseData data, ReceivedMessage message)
{
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_00de: Unknown result type (might be due to invalid IL or missing references)
//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
if (data?.SpawnData != null)
{
Count("responses received");
string text = FusionIds.Of(message);
PlayerID val = (string.IsNullOrEmpty(text) ? null : FusionIds.Find(text));
Identity transportSender = (string.IsNullOrEmpty(text) ? null : ((val != null) ? CreateIdentity(val, 0) : new Identity
{
PlatformId = text,
Name = "Unknown (" + text + ")"
}));
string barcode = data.SpawnData.Barcode;
PendingSpawns[data.EntityID] = new SpawnSnapshot
{
ClaimedOwner = CreateIdentity(PlayerIDManager.GetPlayerID(data.OwnerID), data.OwnerID),
TransportSender = transportSender,
CapturedAt = Time.realtimeSinceStartup,
AlertReason = BuildAlertReason(data, transportSender),
Barcode = barcode,
Source = data.SpawnData.SpawnSource,
CrateMissing = (!string.IsNullOrWhiteSpace(barcode) && !TryResolveCrate(barcode, out var _))
};
}
}
private static string BuildAlertReason(SpawnResponseData data, Identity transportSender)
{
PlayerID hostID = PlayerIDManager.GetHostID();
bool flag = hostID != null && transportSender != null && !string.Equals(transportSender.PlatformId, FusionIds.Of(hostID), (StringComparison)4);
bool flag2 = NetworkEntityManager.IDManager.RegisteredEntities.GetEntity(data.EntityID) != null;
if (!flag && !flag2)
{
return null;
}
string text = (flag ? "spawn response came directly from a non-host client" : string.Empty);
if (flag && transportSender.SmallId != data.OwnerID)
{
text += $"; payload claimed owner ID {data.OwnerID}";
}
if (flag2)
{
text = text + ((text.Length == 0) ? string.Empty : "; ") + "entity ID was already registered";
}
return text;
}
internal static void CompleteSpawn(SpawnResponseData data, Poolee poolee)
{
//IL_0181: Unknown result type (might be due to invalid IL or missing references)
//IL_0187: Invalid comparison between Unknown and I4
//IL_016d: Unknown result type (might be due to invalid IL or missing references)
if (data?.SpawnData == null)
{
return;
}
SpawnSnapshot spawnSnapshot = default(SpawnSnapshot);
PendingSpawns.TryGetValue(data.EntityID, ref spawnSnapshot);
PendingSpawns.Remove(data.EntityID);
Count("spawns completed");
if (Time.realtimeSinceStartup < _loggingReadyAt)
{
Count("dropped: level settling");
return;
}
if (!SeenEntityIds.Add(data.EntityID))
{
Count("dropped: duplicate entity ID");
return;
}
string barcode = data.SpawnData.Barcode;
if (string.IsNullOrWhiteSpace(barcode))
{
Count("dropped: no barcode");
return;
}
TryResolveCrate(barcode, out var crate);
if (IsMagazine(poolee, crate, barcode))
{
Count("dropped: magazine");
return;
}
if (ShouldExcludeSlz(crate, barcode))
{
Count("dropped: SLZ content");
return;
}
spawnSnapshot = spawnSnapshot ?? new SpawnSnapshot
{
ClaimedOwner = CreateIdentity(PlayerIDManager.GetPlayerID(data.OwnerID), data.OwnerID)
};
ResolveLateOwner(spawnSnapshot, data);
string itemName = (string.IsNullOrWhiteSpace((crate != null) ? ((Scannable)crate).Title : null) ? barcode : ((Scannable)crate).Title);
if (!string.IsNullOrEmpty(spawnSnapshot.AlertReason))
{
AddAlert(spawnSnapshot, data.SpawnData.SpawnSource, itemName);
}
else if ((int)data.SpawnData.SpawnSource != 1)
{
AddNormalSpawn(spawnSnapshot.ClaimedOwner, itemName);
}
else
{
Count("dropped: scene source");
}
}
private static void ResolveLateOwner(SpawnSnapshot snapshot, SpawnResponseData data)
{
if (string.IsNullOrEmpty(snapshot.ClaimedOwner.PlatformId))
{
Identity identity = CreateIdentity(PlayerIDManager.GetPlayerID(data.OwnerID), data.OwnerID);
if (!string.IsNullOrEmpty(identity.PlatformId))
{
snapshot.ClaimedOwner = identity;
}
}
}
private static void ReportIncomplete(ushort entityId, SpawnSnapshot snapshot)
{
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Invalid comparison between Unknown and I4
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
if (!snapshot.CrateMissing)
{
Count("never completed: crate was installed");
return;
}
Count("never completed: crate not installed");
if (snapshot.CapturedAt < _loggingReadyAt || !SeenEntityIds.Add(entityId))
{
return;
}
string barcode = snapshot.Barcode;
if ((int)snapshot.Source != 1 && !IsMagazineBarcode(barcode) && ((_logSlzContent != null && _logSlzContent.Value) || !barcode.StartsWith("SLZ.", (StringComparison)5)))
{
if (!string.IsNullOrEmpty(snapshot.AlertReason))
{
AddAlert(snapshot, snapshot.Source, barcode);
}
else
{
AddNormalSpawn(snapshot.ClaimedOwner, barcode + " (not installed)");
}
}
}
private static bool TryResolveCrate(string barcode, out Crate crate)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Expected O, but got Unknown
crate = null;
AssetWarehouse instance = AssetWarehouse.Instance;
return instance != null && instance.TryGetCrate(new Barcode(barcode), ref crate) && (Object)(object)crate != (Object)null;
}
private static bool ShouldExcludeSlz(Crate crate, string barcode)
{
return (_logSlzContent == null || !_logSlzContent.Value) && IsSlzContent(crate, barcode);
}
private static bool IsSlzContent(Crate crate, string barcode)
{
if (barcode.StartsWith("SLZ.", (StringComparison)5))
{
return true;
}
Pallet val = ((crate != null) ? crate.Pallet : null);
if ((Object)(object)val == (Object)null)
{
return false;
}
if (val.Internal)
{
return true;
}
string text = val.Author?.Trim();
return string.Equals(text, "SLZ", (StringComparison)5) || string.Equals(text, "Stress Level Zero", (StringComparison)5) || string.Equals(text, "StressLevelZero", (StringComparison)5);
}
private static bool IsMagazine(Poolee poolee, Crate crate, string barcode)
{
if ((Object)(object)poolee != (Object)null && (Object)(object)((Component)poolee).gameObject.GetComponentInChildren<Magazine>(true) != (Object)null)
{
return true;
}
string text = ((crate != null) ? ((Scannable)crate).Title : null) ?? string.Empty;
return text.IndexOf("magazine", (StringComparison)5) >= 0 || text.IndexOf(" ammo mag", (StringComparison)5) >= 0 || IsMagazineBarcode(barcode);
}
private static bool IsMagazineBarcode(string barcode)
{
return barcode.IndexOf(".Magazine.", (StringComparison)5) >= 0 || barcode.EndsWith(".Magazine", (StringComparison)5);
}
private static void AddNormalSpawn(Identity owner, string itemName)
{
if (string.IsNullOrEmpty(owner.PlatformId))
{
Count("owner unresolved");
}
Count("logged");
string text = (owner.Departed ? (owner.Name + " (left)") : owner.Name);
float realtimeSinceStartup = Time.realtimeSinceStartup;
PlayerHistory orCreateHistory = GetOrCreateHistory(owner);
FeedItem feedItem = ((orCreateHistory.Entries.Count > 0) ? orCreateHistory.Entries[0] : null);
if (feedItem != null && feedItem.Item == itemName && realtimeSinceStartup - feedItem.LastAt <= 4f)
{
feedItem.Count++;
feedItem.LastAt = realtimeSinceStartup;
SpawnFeed.Remove(feedItem);
SpawnFeed.Insert(0, feedItem);
}
else
{
feedItem = new FeedItem
{
PlatformId = owner.PlatformId,
Owner = text,
Item = itemName,
Time = System.DateTime.Now.ToString("HH:mm:ss"),
LastAt = realtimeSinceStartup
};
SpawnFeed.Insert(0, feedItem);
Trim<FeedItem>(SpawnFeed);
InsertNewest(orCreateHistory.Entries, feedItem);
}
MarkDirty(orCreateHistory);
MelonLogger.Msg($"[{System.DateTime.Now:HH:mm:ss}] {text}: {itemName}");
}
private static void AddAlert(SpawnSnapshot snapshot, EntitySource source, string itemName)
{
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
Count("alerts");
string text = snapshot.TransportSender?.Name ?? "Unresolved sender";
string text2 = $"[{System.DateTime.Now:HH:mm:ss}] ALERT: {text} -> {itemName} (claimed {snapshot.ClaimedOwner.Name}; source {source}; {snapshot.AlertReason})";
MelonLogger.Warning(text2);
}
private static PlayerHistory GetOrCreateHistory(Identity identity)
{
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
string text = ((!string.IsNullOrEmpty(identity.PlatformId)) ? identity.PlatformId : ("small:" + identity.SmallId));
PlayerHistory playerHistory = default(PlayerHistory);
if (!PlayerHistories.TryGetValue(text, ref playerHistory))
{
playerHistory = new PlayerHistory();
PlayerHistories.Add(text, playerHistory);
}
if (playerHistory.Page == null && _playersPage != null)
{
playerHistory.Page = _playersPage.CreatePage(identity.Name, Color.white, 0, true);
}
if (playerHistory.Page != null)
{
playerHistory.Page.Name = identity.Name;
}
return playerHistory;
}
private static void RefreshHistory(PlayerHistory history)
{
RenderItems(history.Page, history.Entries);
}
private static void ClearLogs()
{
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
SpawnFeed.Clear();
DirtyHistories.Clear();
_spawnPageDirty = false;
Enumerator<string, PlayerHistory> enumerator = PlayerHistories.Values.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
PlayerHistory current = enumerator.Current;
current.Entries.Clear();
RefreshHistory(current);
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
RefreshSpawnPage();
}
private static void RefreshSpawnPage()
{
RenderItems(_spawnPage, SpawnFeed);
}
private static void RenderItems(Page page, List<FeedItem> items)
{
//IL_0057: 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_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Expected O, but got Unknown
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: Expected O, but got Unknown
if (page == null)
{
return;
}
page.RemoveAll();
if (items.Count == 0)
{
Color gray = Color.gray;
object obj = <>c.<>9__61_0;
if (obj == null)
{
Action val = delegate
{
};
<>c.<>9__61_0 = val;
obj = (object)val;
}
page.CreateFunction("No spawns logged yet", gray, (Action)obj);
return;
}
Enumerator<FeedItem> enumerator = items.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
FeedItem current = enumerator.Current;
string text = current.Text;
Color white = Color.white;
object obj2 = <>c.<>9__61_1;
if (obj2 == null)
{
Action val2 = delegate
{
};
<>c.<>9__61_1 = val2;
obj2 = (object)val2;
}
page.CreateFunction(text, white, (Action)obj2);
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
}
private static void InsertNewest<T>(List<T> entries, T item)
{
entries.Insert(0, item);
Trim<T>(entries);
}
private static void Trim<T>(List<T> entries)
{
if (entries.Count > 30)
{
entries.RemoveRange(30, entries.Count - 30);
}
}
}
internal static class FusionIds
{
private static readonly PropertyInfo PlayerPlatformId = AccessTools.Property(typeof(PlayerID), "PlatformID");
private static readonly PropertyInfo MessagePlatformId = AccessTools.Property(typeof(ReceivedMessage), "PlatformID");
[ThreadStatic]
internal static string CurrentEosPeer;
public static string Of(PlayerID player)
{
return (player == null || PlayerPlatformId == (PropertyInfo)null) ? null : Normalize(PlayerPlatformId.GetValue((object)player));
}
public static string Of(ReceivedMessage message)
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
string text = ((MessagePlatformId == (PropertyInfo)null) ? null : Normalize(MessagePlatformId.GetValue((object)message)));
return text ?? CurrentEosPeer;
}
public static PlayerID Find(string platformId)
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
if (string.IsNullOrEmpty(platformId))
{
return null;
}
Enumerator<PlayerID> enumerator = PlayerIDManager.PlayerIDs.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
PlayerID current = enumerator.Current;
if (string.Equals(Of(current), platformId, (StringComparison)4))
{
return current;
}
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
return null;
}
private static string Normalize(object value)
{
if (value != null)
{
if (!(value is string text))
{
if (value is ulong num)
{
return (num == 0L) ? null : num.ToString();
}
return value.ToString();
}
return (text.Length == 0) ? null : text;
}
return null;
}
}
internal static class EosPeerCapture
{
public static void TryApply(Harmony harmony)
{
//IL_0045: 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_0067: Expected O, but got Unknown
//IL_0067: Expected O, but got Unknown
System.Type type = AccessTools.TypeByName("LabFusion.Network.EOSP2PReceiver");
MethodInfo val = ((type == (System.Type)null) ? null : AccessTools.Method(type, "ProcessPacket", (System.Type[])null, (System.Type[])null));
if (!(val == (MethodInfo)null))
{
harmony.Patch((MethodBase)(object)val, new HarmonyMethod(typeof(EosPeerCapture), "Prefix", (System.Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(EosPeerCapture), "Finalizer", (System.Type[])null), (HarmonyMethod)null);
MelonLogger.Msg("EOS network layer detected; attributing spawns by ProductUserId.");
}
}
private static void Prefix(object[] __args)
{
FusionIds.CurrentEosPeer = ((__args.Length <= 2) ? null : __args[2]?.ToString());
}
private static System.Exception Finalizer(System.Exception __exception)
{
FusionIds.CurrentEosPeer = null;
return __exception;
}
}
[HarmonyPatch(typeof(SpawnResponseMessage), "OnHandleMessage")]
internal static class SpawnResponseReceivedPatch
{
private static void Prefix(ReceivedMessage received)
{
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
try
{
SpawnLoggerMod.CaptureResponse(((ReceivedMessage)(ref received)).ReadData<SpawnResponseData>(), received);
}
catch (System.Exception ex)
{
MelonLogger.Warning("Spawn Logger could not inspect a response: " + ex.Message);
}
}
}
[HarmonyPatch(typeof(SpawnResponseMessage), "OnSpawnFinished")]
internal static class SpawnFinishedPatch
{
private static void Postfix(SpawnResponseData data, Poolee poolee)
{
try
{
SpawnLoggerMod.CompleteSpawn(data, poolee);
}
catch (System.Exception ex)
{
MelonLogger.Error($"Spawn Logger failed to inspect a spawn: {ex}");
}
}
}
}