Decompiled source of MimesisItemSpawner v1.0.1

MimesisItemSpawner.dll

Decompiled 9 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using MimesisItemSpawner;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "MimesisItemSpawner", "1.0.0", "corbyn.barker", null)]
[assembly: MelonGame("ReLUGames", "MIMESIS")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MimesisItemSpawner")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("MimesisItemSpawner")]
[assembly: AssemblyTitle("MimesisItemSpawner")]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 MimesisItemSpawner
{
	public class Core : MelonMod
	{
		private MelonPreferences_Entry<string> _toggleKeyPref;

		private MelonPreferences_Entry<string> _reasonPref;

		private MelonPreferences_Entry<int> _amountPref;

		private bool _open;

		private bool _itemsLoaded;

		private string _search = "";

		private string _amountText = "1";

		private string _status = "Press the toggle key in-game to open.";

		private Vector2 _scroll;

		private Rect _window = new Rect(40f, 40f, 480f, 600f);

		private readonly List<GameBridge.ItemEntry> _allItems = new List<GameBridge.ItemEntry>();

		private readonly List<GameBridge.ItemEntry> _filtered = new List<GameBridge.ItemEntry>();

		private string _lastSearch;

		private int _pendingSpawnId = -1;

		private const int MaxRows = 300;

		private const int WindowId = 1296651088;

		public override void OnInitializeMelon()
		{
			MelonPreferences_Category val = MelonPreferences.CreateCategory("MimesisItemSpawner");
			_toggleKeyPref = val.CreateEntry<string>("HotKey", "F6", "Toggle key (Input System key name, e.g. F6, Backquote, F1..F12)", (string)null, false, false, (ValueValidator)null, (string)null);
			_reasonPref = val.CreateEntry<string>("ReasonOfSpawn", "Admin", "Spawn reason (None/Spawn/Admin/ItemSpawn/...)", (string)null, false, false, (ValueValidator)null, (string)null);
			_amountPref = val.CreateEntry<int>("DefaultAmount", 1, "Default stack amount", (string)null, false, false, (ValueValidator)null, (string)null);
			_amountText = _amountPref.Value.ToString();
			MelonLogger.Msg("MimesisItemSpawner loaded. Toggle key: " + _toggleKeyPref.Value);
		}

		public override void OnUpdate()
		{
			if (GameBridge.WasKeyPressed(_toggleKeyPref.Value))
			{
				_open = !_open;
				if (_open && !_itemsLoaded)
				{
					TryLoadItems();
				}
			}
			if (_pendingSpawnId >= 0)
			{
				int pendingSpawnId = _pendingSpawnId;
				_pendingSpawnId = -1;
				DoSpawn(pendingSpawnId);
			}
		}

		public override void OnGUI()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (_open)
			{
				_window = GUI.Window(1296651088, _window, new WindowFunction(DrawWindow), "Mimesis Item Spawner");
			}
		}

		private void TryLoadItems()
		{
			if (!GameBridge.EnsureReady(out var error))
			{
				_status = "Cannot load items: " + error;
				return;
			}
			try
			{
				_allItems.Clear();
				_allItems.AddRange(GameBridge.GetAllItems());
				_itemsLoaded = _allItems.Count > 0;
				_lastSearch = null;
				_status = (_itemsLoaded ? $"Loaded {_allItems.Count} items." : "Item table not ready yet (enter a session, then Refresh).");
			}
			catch (Exception ex)
			{
				_status = "Error loading items: " + ex.Message;
				MelonLogger.Error((object)ex);
			}
		}

		private void RebuildFilter()
		{
			if (_lastSearch == _search)
			{
				return;
			}
			_lastSearch = _search;
			_filtered.Clear();
			string text = _search?.Trim();
			bool flag = string.IsNullOrEmpty(text);
			string value = (flag ? null : text.ToLowerInvariant());
			foreach (GameBridge.ItemEntry allItem in _allItems)
			{
				if (flag || allItem.Id.ToString().Contains(text) || (allItem.Name != null && allItem.Name.ToLowerInvariant().Contains(value)) || (allItem.Display != null && allItem.Display.ToLowerInvariant().Contains(value)))
				{
					_filtered.Add(allItem);
					if (_filtered.Count >= 300)
					{
						break;
					}
				}
			}
		}

		private void DrawWindow(int id)
		{
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Space(4f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Search:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) });
			_search = GUILayout.TextField(_search ?? "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(250f) });
			if (GUILayout.Button("Refresh", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }))
			{
				TryLoadItems();
			}
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Amount:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) });
			_amountText = GUILayout.TextField(_amountText ?? "1", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) });
			GUILayout.FlexibleSpace();
			GUILayout.Label(_itemsLoaded ? $"{_allItems.Count} items" : "not loaded", Array.Empty<GUILayoutOption>());
			GUILayout.EndHorizontal();
			GUILayout.Space(4f);
			GUILayout.Label(_status, Array.Empty<GUILayoutOption>());
			GUILayout.Space(2f);
			RebuildFilter();
			if (_filtered.Count >= 300)
			{
				GUILayout.Label($"Showing first {300} matches — refine search.", Array.Empty<GUILayoutOption>());
			}
			_scroll = GUILayout.BeginScrollView(_scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(440f) });
			foreach (GameBridge.ItemEntry item in _filtered)
			{
				if (GUILayout.Button($"[{item.Id}] {item.Display}", Array.Empty<GUILayoutOption>()))
				{
					_pendingSpawnId = item.Id;
					_status = $"Spawning [{item.Id}] {item.Display} ...";
				}
			}
			if (_filtered.Count == 0)
			{
				GUILayout.Label(_itemsLoaded ? "No matches." : "Open a session and press Refresh.", Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndScrollView();
			GUILayout.Space(4f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Close", Array.Empty<GUILayoutOption>()))
			{
				_open = false;
			}
			GUILayout.EndHorizontal();
			GUI.DragWindow();
		}

		private void DoSpawn(int id)
		{
			int result = 1;
			int.TryParse(_amountText, out result);
			if (result < 1)
			{
				result = 1;
			}
			if (!GameBridge.EnsureReady(out var error))
			{
				_status = "Not ready: " + error;
				return;
			}
			string message;
			bool flag = GameBridge.SpawnItem(id, result, _reasonPref.Value, out message);
			_status = message ?? (flag ? "Spawned." : "Spawn failed.");
			if (flag)
			{
				MelonLogger.Msg(message);
			}
			else
			{
				MelonLogger.Warning(message);
			}
		}
	}
	internal static class GameBridge
	{
		public struct ItemEntry
		{
			public int Id;

			public string Name;

			public string Display;
		}

		private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static Assembly _gameAsm;

		private static Type _hubType;

		private static Type _ivroomType;

		private static Type _posWithRotType;

		private static Type _reasonType;

		private static bool _inputResolved;

		private static Type _keyboardType;

		private static Type _keyEnumType;

		private static PropertyInfo _keyboardCurrent;

		private static MethodInfo _keyboardIndexer;

		private static void ResolveInput()
		{
			if (_inputResolved)
			{
				return;
			}
			_inputResolved = true;
			Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "Unity.InputSystem");
			if (!(assembly == null))
			{
				_keyboardType = assembly.GetType("UnityEngine.InputSystem.Keyboard");
				_keyEnumType = assembly.GetType("UnityEngine.InputSystem.Key");
				if (!(_keyboardType == null) && !(_keyEnumType == null))
				{
					_keyboardCurrent = _keyboardType.GetProperty("current", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					_keyboardIndexer = _keyboardType.GetMethod("get_Item", new Type[1] { _keyEnumType });
				}
			}
		}

		public static bool WasKeyPressed(string keyName)
		{
			ResolveInput();
			if (_keyboardCurrent == null || _keyboardIndexer == null)
			{
				return false;
			}
			try
			{
				object value = _keyboardCurrent.GetValue(null);
				if (value == null)
				{
					return false;
				}
				object obj;
				try
				{
					obj = Enum.Parse(_keyEnumType, keyName, ignoreCase: true);
				}
				catch
				{
					obj = Enum.Parse(_keyEnumType, "F6", ignoreCase: true);
				}
				object obj3 = _keyboardIndexer.Invoke(value, new object[1] { obj });
				if (obj3 == null)
				{
					return false;
				}
				PropertyInfo property = obj3.GetType().GetProperty("wasPressedThisFrame", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				return property != null && (bool)property.GetValue(obj3);
			}
			catch
			{
				return false;
			}
		}

		public static bool EnsureReady(out string error)
		{
			error = null;
			if (_gameAsm == null)
			{
				_gameAsm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "Assembly-CSharp");
				if (_gameAsm == null)
				{
					error = "Assembly-CSharp not loaded yet.";
					return false;
				}
			}
			if ((object)_hubType == null)
			{
				_hubType = _gameAsm.GetType("Hub");
			}
			if ((object)_ivroomType == null)
			{
				_ivroomType = _gameAsm.GetType("IVroom");
			}
			if ((object)_posWithRotType == null)
			{
				_posWithRotType = _gameAsm.GetType("ReluProtocol.PosWithRot");
			}
			if ((object)_reasonType == null)
			{
				_reasonType = _gameAsm.GetType("ReluProtocol.Enum.ReasonOfSpawn");
			}
			if (_hubType == null)
			{
				error = "Hub type not found.";
				return false;
			}
			if (_ivroomType == null)
			{
				error = "IVroom type not found.";
				return false;
			}
			if (_posWithRotType == null)
			{
				error = "PosWithRot type not found.";
				return false;
			}
			if (_reasonType == null)
			{
				error = "ReasonOfSpawn type not found.";
				return false;
			}
			return true;
		}

		private static object GetHub()
		{
			return _hubType.GetField("s", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null);
		}

		private static object GetField(object obj, string name)
		{
			if (obj == null)
			{
				return null;
			}
			Type type = obj.GetType();
			while (type != null)
			{
				FieldInfo field = type.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field.GetValue(obj);
				}
				type = type.BaseType;
			}
			return null;
		}

		private static object GetFieldOn(Type declaring, object obj, string name)
		{
			return declaring.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
		}

		private static object GetProp(object obj, string name)
		{
			if (obj == null)
			{
				return null;
			}
			Type type = obj.GetType();
			while (type != null)
			{
				PropertyInfo property = type.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					return property.GetValue(obj);
				}
				type = type.BaseType;
			}
			return null;
		}

		private static object GetVWorld()
		{
			return GetField(GetHub(), "<vworld>k__BackingField");
		}

		private static int GetMyActorId(out object hub, out object pdata)
		{
			hub = GetHub();
			pdata = GetField(hub, "pdata");
			if (pdata == null)
			{
				return 0;
			}
			FieldInfo field = pdata.GetType().GetField("MyActorID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (!(field != null))
			{
				return 0;
			}
			return (int)field.GetValue(pdata);
		}

		private static bool GetLocalRoomAndPlayer(out object room, out object player, out string error)
		{
			room = null;
			player = null;
			error = null;
			object vWorld = GetVWorld();
			if (vWorld == null)
			{
				error = "Not in game yet (no VWorld). Join/host a session first.";
				return false;
			}
			object field = GetField(vWorld, "_vRoomManager");
			if (field == null)
			{
				error = "No room manager (host the session to spawn).";
				return false;
			}
			if (!(GetField(field, "_vrooms") is IDictionary { Count: not 0 } dictionary))
			{
				error = "No active rooms found.";
				return false;
			}
			object hub;
			object pdata;
			int myActorId = GetMyActorId(out hub, out pdata);
			if (myActorId == 0)
			{
				error = "Local actor id is 0 (not spawned in a room yet).";
				return false;
			}
			foreach (object value in dictionary.Values)
			{
				object fieldOn = GetFieldOn(_ivroomType, value, "_vPlayerDict");
				if (fieldOn == null)
				{
					continue;
				}
				MethodInfo method = fieldOn.GetType().GetMethod("FindActor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (!(method == null))
				{
					object obj = method.Invoke(fieldOn, new object[1] { myActorId });
					if (obj != null)
					{
						room = value;
						player = obj;
						return true;
					}
				}
			}
			IEnumerator enumerator = dictionary.Values.GetEnumerator();
			try
			{
				if (enumerator.MoveNext())
				{
					object current2 = enumerator.Current;
					room = current2;
				}
			}
			finally
			{
				IDisposable disposable = enumerator as IDisposable;
				if (disposable != null)
				{
					disposable.Dispose();
				}
			}
			if (room == null)
			{
				error = "Could not resolve a room for the local player.";
				return false;
			}
			return true;
		}

		public static List<ItemEntry> GetAllItems()
		{
			List<ItemEntry> list = new List<ItemEntry>();
			object hub = GetHub();
			object field = GetField(hub, "<dataman>k__BackingField");
			if (field == null)
			{
				return list;
			}
			object prop = GetProp(field, "ExcelDataManager");
			if (prop == null)
			{
				return list;
			}
			if (!(GetProp(prop, "ItemInfoDict") is IEnumerable enumerable))
			{
				return list;
			}
			object field2 = GetField(hub, "pdata");
			MethodInfo methodInfo = field2?.GetType().GetMethod("GetItemDisplayName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			MethodInfo method = _hubType.GetMethod("GetL10NText", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (object item in enumerable)
			{
				Type type = item.GetType();
				object obj = type.GetProperty("Key")?.GetValue(item);
				object obj2 = type.GetProperty("Value")?.GetValue(item);
				if (obj == null || obj2 == null)
				{
					continue;
				}
				int num = (int)obj;
				string text = (obj2.GetType().GetField("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj2) as string) ?? num.ToString();
				string text2 = text;
				try
				{
					if (methodInfo != null)
					{
						string text3 = methodInfo.Invoke(field2, new object[2] { num, text }) as string;
						if (!string.IsNullOrEmpty(text3))
						{
							text2 = text3;
						}
					}
					if (method != null)
					{
						string text4 = method.Invoke(null, new object[1] { text2 }) as string;
						if (!string.IsNullOrEmpty(text4))
						{
							text2 = text4;
						}
					}
				}
				catch
				{
				}
				list.Add(new ItemEntry
				{
					Id = num,
					Name = text,
					Display = text2
				});
			}
			list.Sort((ItemEntry a, ItemEntry b) => a.Id.CompareTo(b.Id));
			return list;
		}

		public static bool SpawnItem(int masterId, int qty, string reasonName, out string message)
		{
			message = null;
			try
			{
				if (!GetLocalRoomAndPlayer(out var room, out var player, out var error))
				{
					message = error;
					return false;
				}
				MethodInfo method = _ivroomType.GetMethod("GetNewItemElement", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (method == null)
				{
					message = "GetNewItemElement not found.";
					return false;
				}
				object obj = method.Invoke(room, new object[6]
				{
					masterId,
					false,
					Math.Max(1, qty),
					0,
					0,
					0
				});
				if (obj == null)
				{
					message = $"Item {masterId} is invalid (GetNewItemElement returned null).";
					return false;
				}
				object obj2 = Activator.CreateInstance(_posWithRotType);
				bool flag = false;
				if (player != null)
				{
					object prop = GetProp(player, "PositionVector");
					if (prop != null)
					{
						_posWithRotType.GetMethod("set_pos", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(obj2, new object[1] { prop });
					}
					if (GetProp(player, "IsIndoor") is bool flag2)
					{
						flag = flag2;
					}
				}
				object obj3;
				try
				{
					obj3 = Enum.Parse(_reasonType, reasonName, ignoreCase: true);
				}
				catch
				{
					obj3 = Enum.ToObject(_reasonType, 8);
				}
				MethodInfo method2 = _ivroomType.GetMethod("SpawnLootingObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (method2 == null)
				{
					message = "SpawnLootingObject not found.";
					return false;
				}
				int num = ((method2.Invoke(room, new object[9] { obj, obj2, flag, obj3, 0, 0, 0L, false, false }) is int num2) ? num2 : 0);
				if (num == 0)
				{
					message = $"Spawn rejected by game for item {masterId}.";
					return false;
				}
				message = $"Spawned item {masterId} x{Math.Max(1, qty)} (objId {num}).";
				return true;
			}
			catch (Exception ex)
			{
				message = "Spawn error: " + ex.Message;
				MelonLogger.Error((object)ex);
				return false;
			}
		}
	}
}