Decompiled source of BepinExScriptEngine v0.1.0

ScriptEngine.dll

Decompiled 2 months ago
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.InteropServices;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Mono.Cecil;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ScriptEngine")]
[assembly: AssemblyDescription("Loads and reloads BepInEx plugins from the BepInEx\\scripts folder")]
[assembly: AssemblyProduct("ScriptEngine")]
[assembly: Guid("967c527c-1a7e-4c83-9427-e92a6092f59e")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("https://github.com/BepInEx/BepInEx.Debug")]
[assembly: AssemblyCopyright("Copyright ©  2020")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: AssemblyVersion("11.0.0.0")]
namespace Common
{
	internal static class Metadata
	{
		public const string Version = "11.0";
	}
}
namespace ScriptEngine
{
	[BepInPlugin("com.bepis.bepinex.scriptengine", "Script Engine", "11.0")]
	public class ScriptEngine : BaseUnityPlugin
	{
		public const string GUID = "com.bepis.bepinex.scriptengine";

		public const string Version = "11.0";

		private GameObject scriptManager;

		private static readonly string DumpedAssembliesPath = Utility.CombinePaths(new string[2]
		{
			Paths.BepInExRootPath,
			"ScriptEngineDumpedAssemblies"
		});

		private FileSystemWatcher fileSystemWatcher;

		private bool shouldReload;

		private float autoReloadTimer;

		public string ScriptDirectory => Path.Combine(Paths.BepInExRootPath, "scripts");

		private ConfigEntry<bool> LoadOnStart { get; set; }

		private ConfigEntry<KeyboardShortcut> ReloadKey { get; set; }

		private ConfigEntry<bool> QuietMode { get; set; }

		private ConfigEntry<bool> EnableFileSystemWatcher { get; set; }

		private ConfigEntry<bool> IncludeSubdirectories { get; set; }

		private ConfigEntry<float> AutoReloadDelay { get; set; }

		private ConfigEntry<bool> DumpAssemblies { get; set; }

