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 <FindPalletFiles>d__12 : IEnumerable<PalletFile>, IEnumerable, IEnumerator<PalletFile>, IEnumerator, IDisposable
{
private int <>1__state;
private PalletFile <>2__current;
private int <>l__initialThreadId;
private IEnumerator<string> <>7__wrap1;
PalletFile IEnumerator<PalletFile>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <FindPalletFiles>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<PalletFile> IEnumerable<PalletFile>.GetEnumerator()
{
if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
{
<>1__state = 0;
return this;
}
return new <FindPalletFiles>d__12(0);
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<PalletFile>)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<SpawnEntry> _entries = new List<SpawnEntry>();
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<SpawnEntry> entries)
{
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
foreach (IGrouping<string, SpawnEntry> item in from entry in entries
group entry by entry.SourceName into @group
orderby @group.Key
select @group)
{
List<SpawnEntry> 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<SpawnEntry> 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<SpawnEntry> source = entries.ToList();
SpawnCategory[] categoryOrder = CategoryOrder;
foreach (SpawnCategory category in categoryOrder)
{
List<SpawnEntry> 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<Poolee>)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<SpawnEntry> LoadSpawnEntries()
{
Dictionary<string, SpawnEntry> dictionary = new Dictionary<string, SpawnEntry>(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(<FindPalletFiles>d__12))]
private IEnumerable<PalletFile> FindPalletFiles()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <FindPalletFiles>d__12(-2);
}
private static void ReadPalletFile(PalletFile palletFile, Dictionary<string, SpawnEntry> 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<string> 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<string> GetTags(JsonElement crate)
{
List<string> list = new List<string>();
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<string> 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;
}
}
}