feat(cordis): 接入上下文组合与模组事务热替换
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user