		private void Awake()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			LoadOnStart = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LoadOnStart", false, new ConfigDescription("Load all plugins from the scripts folder when starting the application. This is done from inside of Chainloader's Awake, therefore not all plugis might be loaded yet. BepInDependency attributes are ignored.", (AcceptableValueBase)null, new object[0]));
			ReloadKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ReloadKey", new KeyboardShortcut((KeyCode)287, (KeyCode[])(object)new KeyCode[0]), new ConfigDescription("Press this key to reload all the plugins from the scripts folder", (AcceptableValueBase)null, new object[0]));
			QuietMode = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "QuietMode", false, new ConfigDescription("Disable all logging except for error messages.", (AcceptableValueBase)null, new object[0]));
			IncludeSubdirectories = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeSubdirectories", false, new ConfigDescription("Also load plugins from subdirectories of the scripts folder.", (AcceptableValueBase)null, new object[0]));
			EnableFileSystemWatcher = ((BaseUnityPlugin)this).Config.Bind<bool>("AutoReload", "EnableFileSystemWatcher", false, new ConfigDescription("Watches the scripts directory for file changes and automatically reloads all plugins if any of the files gets changed (added/removed/modified).", (AcceptableValueBase)null, new object[0]));
			AutoReloadDelay = ((BaseUnityPlugin)this).Config.Bind<float>("AutoReload", "AutoReloadDelay", 3f, new ConfigDescription("Delay in seconds from detecting a change to files in the scripts directory to plugins being reloaded. Affects only EnableFileSystemWatcher.", (AcceptableValueBase)null, new object[0]));
			DumpAssemblies = ((BaseUnityPlugin)this).Config.Bind<bool>("AutoReload", "DumpAssemblies", false, "If enabled, BepInEx will save patched assemblies & symbols into BepInEx/ScriptEngineDumpedAssemblies.\nThis can be used by developers to inspect and debug plugins loaded by ScriptEngine.");
			if (Directory.Exists(DumpedAssembliesPath))
			{
				Directory.Delete(DumpedAssembliesPath, recursive: true);
			}
			if (LoadOnStart.Value)
			{
				ReloadPlugins();
			}
			if (EnableFileSystemWatcher.Value)
			{
				StartFileSystemWatcher();
			}
		}

		private void Update()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = ReloadKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				ReloadPlugins();
			}
			else if (shouldReload)
			{
				autoReloadTimer -= Time.unscaledDeltaTime;
				if (autoReloadTimer <= 0f)
				{
					ReloadPlugins();
				}
			}
		}

		private void ReloadPlugins()
		{
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			shouldReload = false;
			if ((Object)(object)scriptManager != (Object)null)
			{
				if (!QuietMode.Value)
				{
					((BaseUnityPlugin)this).Logger.Log((LogLevel)16, (object)"Unloading old plugin instances");
				}
				BaseUnityPlugin[] components = scriptManager.GetComponents<BaseUnityPlugin>();
				foreach (BaseUnityPlugin val in components)
				{
					string gUID = val.Info.Metadata.GUID;
					if (Chainloader.PluginInfos.ContainsKey(gUID))
					{
						Chainloader.PluginInfos.Remove(gUID);
					}
				}
				Object.Destroy((Object)(object)scriptManager);
			}
			scriptManager = new GameObject($"ScriptEngine_{DateTime.Now.Ticks}");
			Object.DontDestroyOnLoad((Object)(object)scriptManager);
			string[] files = Directory.GetFiles(ScriptDirectory, "*.dll", IncludeSubdirectories.Value ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
			if (files.Length != 0)
			{
				string[] files2 = Directory.GetFiles(ScriptDirectory, "*.dll", IncludeSubdirectories.Value ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
				foreach (string path in files2)
				{
					LoadDLL(path, scriptManager);
				}
				if (!QuietMode.Value)
				{
					((BaseUnityPlugin)this).Logger.LogMessage((object)"Reloaded all plugins!");
				}
			}
			else if (!QuietMode.Value)
			{
				((BaseUnityPlugin)this).Logger.LogMessage((object)"No plugins to reload");
			}
		}

		private void LoadDLL(string path, GameObject obj)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_008b: Expected O, but got Unknown
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Expected O, but got Unknown
			DefaultAssemblyResolver val = new DefaultAssemblyResolver();
			((BaseAssemblyResolver)val).AddSearchDirectory(ScriptDirectory);
			((BaseAssemblyResolver)val).AddSearchDirectory(Paths.ManagedPath);
			((BaseAssemblyResolver)val).AddSearchDirectory(Paths.BepInExAssemblyDirectory);
			if (!QuietMode.Value)
			{
				((BaseUnityPlugin)this).Logger.Log((LogLevel)16, (object)("Loading plugins from " + path));
			}
			AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly(path, new ReaderParameters
			{
				AssemblyResolver = (IAssemblyResolver)(object)val,
				ReadSymbols = true
			});
			try
			{
				((AssemblyNameReference)val2.Name).Name = $"{((AssemblyNameReference)val2.Name).Name}-{DateTime.Now.Ticks}";
				Assembly ass;
				if (DumpAssemblies.Value)
				{
					if (!Directory.Exists(DumpedAssembliesPath))
					{
						Directory.CreateDirectory(DumpedAssembliesPath);
					}
					string text = Path.Combine(DumpedAssembliesPath, ((AssemblyNameReference)val2.Name).Name + Path.GetExtension(((ModuleReference)val2.MainModule).Name));
					using (FileStream fileStream = new FileStream(text, FileMode.Create))
					{
						val2.Write((Stream)fileStream, new WriterParameters
						{
							WriteSymbols = true
						});
					}
					ass = Assembly.LoadFile(text);
					if (!QuietMode.Value)
					{
						((BaseUnityPlugin)this).Logger.Log((LogLevel)16, (object)("Loaded dumped Assembly from " + text));
					}
				}
				else
				{
					using MemoryStream memoryStream = new MemoryStream();
					val2.Write((Stream)memoryStream);
					ass = Assembly.Load(memoryStream.ToArray());
				}
				foreach (Type type in GetTypesSafe(ass))
				{
					try
					{
						if (!typeof(BaseUnityPlugin).IsAssignableFrom(type))
						{
							continue;
						}
						BepInPlugin metadata = MetadataHelper.GetMetadata(type);
						if (metadata == null)
						{
							continue;
						}
						if (!QuietMode.Value)
						{
							((BaseUnityPlugin)this).Logger.Log((LogLevel)16, (object)("Loading " + metadata.GUID));
						}
						if (Chainloader.PluginInfos.TryGetValue(metadata.GUID, out var value))
						{
							throw new InvalidOperationException($"A plugin with GUID {metadata.GUID} is already loaded! ({value.Metadata.Name} v{value.Metadata.Version})");
						}
						TypeDefinition val3 = ((IEnumerable<TypeDefinition>)val2.MainModule.Types).First((TypeDefinition x) => ((MemberReference)x).FullName == type.FullName);
						PluginInfo pluginInfo = Chainloader.ToPluginInfo(val3);
						((MonoBehaviour)this).StartCoroutine(DelayAction(delegate
						{
							//IL_0055: Unknown result type (might be due to invalid IL or missing references)
							//IL_005f: Expected O, but got Unknown
							try
							{
								Chainloader.PluginInfos[metadata.GUID] = pluginInfo;
								Component val4 = obj.AddComponent(type);
								Traverse val5 = Traverse.Create((object)pluginInfo);
								val5.Property<BaseUnityPlugin>("Instance", (object[])null).Value = (BaseUnityPlugin)val4;
								val5.Property<string>("Location", (object[])null).Value = path;
							}
							catch (Exception arg2)
							{
								((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to load plugin {metadata.GUID} because of exception: {arg2}");
								Chainloader.PluginInfos.Remove(metadata.GUID);
							}
						}));
					}
					catch (Exception arg)
					{
						((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to load plugin {type.Name} because of exception: {arg}");
					}
				}
			}
			finally
			{
				((IDisposable)val2)?.Dispose();
			}
		}

		private void StartFileSystemWatcher()
		{
			fileSystemWatcher = new FileSystemWatcher(ScriptDirectory)
			{
				IncludeSubdirectories = IncludeSubdirectories.Value
			};
			fileSystemWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;
			fileSystemWatcher.Filter = "*.dll";
			fileSystemWatcher.Changed += FileChangedEventHandler;
			fileSystemWatcher.Deleted += FileChangedEventHandler;
			fileSystemWatcher.Created += FileChangedEventHandler;
			fileSystemWatcher.Renamed += FileChangedEventHandler;
			fileSystemWatcher.EnableRaisingEvents = true;
		}

		private void FileChangedEventHandler(object sender, FileSystemEventArgs args)
		{
			if (!QuietMode.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("File " + Path.GetFileName(args.Name) + " changed. Delayed recompiling..."));
			}
			shouldReload = true;
			autoReloadTimer = AutoReloadDelay.Value;
		}

		private IEnumerable<Type> GetTypesSafe(Assembly ass)
		{
			try
			{
				return ass.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("\r\n-- LoaderExceptions --");
				Exception[] loaderExceptions = ex.LoaderExceptions;
				foreach (Exception ex2 in loaderExceptions)
				{
					stringBuilder.AppendLine(ex2.ToString());
				}
				stringBuilder.AppendLine("\r\n-- StackTrace --");
				stringBuilder.AppendLine(ex.StackTrace);
				((BaseUnityPlugin)this).Logger.LogError((object)stringBuilder.ToString());
				return ex.Types.Where((Type x) => (object)x != null);
			}
		}

		private IEnumerator DelayAction(Action action)
		{
			yield return null;
			action();
		}
	}
}