Decompiled source of SilkRoomUtils v0.0.23

BepInEx/plugins/SilkRoomUtils/SilkRoomUtils.dll

Decompiled a month ago
using System;
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.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SilkRoomUtils")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+275c2df1d9c5d7fdc7136ab8998caeab3ee513b8")]
[assembly: AssemblyProduct("SilkRoomUtils")]
[assembly: AssemblyTitle("SilkRoomUtils")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace SilksongRooms._1
{
	public interface IRoomIntegrator
	{
		void RegisterRooms(IReadOnlyList<LoadedRoom> rooms);

		void OnSceneLoaded(Scene scene, LoadSceneMode mode, IReadOnlyList<LoadedRoom> rooms);
	}
	[Serializable]
	public class RoomDefinition
	{
		public string id = string.Empty;

		public string bundle = string.Empty;

		public string prefab = string.Empty;

		public Vector3 entryPoint;

		public string[] tags = Array.Empty<string>();

		public Connection[] connections = Array.Empty<Connection>();
	}
	[Serializable]
	public class Connection
	{
		public string toRoomId = string.Empty;

		public string doorName = string.Empty;
	}
	public class LoadedRoom
	{
		public RoomDefinition Definition = new RoomDefinition();

		public AssetBundle? Bundle;

		public GameObject? Prefab;

		public string SourceJsonPath = string.Empty;
	}
	public sealed class RoomLoader
	{
		private readonly ManualLogSource _log;

		public List<LoadedRoom> LoadedRooms { get; } = new List<LoadedRoom>();


		public RoomLoader(ManualLogSource log)
		{
			_log = log;
		}

		public void LoadAll(string roomsFolder)
		{
			LoadedRooms.Clear();
			AddFromFolder(roomsFolder);
		}

		public List<LoadedRoom> AddFromFolder(string roomsFolder)
		{
			List<LoadedRoom> list = new List<LoadedRoom>();
			if (!Directory.Exists(roomsFolder))
			{
				_log.LogWarning((object)("Rooms folder does not exist: " + roomsFolder));
				return list;
			}
			foreach (string item in Directory.EnumerateFiles(roomsFolder, "*.room.json", SearchOption.AllDirectories))
			{
				LoadedRoom loaded = TryLoadFromJsonFile(item, roomsFolder);
				if (loaded != null)
				{
					int num = LoadedRooms.FindIndex((LoadedRoom r) => string.Equals(r.Definition.id, loaded.Definition.id, StringComparison.Ordinal));
					if (num >= 0)
					{
						_log.LogWarning((object)("Room id '" + loaded.Definition.id + "' already loaded; replacing with definition from '" + item + "'."));
						LoadedRooms[num] = loaded;
					}
					else
					{
						LoadedRooms.Add(loaded);
					}
					list.Add(loaded);
				}
			}
			return list;
		}

		public LoadedRoom? AddFromJsonFile(string jsonPath, string? resolveRoot = null)
		{
			string resolveRoot2 = resolveRoot ?? Path.GetDirectoryName(jsonPath) ?? Environment.CurrentDirectory;
			LoadedRoom loaded = TryLoadFromJsonFile(jsonPath, resolveRoot2);
			if (loaded == null)
			{
				return null;
			}
			int num = LoadedRooms.FindIndex((LoadedRoom r) => string.Equals(r.Definition.id, loaded.Definition.id, StringComparison.Ordinal));
			if (num >= 0)
			{
				_log.LogWarning((object)("Room id '" + loaded.Definition.id + "' already loaded; replacing with definition from '" + jsonPath + "'."));
				LoadedRooms[num] = loaded;
			}
			else
			{
				LoadedRooms.Add(loaded);
			}
			return loaded;
		}

		public bool TryGetRoom(string id, out LoadedRoom? room)
		{
			string id2 = id;
			room = LoadedRooms.FirstOrDefault((LoadedRoom r) => string.Equals(r.Definition.id, id2, StringComparison.Ordinal));
			return room != null;
		}

		private LoadedRoom? TryLoadFromJsonFile(string jsonPath, string resolveRoot)
		{
			try
			{
				RoomDefinition roomDefinition = JsonUtility.FromJson<RoomDefinition>(File.ReadAllText(jsonPath));
				if (roomDefinition == null || string.IsNullOrWhiteSpace(roomDefinition.id))
				{
					_log.LogWarning((object)("Skipping invalid room: " + jsonPath));
					return null;
				}
				LoadedRoom loadedRoom = new LoadedRoom
				{
					Definition = roomDefinition,
					SourceJsonPath = jsonPath
				};
				if (!string.IsNullOrWhiteSpace(roomDefinition.bundle))
				{
					string text = Path.Combine(resolveRoot, roomDefinition.bundle.Replace('/', Path.DirectorySeparatorChar));
					if (File.Exists(text))
					{
						loadedRoom.Bundle = AssetBundle.LoadFromFile(text);
						if ((Object)(object)loadedRoom.Bundle == (Object)null)
						{
							_log.LogError((object)("Failed to load AssetBundle for room '" + roomDefinition.id + "' at '" + text + "'."));
						}
						else
						{
							if (!string.IsNullOrWhiteSpace(roomDefinition.prefab))
							{
								loadedRoom.Prefab = loadedRoom.Bundle.LoadAsset<GameObject>(roomDefinition.prefab);
								if ((Object)(object)loadedRoom.Prefab == (Object)null)
								{
									_log.LogWarning((object)("Prefab '" + roomDefinition.prefab + "' not found in bundle for room '" + roomDefinition.id + "'."));
								}
							}
							if ((Object)(object)loadedRoom.Prefab == (Object)null)
							{
								string[] allAssetNames = loadedRoom.Bundle.GetAllAssetNames();
								foreach (string text2 in allAssetNames)
								{
									GameObject val = loadedRoom.Bundle.LoadAsset<GameObject>(text2);
									if ((Object)(object)val != (Object)null)
									{
										loadedRoom.Prefab = val;
										break;
									}
								}
								if ((Object)(object)loadedRoom.Prefab == (Object)null)
								{
									_log.LogWarning((object)("No GameObject prefab found in bundle for room '" + roomDefinition.id + "'."));
								}
							}
						}
					}
					else
					{
						_log.LogError((object)("Bundle path not found for room '" + roomDefinition.id + "': " + text));
					}
				}
				_log.LogDebug((object)("Loaded definition for room '" + roomDefinition.id + "'."));
				return loadedRoom;
			}
			catch (Exception arg)
			{
				_log.LogError((object)$"Error loading room JSON '{jsonPath}': {arg}");
				return null;
			}
		}
	}
	public static class RoomsApi
	{
		private static readonly List<Action<SilksongRooms__1Plugin>> _pending = new List<Action<SilksongRooms__1Plugin>>();

		public static event Action<IReadOnlyList<LoadedRoom>>? RoomsAdded;

		internal static void Bind(SilksongRooms__1Plugin plugin)
		{
			if (_pending.Count <= 0)
			{
				return;
			}
			Action<SilksongRooms__1Plugin>[] array = _pending.ToArray();
			foreach (Action<SilksongRooms__1Plugin> action in array)
			{
				try
				{
					action(plugin);
				}
				catch (Exception)
				{
				}
			}
			_pending.Clear();
		}

		public static void RegisterRoomsFolder(string folder)
		{
			string folder2 = folder;
			SilksongRooms__1Plugin instanceOrNull = SilksongRooms__1Plugin.InstanceOrNull;
			if (instanceOrNull != null)
			{
				Do(instanceOrNull);
			}
			else
			{
				_pending.Add(Do);
			}
			void Do(SilksongRooms__1Plugin p)
			{
				IReadOnlyList<LoadedRoom> obj = p.RegisterRoomsFolder(folder2);
				RoomsApi.RoomsAdded?.Invoke(obj);
			}
		}

		public static void RegisterRoomJson(string jsonFilePath, string? resolveRoot = null)
		{
			string jsonFilePath2 = jsonFilePath;
			string resolveRoot2 = resolveRoot;
			SilksongRooms__1Plugin instanceOrNull = SilksongRooms__1Plugin.InstanceOrNull;
			if (instanceOrNull != null)
			{
				Do(instanceOrNull);
			}
			else
			{
				_pending.Add(Do);
			}
			void Do(SilksongRooms__1Plugin p)
			{
				LoadedRoom loadedRoom = p.RegisterRoomJson(jsonFilePath2, resolveRoot2);
				if (loadedRoom != null)
				{
					RoomsApi.RoomsAdded?.Invoke(new LoadedRoom[1] { loadedRoom });
				}
			}
		}

		public static string GetDefaultRoomsFolder(string pluginName)
		{
			return Path.Combine(Paths.PluginPath, pluginName, "Rooms");
		}
	}
	public sealed class SilksongRoomIntegrator : IRoomIntegrator
	{
		private readonly ManualLogSource _log;

		public SilksongRoomIntegrator(ManualLogSource log)
		{
			_log = log;
		}

		public void RegisterRooms(IReadOnlyList<LoadedRoom> rooms)
		{
			_log.LogInfo((object)$"Registering {rooms.Count} loaded room(s).");
			foreach (LoadedRoom room in rooms)
			{
				_log.LogInfo((object)("Room: " + room.Definition.id + " | Bundle: " + (Object.op_Implicit((Object)(object)room.Bundle) ? ((Object)room.Bundle).name : "(none)") + " | Prefab: " + (Object.op_Implicit((Object)(object)room.Prefab) ? ((Object)room.Prefab).name : "(none)")));
			}
		}

		public void OnSceneLoaded(Scene scene, LoadSceneMode mode, IReadOnlyList<LoadedRoom> rooms)
		{
			//IL_0013: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (GameObject)(((object)GameObject.Find("__RoomsSandbox")) ?? ((object)new GameObject("__RoomsSandbox")));
			Object.DontDestroyOnLoad((Object)(object)val);
			foreach (LoadedRoom room in rooms)
			{
				if (!((Object)(object)room.Prefab == (Object)null) && !((Object)(object)val.transform.Find(room.Definition.id) != (Object)null))
				{
					GameObject obj = Object.Instantiate<GameObject>(room.Prefab, room.Definition.entryPoint, Quaternion.identity, val.transform);
					((Object)obj).name = room.Definition.id;
					obj.SetActive(false);
					_log.LogInfo((object)("Spawned prefab for room '" + room.Definition.id + "' into sandbox (inactive). Enable it manually to preview."));
				}
			}
		}
	}
	[BepInPlugin("io.github.silksongrooms__1", "SilkRoomUtils", "0.1.0")]
	public class SilksongRooms__1Plugin : BaseUnityPlugin
	{
		private static ManualLogSource? _log;

		private RoomLoader? _loader;

		private IRoomIntegrator? _integrator;

		public const string Id = "io.github.silksongrooms__1";

		internal static ManualLogSource LogS => _log ?? (_log = Logger.CreateLogSource("SilksongRooms"));

		internal static SilksongRooms__1Plugin? InstanceOrNull { get; private set; }

		private string RoomsFolder => Path.Combine(Paths.PluginPath, ((BaseUnityPlugin)this).Info.Metadata.Name, "Rooms");

		public static string Name => "SilkRoomUtils";

		public static string Version => "0.1.0";

		private void Awake()
		{
			InstanceOrNull = this;
			Directory.CreateDirectory(RoomsFolder);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin " + Name + " (io.github.silksongrooms__1) has loaded! Rooms folder: " + RoomsFolder));
			_loader = new RoomLoader(((BaseUnityPlugin)this).Logger);
			_integrator = new SilksongRoomIntegrator(((BaseUnityPlugin)this).Logger);
			RoomsApi.Bind(this);
			SafeLoadAllRooms();
			SceneManager.sceneLoaded += OnSceneLoaded;
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			InstanceOrNull = null;
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			//IL_0018: 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)
			if (_loader == null || _integrator == null)
			{
				return;
			}
			try
			{
				_integrator.OnSceneLoaded(scene, mode, _loader.LoadedRooms);
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Room integrator OnSceneLoaded failed: {arg}");
			}
		}

		private void SafeLoadAllRooms()
		{
			if (_loader == null || _integrator == null)
			{
				return;
			}
			try
			{
				_loader.LoadAll(RoomsFolder);
				_integrator.RegisterRooms(_loader.LoadedRooms);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded {_loader.LoadedRooms.Count} room(s).");
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to load rooms: {arg}");
			}
		}

		internal IReadOnlyList<LoadedRoom> RegisterRoomsFolder(string folder)
		{
			if (_loader == null || _integrator == null)
			{
				return Array.Empty<LoadedRoom>();
			}
			List<LoadedRoom> list = _loader.AddFromFolder(folder);
			if (list.Count > 0)
			{
				_integrator.RegisterRooms(_loader.LoadedRooms);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Added {list.Count} room(s) from '{folder}'. Total: {_loader.LoadedRooms.Count}.");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("No rooms found in '" + folder + "'."));
			}
			return list;
		}

		internal LoadedRoom? RegisterRoomJson(string jsonFilePath, string? resolveRoot)
		{
			if (_loader == null || _integrator == null)
			{
				return null;
			}
			LoadedRoom loadedRoom = _loader.AddFromJsonFile(jsonFilePath, resolveRoot);
			if (loadedRoom != null)
			{
				_integrator.RegisterRooms(_loader.LoadedRooms);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Added room '{loadedRoom.Definition.id}' from '{jsonFilePath}'. Total: {_loader.LoadedRooms.Count}.");
			}
			return loadedRoom;
		}
	}
}