using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.Json; using BoneLib; using BoneLib.BoneMenu; using Il2CppSLZ.Marrow.Data; using Il2CppSLZ.Marrow.Pool; using LabFusion.Entities; using LabFusion.Marrow.Pool; using LabFusion.RPC; using LabFusion.Scene; using MelonLoader; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using SpawnLab; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(SpawnLabMod), "SpawnLab", "1.0.0", "ChappieStudios", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("SpawnLab")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SpawnLab")] [assembly: AssemblyTitle("SpawnLab")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SpawnLab { public sealed class SpawnLabMod : MelonMod { private readonly struct PalletFile { public string Path { get; } public bool IsDownloaded { get; } public PalletFile(string path, bool isDownloaded) { Path = path; IsDownloaded = isDownloaded; } } private readonly struct SpawnEntry { public string Title { get; } public string Barcode { get; } public string SourceName { get; } public bool IsDownloaded { get; } public SpawnCategory Category { get; } public SpawnEntry(string title, string barcode, string sourceName, bool isDownloaded, SpawnCategory category) { Title = title; Barcode = barcode; SourceName = sourceName; IsDownloaded = isDownloaded; Category = category; } } private enum SpawnCategory { Guns, Melee, NPCs, Vehicles, Gadgets, Money, Props, Misc } [CompilerGenerated] private sealed class d__12 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private PalletFile <>2__current; private int <>l__initialThreadId; private IEnumerator <>7__wrap1; PalletFile IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__12(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { switch (<>1__state) { case -3: case 1: try { } finally { <>m__Finally1(); } break; case -4: case 2: try { } finally { <>m__Finally2(); } break; } <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { try { string path2; switch (<>1__state) { default: return false; case 0: { <>1__state = -1; string path = Path.Combine(Application.persistentDataPath, "Mods"); if (Directory.Exists(path)) { <>7__wrap1 = Directory.EnumerateFiles(path, "*.pallet.json", SearchOption.AllDirectories).GetEnumerator(); <>1__state = -3; goto IL_0091; } goto IL_00ab; } case 1: <>1__state = -3; goto IL_0091; case 2: { <>1__state = -4; goto IL_011c; } IL_00ab: path2 = Path.Combine(MelonEnvironment.UnityGameDataDirectory, "StreamingAssets", "aa", "StandaloneWindows64"); if (!Directory.Exists(path2)) { break; } <>7__wrap1 = Directory.EnumerateFiles(path2, "*.pallet.json", SearchOption.TopDirectoryOnly).GetEnumerator(); <>1__state = -4; goto IL_011c; IL_0091: if (<>7__wrap1.MoveNext()) { string current = <>7__wrap1.Current; <>2__current = new PalletFile(current, isDownloaded: true); <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap1 = null; goto IL_00ab; IL_011c: if (<>7__wrap1.MoveNext()) { string current2 = <>7__wrap1.Current; <>2__current = new PalletFile(current2, isDownloaded: false); <>1__state = 2; return true; } <>m__Finally2(); <>7__wrap1 = null; break; } return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } private void <>m__Finally2() { <>1__state = -1; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new d__12(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } private const float SpawnDistance = 1.25f; private const int MaxMenuNameLength = 48; private static readonly SpawnCategory[] CategoryOrder = new SpawnCategory[8] { SpawnCategory.Guns, SpawnCategory.Melee, SpawnCategory.NPCs, SpawnCategory.Vehicles, SpawnCategory.Gadgets, SpawnCategory.Money, SpawnCategory.Props, SpawnCategory.Misc }; private Page _rootPage; private List _entries = new List(); public override void OnInitializeMelon() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) _rootPage = Page.Root.CreatePage("SpawnLab", Color.cyan, 0, true); RebuildMenu(); ((MelonBase)this).LoggerInstance.Msg("SpawnLab loaded."); } private void RebuildMenu() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: 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_012b: Unknown result type (might be due to invalid IL or missing references) _entries = LoadSpawnEntries(); _rootPage.RemoveAll(); _rootPage.CreateFunction($"Refresh List ({_entries.Count})", Color.yellow, (Action)RebuildMenu); if (_entries.Count == 0) { _rootPage.CreateFunction("No Spawnables Found", Color.red, (Action)delegate { }); return; } Page parentPage = _rootPage.CreatePage("All Categories", Color.cyan, 0, true); AddCategoryPages(parentPage, _entries); Page parentPage2 = _rootPage.CreatePage("Downloaded Mods", Color.green, 0, true); AddSourcePages(parentPage2, _entries.Where((SpawnEntry entry) => entry.IsDownloaded)); Page parentPage3 = _rootPage.CreatePage("Base Game", Color.blue, 0, true); AddSourcePages(parentPage3, _entries.Where((SpawnEntry entry) => !entry.IsDownloaded)); } private void AddSourcePages(Page parentPage, IEnumerable entries) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) foreach (IGrouping item in from entry in entries group entry by entry.SourceName into @group orderby @group.Key select @group) { List list = item.OrderBy((SpawnEntry entry) => entry.Title).ToList(); if (list.Count != 0) { Page parentPage2 = parentPage.CreatePage(TrimMenuName($"{item.Key} ({list.Count})"), Color.white, 0, true); AddCategoryPages(parentPage2, list); } } } private void AddCategoryPages(Page parentPage, IEnumerable entries) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) List source = entries.ToList(); SpawnCategory[] categoryOrder = CategoryOrder; foreach (SpawnCategory category in categoryOrder) { List list = (from entry in source where entry.Category == category orderby entry.Title, entry.SourceName select entry).ToList(); if (list.Count == 0) { continue; } Page val = parentPage.CreatePage($"{GetCategoryName(category)} ({list.Count})", GetCategoryColor(category), 0, true); foreach (SpawnEntry item in list) { SpawnEntry capturedEntry = item; val.CreateFunction(TrimMenuName(capturedEntry.Title), Color.white, (Action)delegate { Spawn(capturedEntry); }); } } } private void Spawn(SpawnEntry entry) { //IL_005c: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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) try { Spawnable val = LocalAssetSpawner.CreateSpawnable(entry.Barcode); GetSpawnTransform(out var position, out var rotation); if (NetworkSceneManager.IsLevelNetworked) { SpawnRequestInfo val2 = default(SpawnRequestInfo); val2.Spawnable = val; val2.Position = position; val2.Rotation = rotation; val2.SpawnEffect = true; val2.SpawnSource = (EntitySource)2; NetworkAssetSpawner.Spawn(val2); } else { LocalAssetSpawner.Register(val); LocalAssetSpawner.Spawn(val, position, rotation, (Action)null); } } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Error("Could not spawn " + entry.Title + ": " + ex.Message); } } private static void GetSpawnTransform(out Vector3 position, out Quaternion rotation) { //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_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) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: 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) //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) Transform head = Player.Head; Transform val = (((Object)(object)Player.RigManager != (Object)null) ? ((Component)Player.RigManager).transform : null); Vector3 val2 = (((Object)(object)head != (Object)null) ? head.position : (((Object)(object)val != (Object)null) ? (val.position + Vector3.up * 1.5f) : Vector3.zero)); Vector3 val3 = (((Object)(object)head != (Object)null) ? head.forward : (((Object)(object)val != (Object)null) ? val.forward : Vector3.forward)); val3.y = 0f; if (((Vector3)(ref val3)).sqrMagnitude < 0.001f) { val3 = Vector3.forward; } ((Vector3)(ref val3)).Normalize(); position = val2 + val3 * 1.25f + Vector3.down * 0.12f; rotation = Quaternion.LookRotation(val3, Vector3.up); } private List LoadSpawnEntries() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (PalletFile item in FindPalletFiles()) { try { ReadPalletFile(item, dictionary); } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Warning("Could not read pallet " + item.Path + ": " + ex.Message); } } return (from entry in dictionary.Values orderby (!entry.IsDownloaded) ? 1 : 0, entry.SourceName, entry.Category, entry.Title select entry).ToList(); } [IteratorStateMachine(typeof(d__12))] private IEnumerable FindPalletFiles() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__12(-2); } private static void ReadPalletFile(PalletFile palletFile, Dictionary entries) { using JsonDocument jsonDocument = JsonDocument.Parse(File.ReadAllText(palletFile.Path)); JsonElement rootElement = jsonDocument.RootElement; if (!rootElement.TryGetProperty("objects", out var value) || value.ValueKind != JsonValueKind.Object) { return; } string palletTitle = GetPalletTitle(rootElement, value, palletFile.Path); foreach (JsonProperty item in value.EnumerateObject()) { JsonElement value2 = item.Value; if (!IsSpawnableCrate(value2)) { continue; } string @string = GetString(value2, "barcode"); if (!string.IsNullOrWhiteSpace(@string) && !entries.ContainsKey(@string)) { string text = GetString(value2, "title"); if (string.IsNullOrWhiteSpace(text)) { text = @string; } List tags = GetTags(value2); SpawnCategory category = Categorize(text, @string, tags); entries.Add(@string, new SpawnEntry(text, @string, palletTitle, palletFile.IsDownloaded, category)); } } } private static string GetPalletTitle(JsonElement root, JsonElement objects, string path) { string text = null; if (root.TryGetProperty("root", out var value) && value.TryGetProperty("ref", out var value2) && value2.ValueKind == JsonValueKind.String) { text = value2.GetString(); } if (!string.IsNullOrWhiteSpace(text) && objects.TryGetProperty(text, out var value3) && value3.TryGetProperty("title", out var value4) && value4.ValueKind == JsonValueKind.String && !string.IsNullOrWhiteSpace(value4.GetString())) { return value4.GetString(); } return Path.GetFileNameWithoutExtension(path).Replace(".pallet", string.Empty); } private static bool IsSpawnableCrate(JsonElement crate) { if (!crate.TryGetProperty("isa", out var value) || !value.TryGetProperty("type", out var value2)) { return false; } if (value2.ValueKind == JsonValueKind.String) { return value2.GetString() == "crate-spawnable#0"; } return false; } private static string GetString(JsonElement element, string propertyName) { if (element.TryGetProperty(propertyName, out var value) && value.ValueKind == JsonValueKind.String) { return value.GetString(); } return null; } private static List GetTags(JsonElement crate) { List list = new List(); if (!crate.TryGetProperty("tags", out var value) || value.ValueKind != JsonValueKind.Array) { return list; } foreach (JsonElement item in value.EnumerateArray()) { if (item.ValueKind == JsonValueKind.String) { list.Add(item.GetString()); } } return list; } private static SpawnCategory Categorize(string title, string barcode, List tags) { string text = $"{title} {barcode} {string.Join(" ", tags)}".ToLowerInvariant(); if (ContainsAny(text, "money", "dollar", "bill", "coin", "penny", "nickel", "dime", "quarter", "wallet", "credit card")) { return SpawnCategory.Money; } if (ContainsAny(text, "npc", "enemy", "ford", "nullbody", "crablet", "skeleton", "zombie", "omni", "security", "agent", "peasant", "cultist")) { return SpawnCategory.NPCs; } if (ContainsAny(text, "rifle", "pistol", "gun", "shotgun", "smg", "revolver", "launcher", "magazine", "ammo", "cartridge")) { return SpawnCategory.Guns; } if (ContainsAny(text, "sword", "knife", "blade", "axe", "bat", "crowbar", "dagger", "spear", "hammer", "wrench", "mace", "katana")) { return SpawnCategory.Melee; } if (ContainsAny(text, "vehicle", "car", "kart", "truck", "taxi", "hover", "board", "bike", "plane")) { return SpawnCategory.Vehicles; } if (ContainsAny(text, "tool", "gadget", "utility", "nimbus", "spawn gun", "constrainer", "dev", "capsule", "button")) { return SpawnCategory.Gadgets; } if (ContainsAny(text, "prop", "crate", "box", "barrel", "table", "chair", "door", "ball", "target")) { return SpawnCategory.Props; } return SpawnCategory.Misc; } private static bool ContainsAny(string text, params string[] values) { foreach (string value in values) { if (text.Contains(value)) { return true; } } return false; } private static string GetCategoryName(SpawnCategory category) { return category switch { SpawnCategory.Guns => "Guns", SpawnCategory.Melee => "Melee", SpawnCategory.NPCs => "NPCs", SpawnCategory.Vehicles => "Vehicles", SpawnCategory.Gadgets => "Gadgets", SpawnCategory.Money => "Money", SpawnCategory.Props => "Props", _ => "Misc", }; } private static Color GetCategoryColor(SpawnCategory category) { //IL_0024: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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: Unknown result type (might be due to invalid IL or missing references) return (Color)(category switch { SpawnCategory.Guns => Color.red, SpawnCategory.Melee => new Color(1f, 0.45f, 0f), SpawnCategory.NPCs => Color.magenta, SpawnCategory.Vehicles => Color.cyan, SpawnCategory.Gadgets => Color.yellow, SpawnCategory.Money => Color.green, SpawnCategory.Props => Color.white, _ => Color.gray, }); } private static string TrimMenuName(string value) { if (string.IsNullOrWhiteSpace(value)) { return "Unnamed"; } value = value.Replace("\r", " ").Replace("\n", " ").Trim(); if (value.Length > 48) { return value.Substring(0, 45) + "..."; } return value; } } }