Skip to content

ScriptEngine: added FileSystemWatcher, optionally including subdirectories and… #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 8, 2023
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 64 additions & 8 deletions src/ScriptEngine/ScriptEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,40 +27,59 @@ public class ScriptEngine : BaseUnityPlugin

ConfigEntry<bool> LoadOnStart { get; set; }
ConfigEntry<KeyboardShortcut> ReloadKey { get; set; }

// FS Watcher EDIT
ConfigEntry<bool> QuietMode { get; set; }
ConfigEntry<bool> EnableFileSystemWatcher { get; set; }
ConfigEntry<bool> IncludeSubdirectories { get; set; }

private FileSystemWatcher fileSystemWatcher;
private bool shouldReload;
//

void Awake()
{
LoadOnStart = Config.Bind("General", "LoadOnStart", false, new ConfigDescription("Load all plugins from the scripts folder when starting the application"));
ReloadKey = Config.Bind("General", "ReloadKey", new KeyboardShortcut(KeyCode.F6), new ConfigDescription("Press this key to reload all the plugins from the scripts folder"));
QuietMode = Config.Bind("General", "QuietMode", false, new ConfigDescription("Suppress sending log messages to console except for the error ones."));
EnableFileSystemWatcher = Config.Bind("General", "EnableFileSystemWatcher", false, new ConfigDescription("Listens for changes to the scripts directory and reloads all plugins automatically."));
IncludeSubdirectories = Config.Bind("General", "IncludeSubdirectories", false, new ConfigDescription("Include also subdirectories under the scripts folder."));

if (LoadOnStart.Value)
ReloadPlugins();

if (EnableFileSystemWatcher.Value)
StartFileSystemWatcher();
}

void Update()
{
if (ReloadKey.Value.IsDown())
if (shouldReload || ReloadKey.Value.IsDown())
ReloadPlugins();
}

void ReloadPlugins()
{
Logger.Log(LogLevel.Info, "Unloading old plugin instances");
shouldReload = false;
if (!QuietMode.Value)
Logger.Log(LogLevel.Info, "Unloading old plugin instances");
Destroy(scriptManager);
scriptManager = new GameObject($"ScriptEngine_{DateTime.Now.Ticks}");
DontDestroyOnLoad(scriptManager);

var files = Directory.GetFiles(ScriptDirectory, "*.dll");
var files = Directory.GetFiles(ScriptDirectory, "*.dll", IncludeSubdirectories.Value ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
if (files.Length > 0)
{
foreach (string path in Directory.GetFiles(ScriptDirectory, "*.dll"))
foreach (string path in Directory.GetFiles(ScriptDirectory, "*.dll", IncludeSubdirectories.Value ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
LoadDLL(path, scriptManager);

Logger.LogMessage("Reloaded all plugins!");
if (!QuietMode.Value)
Logger.LogMessage("Reloaded all plugins!");
}
else
{
Logger.LogMessage("No plugins to reload");
if (!QuietMode.Value)
Logger.LogMessage("No plugins to reload");
}
}

Expand All @@ -71,7 +90,8 @@ void LoadDLL(string path, GameObject obj)
defaultResolver.AddSearchDirectory(Paths.ManagedPath);
defaultResolver.AddSearchDirectory(Paths.BepInExAssemblyDirectory);

Logger.Log(LogLevel.Info, $"Loading plugins from {path}");
if (!QuietMode.Value)
Logger.Log(LogLevel.Info, $"Loading plugins from {path}");

using (var dll = AssemblyDefinition.ReadAssembly(path, new ReaderParameters { AssemblyResolver = defaultResolver }))
{
Expand All @@ -95,7 +115,8 @@ void LoadDLL(string path, GameObject obj)
var typeInfo = Chainloader.ToPluginInfo(typeDefinition);
Chainloader.PluginInfos[metadata.GUID] = typeInfo;

Logger.Log(LogLevel.Info, $"Loading {metadata.GUID}");
if (!QuietMode.Value)
Logger.Log(LogLevel.Info, $"Loading {metadata.GUID}");
StartCoroutine(DelayAction(() =>
{
try
Expand All @@ -119,6 +140,41 @@ void LoadDLL(string path, GameObject obj)
}
}

private void StartFileSystemWatcher()
{
fileSystemWatcher = new FileSystemWatcher(ScriptDirectory)
{
IncludeSubdirectories = IncludeSubdirectories.Value
};
fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
fileSystemWatcher.Filter = "*.dll";
fileSystemWatcher.Changed += (sender, args) =>
{
if (!QuietMode.Value)
Logger.LogInfo($"File {Path.GetFileName(args.Name)} changed. Recompiling...");
shouldReload = true;
};
fileSystemWatcher.Deleted += (sender, args) =>
{
if (!QuietMode.Value)
Logger.LogInfo($"File {Path.GetFileName(args.Name)} removed. Recompiling...");
shouldReload = true;
};
fileSystemWatcher.Created += (sender, args) =>
{
if (!QuietMode.Value)
Logger.LogInfo($"File {Path.GetFileName(args.Name)} created. Recompiling...");
shouldReload = true;
};
fileSystemWatcher.Renamed += (sender, args) =>
{
if (!QuietMode.Value)
Logger.LogInfo($"File {Path.GetFileName(args.Name)} renamed. Recompiling...");
shouldReload = true;
};
fileSystemWatcher.EnableRaisingEvents = true;
}

private IEnumerable<Type> GetTypesSafe(Assembly ass)
{
try
Expand Down