168 lines
5.0 KiB
C#
168 lines
5.0 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.IO;
|
|
using UnityEngine;
|
|
|
|
namespace ShrinkModFramework
|
|
{
|
|
[DefaultExecutionOrder(-2099)]
|
|
internal sealed class ShrinkModRuntimeDriver : MonoBehaviour
|
|
{
|
|
private static ShrinkModRuntimeDriver _instance;
|
|
|
|
private readonly ConcurrentQueue<Action> _mainThreadActions = new();
|
|
private FileSystemWatcher _watcher;
|
|
private ShrinkModFrameworkSettings _settings;
|
|
private bool _pendingExternalReload;
|
|
private float _reloadAtRealtime;
|
|
|
|
public static void EnsureCreated(ShrinkModFrameworkSettings settings)
|
|
{
|
|
if (_instance)
|
|
{
|
|
_instance.ApplySettings(settings);
|
|
return;
|
|
}
|
|
|
|
var go = new GameObject("ShrinkModRuntimeDriver");
|
|
DontDestroyOnLoad(go);
|
|
_instance = go.AddComponent<ShrinkModRuntimeDriver>();
|
|
_instance.ApplySettings(settings);
|
|
}
|
|
|
|
public static void Enqueue(Action action)
|
|
{
|
|
if (action == null)
|
|
return;
|
|
|
|
if (_instance == null)
|
|
throw new InvalidOperationException("ShrinkModRuntimeDriver 尚未创建。");
|
|
|
|
_instance._mainThreadActions.Enqueue(action);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
while (_mainThreadActions.TryDequeue(out var action))
|
|
{
|
|
try
|
|
{
|
|
action();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(ex);
|
|
}
|
|
}
|
|
|
|
if (!_pendingExternalReload || Time.realtimeSinceStartup < _reloadAtRealtime)
|
|
return;
|
|
|
|
_pendingExternalReload = false;
|
|
|
|
try
|
|
{
|
|
ShrinkModLoader.LoadNewExternalMods(_settings);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(ex);
|
|
Debug.LogError($"[ShrinkModFramework] 自动热加载外部模组失败:{ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
DisposeWatcher();
|
|
if (_instance == this)
|
|
_instance = null;
|
|
}
|
|
|
|
private void ApplySettings(ShrinkModFrameworkSettings settings)
|
|
{
|
|
_settings = settings ?? ShrinkModFrameworkSettings.Instance;
|
|
|
|
if (_settings == null || !_settings.enableExternalDllMods || !_settings.watchExternalModsDirectory)
|
|
{
|
|
DisposeWatcher();
|
|
return;
|
|
}
|
|
|
|
TrySetupWatcher();
|
|
}
|
|
|
|
private void TrySetupWatcher()
|
|
{
|
|
#if ENABLE_IL2CPP && !UNITY_EDITOR
|
|
return;
|
|
#else
|
|
var directory = ShrinkExternalModAssemblyLoader.GetExternalModsDirectory(_settings);
|
|
if (_settings.autoCreateExternalModsDirectory)
|
|
Directory.CreateDirectory(directory);
|
|
|
|
if (!Directory.Exists(directory))
|
|
return;
|
|
|
|
if (_watcher != null && string.Equals(_watcher.Path, directory, StringComparison.OrdinalIgnoreCase))
|
|
return;
|
|
|
|
DisposeWatcher();
|
|
|
|
try
|
|
{
|
|
_watcher = new FileSystemWatcher(directory, "*.dll")
|
|
{
|
|
IncludeSubdirectories = true,
|
|
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime
|
|
};
|
|
_watcher.Created += OnExternalModsChanged;
|
|
_watcher.Changed += OnExternalModsChanged;
|
|
_watcher.Renamed += OnExternalModsRenamed;
|
|
_watcher.EnableRaisingEvents = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning($"[ShrinkModFramework] 外部模组目录监听启动失败:{ex.Message}");
|
|
DisposeWatcher();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private void OnExternalModsChanged(object sender, FileSystemEventArgs args)
|
|
{
|
|
ScheduleExternalReload(args.FullPath);
|
|
}
|
|
|
|
private void OnExternalModsRenamed(object sender, RenamedEventArgs args)
|
|
{
|
|
ScheduleExternalReload(args.FullPath);
|
|
}
|
|
|
|
private void ScheduleExternalReload(string path)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
return;
|
|
|
|
_mainThreadActions.Enqueue(() =>
|
|
{
|
|
_pendingExternalReload = true;
|
|
var delay = _settings ? Mathf.Max(0.05f, _settings.externalModsReloadDelaySeconds) : 0.5f;
|
|
_reloadAtRealtime = Time.realtimeSinceStartup + delay;
|
|
});
|
|
}
|
|
|
|
private void DisposeWatcher()
|
|
{
|
|
if (_watcher == null)
|
|
return;
|
|
|
|
_watcher.EnableRaisingEvents = false;
|
|
_watcher.Created -= OnExternalModsChanged;
|
|
_watcher.Changed -= OnExternalModsChanged;
|
|
_watcher.Renamed -= OnExternalModsRenamed;
|
|
_watcher.Dispose();
|
|
_watcher = null;
|
|
}
|
|
}
|
|
}
|