-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathManuallyReloadDomainTool.cs
More file actions
448 lines (413 loc) · 20.5 KB
/
Copy pathManuallyReloadDomainTool.cs
File metadata and controls
448 lines (413 loc) · 20.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
using UnityEditor;
using UnityEditor.Compilation;
using System.Diagnostics;
using UnityEngine;
using System.Reflection;
using System.IO;
using System.Collections.Generic;
using Assembly = UnityEditor.Compilation.Assembly;
using Debug = UnityEngine.Debug;
using AssemblyFlags = UnityEditor.Compilation.AssemblyFlags;
namespace Plugins.ManuallyReload
{
public static class ManuallyReloadDomainTool
{
/* 说明
* 关于域重载 https://docs.unity.cn/cn/2021.3/Manual/DomainReloading.html
* EditorApplication.LockReloadAssemblies()和 EditorApplication.UnlockReloadAssemblies() 成对
* 如果不小心LockReloadAssemblies3次 但是只UnlockReloadAssemblies了一次 那么还是不会重载 必须也要但是只UnlockReloadAssemblies3次
*/
public const string logYellow = "<color=yellow>{0}</color>";
public const string logCyan = "<color=Cyan>{0}</color>";
public const string logWhite = "<color=White>{0}</color>";
const string menuRealodDomain = "Tools/Reload Domain/Unlock Reload _F5"; //菜单快捷键 f5
const string menuForceRealodDomain = "Tools/Reload Domain/Force Unlock Reload %t"; //菜单快捷键(强制重新reload) ctrl+t
const string kFirstEnterUnity = "FirstEnterUnity"; //是否首次进入unity
const string kReloadDomainTimer = "ReloadDomainTimer"; //计时
static MethodInfo canReloadAssembliesMethod;
//编译时间
static Stopwatch compileSW = new Stopwatch();
//是否编译了
public static bool isNewCompile = false;
static bool isReloaded = false; //完全模式下是否reload
static List<string> listAssembly = new List<string>();
[InitializeOnLoadMethod]
static void Init()
{
if (SessionState.GetBool(kFirstEnterUnity, true))
{
SessionState.SetBool(kFirstEnterUnity, false);
if (ManuallyReloadSetting.Instance.IsEnableManuallyReload)
{
UnlockReloadDomain();
LockRealodDomain();
}
string state = ManuallyReloadSetting.Instance.IsEnableManuallyReload ? "Enable Manually Reload !" : "Disable Manually Reload !";
Debug.LogFormat(logCyan, state);
}
CompilationPipeline.compilationStarted += OnCompilationStarted;
CompilationPipeline.compilationFinished += OnCompilationFinished;
CompilationPipeline.assemblyCompilationFinished += EveryAssemblyCompilationFinished;
//域重载事件监听
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeDomainReload;
AssemblyReloadEvents.afterAssemblyReload += OnAfterDomainReload;
//编辑器运行模式改变
//如果不需要自动重置数据请注释下面代码
EditorApplication.playModeStateChanged += EditorApplication_playModeStateChanged;
}
//运行模式改变
static void EditorApplication_playModeStateChanged(PlayModeStateChange state)
{
//如果没有开启 或者 是完全手动模式直接return
if (!ManuallyReloadSetting.Instance.IsEnableManuallyReload) return;
if (ManuallyReloadSetting.Instance.IsFullyManuallyReload) return;
switch (state)
{
case PlayModeStateChange.ExitingEditMode:
if (!isReloaded)
ForceReloadDomain();
break;
case PlayModeStateChange.EnteredPlayMode:
break;
case PlayModeStateChange.ExitingPlayMode:
isReloaded = false;
break;
case PlayModeStateChange.EnteredEditMode:
break;
}
}
#region AssembleCompile
//完成编译任何一个dll
private static void EveryAssemblyCompilationFinished(string assemblypath, CompilerMessage[] infos)
{
if (!ManuallyReloadSetting.Instance.IsEnableManuallyReload) return;
listAssembly.Add(assemblypath);
}
//当开始编译脚本
private static void OnCompilationStarted(object obj)
{
if (!ManuallyReloadSetting.Instance.IsEnableManuallyReload) return;
listAssembly.Clear();
compileSW.Start();
Debug.LogFormat(logYellow, "Beging Compile...");
}
//结束编译
private static void OnCompilationFinished(object obj)
{
if (!ManuallyReloadSetting.Instance.IsEnableManuallyReload) return;
compileSW.Stop();
Debug.LogFormat(logYellow, $"End Compile :{compileSW.ElapsedMilliseconds} ms");
isNewCompile = true;
//编辑器代码不手动reload
if (!ManuallyReloadSetting.Instance.IsEditorUseManuallyReload)
{
//获取所有编辑器dll
var listEditorAssemblys = new List<Assembly>(CompilationPipeline.GetAssemblies(AssembliesType.Editor));
for (int i = listEditorAssemblys.Count - 1; i >= 0; i--)
{
if (listEditorAssemblys[i].flags != AssemblyFlags.EditorAssembly)
{
listEditorAssemblys.RemoveAt(i);
}
}
if (listAssembly.Count > 0)
{
//判断当前编译的dll是否是edior dll
bool result = listAssembly.TrueForAll(assemblyPath => listEditorAssemblys.Exists(editAss => editAss.outputPath == assemblyPath));
//如果都是编辑器代码
if (result)
{
Debug.LogFormat(logWhite, "[IsEditorUseManuallyReload = false] Editor Code Changed Force Reload......");
ForceReloadDomain();
}
}
}
}
#endregion
#region ReloadDomain
//开始reload domain
private static void OnBeforeDomainReload()
{
if (!ManuallyReloadSetting.Instance.IsEnableManuallyReload) return;
Debug.LogFormat(logYellow, "Begin Reload Domain...");
//记录时间 由于
SessionState.SetInt(kReloadDomainTimer, (int)(EditorApplication.timeSinceStartup * 1000));
}
//结束reload domain
private static void OnAfterDomainReload()
{
if (!ManuallyReloadSetting.Instance.IsEnableManuallyReload) return;
var timeMS = (int)(EditorApplication.timeSinceStartup * 1000) - SessionState.GetInt(kReloadDomainTimer, 0);
Debug.LogFormat(logYellow, $"End Reload Domain : {timeMS} ms");
if (ManuallyReloadSetting.Instance.IsEnableManuallyReload)
LockRealodDomain();
isNewCompile = false;
isReloaded = true;
}
#endregion
#region Menu
//手动刷新
[MenuItem(menuRealodDomain)]
static void ManualReload()
{
if (!ManuallyReloadSetting.Instance.IsEnableManuallyReload) return;
if (isNewCompile)
{
ForceReloadDomain();
}
else
{
//Debug.Log(IsAssemblyReloadLocked() + " " + EditorApplication.isCompiling);
if (IsAssemblyReloadLocked() && EditorApplication.isCompiling)
{
ForceReloadDomain();
}
else
{
Debug.LogFormat(logWhite, "无需再reload domain,如需请使用 Ctrl+T 强制reload domain!");
}
}
}
//强制刷新
[MenuItem(menuForceRealodDomain)]
static void MenuForceReloadDomain()
{
if (ManuallyReloadSetting.Instance.IsEnableManuallyReload)
ForceReloadDomain();
}
#endregion
/// <summary>
/// 判断当前程序集重载是否锁定了 (右下角显示 锁)
/// </summary>
public static bool IsAssemblyReloadLocked()
{
//https://github.com/INeatFreak/unity-background-recompiler
//https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/EditorApplication.bindings.cs#L154
if (canReloadAssembliesMethod == null)
canReloadAssembliesMethod = typeof(EditorApplication).GetMethod("CanReloadAssemblies", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
if (canReloadAssembliesMethod != null)
{
// 如果 CanReloadAssemblies 返回 false,说明目前不允许重载(被锁定或正在运行等)
bool canReload = (bool)canReloadAssembliesMethod.Invoke(null, null);
return !canReload;/* && EditorApplication.isCompiling;*/
}
Debug.LogError("无法通过当前版本的反射获取 [CanReloadAssemblies],请检查 Unity 版本差异。");
return false;
}
public static void LockRealodDomain()
{
//如果没有锁住 锁住
if (!IsAssemblyReloadLocked())
EditorApplication.LockReloadAssemblies();
}
public static void UnlockReloadDomain()
{
//如果锁住了 打开
if (IsAssemblyReloadLocked())
EditorApplication.UnlockReloadAssemblies();
}
//强制reloaddomain
public static void ForceReloadDomain()
{
UnlockReloadDomain();
EditorUtility.RequestScriptReload();
}
}
/// <summary>
/// 设置
/// </summary>
[System.Serializable]
public class ManuallyReloadSetting : ScriptableObject
{
[Header("Main Setting")]
[Tooltip("是否启用手动Reload")]
public bool IsEnableManuallyReload;
[Tooltip("是否完全手动Reload 为true时,除了手动外,其他操作均不触发Reload")]
public bool IsFullyManuallyReload;
[Tooltip("是否监听代码行为 默认当创建/删除(cs/asmdef/asmref)时会触发Reload 设置为true时,代码行为变化不再触发Reload")]
public bool IsMonitoringCodeBehavior;
[Tooltip("是否Editor代码也需手动Reload 当编辑Editor代码才有效")]
public bool IsEditorUseManuallyReload;
//[Header("Other Setting (Optional)")]
//[Tooltip("显示编译和Reload耗时日志")]
//public bool ShowCompilationAndReloadLog = true;
private static string filePath;
private static ManuallyReloadSetting m_Instance;
public static ManuallyReloadSetting Instance
{
get
{
if (string.IsNullOrEmpty(filePath))
filePath = Application.dataPath + "/../ProjectSettings/ManuallyReloadSettings.asset";
if (m_Instance == null)
{
m_Instance = ScriptableObject.CreateInstance<ManuallyReloadSetting>();
try
{
JsonUtility.FromJsonOverwrite(File.ReadAllText(filePath), m_Instance);
}
catch (System.Exception e) { }
}
return m_Instance;
}
}
public void Save()
{
File.WriteAllText(filePath, JsonUtility.ToJson(this, true));
}
}
/// <summary>
/// 注册到ProjectSettings中 https://docs.unity.cn/cn/2022.3/ScriptReference/SettingsProvider.html
/// </summary>
static class ManuallyReloadInProjectSetting
{
static GUIStyle iconStyle;
static GUIStyle boxStyle;
static SerializedObject so;
static SerializedProperty p_isEnableManuallyReload;
static SerializedProperty p_isFullyManuallyReload;
static SerializedProperty p_isMonitoringCodeBehaviour;
static SerializedProperty p_isEditorUseManuallyReload;
// static SerializedProperty p_ShowCompilationAndReloadLog;
static GUIContent guiContentEnable = new GUIContent(" Enable Manually Reload", EditorGUIUtility.IconContent("refresh").image);
static GUIContent guiContentFullyManually = new GUIContent(" Fully Manually Reload", EditorGUIUtility.IconContent("d_winbtn_win_restore").image);
static GUIContent guiContentLisCodeBehaviour = new GUIContent(" Monitoring Code Behavior", EditorGUIUtility.IconContent("d_winbtn_win_restore").image);
static GUIContent guiContentEditor = new GUIContent(" Editor Scripts Manually Reload", EditorGUIUtility.IconContent("d_winbtn_win_restore").image);
//static GUIContent guiContentShowLog = new GUIContent(" Show Compilation And Reload Log", EditorGUIUtility.IconContent("d_UnityEditor.ConsoleWindow").image);
[SettingsProvider]
public static SettingsProvider CreateMyManuallyReloadProvider()
{
var provider = new SettingsProvider("Project/ManuallyReload Setting", SettingsScope.Project)
{
label = "Manually Reload Setting",
titleBarGuiHandler = () =>
{
if (iconStyle == null)
iconStyle = GUI.skin.GetStyle("IconButton");
if (boxStyle == null)
{
boxStyle = GUI.skin.box;
boxStyle.richText = true;
boxStyle.alignment = TextAnchor.MiddleLeft;
}
if (GUILayout.Button(EditorGUIUtility.IconContent("_Help"), iconStyle))
Application.OpenURL("https://github.com/ZeroUltra/UnityManualReload");
},
guiHandler = (searchContext) =>
{
EditorGUIUtility.labelWidth = 500;
if (so == null)
{
so = new SerializedObject(ManuallyReloadSetting.Instance);
p_isEnableManuallyReload = so.FindProperty(nameof(ManuallyReloadSetting.Instance.IsEnableManuallyReload));
p_isFullyManuallyReload = so.FindProperty(nameof(ManuallyReloadSetting.Instance.IsFullyManuallyReload));
p_isEditorUseManuallyReload = so.FindProperty(nameof(ManuallyReloadSetting.Instance.IsEditorUseManuallyReload));
p_isMonitoringCodeBehaviour = so.FindProperty(nameof(ManuallyReloadSetting.Instance.IsMonitoringCodeBehavior));
// p_ShowCompilationAndReloadLog = so.FindProperty(nameof(ManuallyReloadSetting.Instance.ShowCompilationAndReloadLog));
}
var settings = ManuallyReloadSetting.Instance;
using (var check = new EditorGUI.ChangeCheckScope())
{
EditorGUILayout.PropertyField(p_isEnableManuallyReload, guiContentEnable);
if (check.changed)
{
so.ApplyModifiedPropertiesWithoutUndo();
settings.Save();
if (settings.IsEnableManuallyReload)
{
//编辑器设置 projectsetting->editor->enterPlayModeSetting
Debug.LogFormat(ManuallyReloadDomainTool.logCyan, "Enable Manually Reload !");
EditorSettings.enterPlayModeOptionsEnabled = true;
EditorSettings.enterPlayModeOptions = EnterPlayModeOptions.DisableDomainReload;
ManuallyReloadDomainTool.LockRealodDomain();
}
else
{
Debug.LogFormat(ManuallyReloadDomainTool.logCyan, "Disable Manually Reload !");
ManuallyReloadDomainTool.ForceReloadDomain();
EditorSettings.enterPlayModeOptionsEnabled = false;
}
}
}
GUISerializedPropertyBool(p_isFullyManuallyReload, !p_isEnableManuallyReload.boolValue, guiContentFullyManually);
GUISerializedPropertyBool(p_isEditorUseManuallyReload, !p_isEnableManuallyReload.boolValue, guiContentEditor);
GUISerializedPropertyBool(p_isMonitoringCodeBehaviour, !p_isEnableManuallyReload.boolValue, guiContentLisCodeBehaviour);
//GUISerializedPropertyBool(p_ShowCompilationAndReloadLog, false, guiContentShowLog);
GUILayout.Space(10);
//GUI.DrawTexture(new Rect(425, 150, 22, 22), EditorGUIUtility.IconContent("Locked").image);
//GUILayout.Box(new GUIContent($"脚本编译之后,按下 <color=red>F5</color> 进行重载(Realod Domain)" +
// $"\n\n如遇编译锁住,按下 <color=red>Ctrl+T</color> 强制进行重载(Unity编辑器右下角始终为 <color=yellow>[锁] </color> 状态时)" +
// $"\n\n如需修改快捷键请自行修改代码", EditorGUIUtility.IconContent("d_console.warnicon").image), boxStyle, GUILayout.MinWidth(520));
GUILayout.Box(new GUIContent($"脚本编译之后,按下 <color=red>F5</color> 进行重载(Realod Domain)" +
$"\n\n如遇其他问题,按下 <color=red>Ctrl+T</color> 强制进行重载)" +
$"\n\n如需修改快捷键请自行修改代码", EditorGUIUtility.IconContent("d_console.warnicon").image), boxStyle, GUILayout.MinWidth(520));
},
keywords = new string[] { "Reload", "Manually" }
};
return provider;
}
private static void GUISerializedPropertyBool(SerializedProperty serializedProperty, bool disable, GUIContent guiContent)
{
using (var check = new EditorGUI.ChangeCheckScope())
{
using (new EditorGUI.DisabledScope(disable))
EditorGUILayout.PropertyField(serializedProperty, guiContent);
if (check.changed)
{
so.ApplyModifiedPropertiesWithoutUndo();
ManuallyReloadSetting.Instance.Save();
}
}
}
}
/// <summary>
/// 监听文件变化
/// </summary>
public class ScriptsProcessor : AssetModificationProcessor
{
static int reloadCount = 0;
const string metaExtension = ".meta";
static void OnWillCreateAsset(string assetPath)
{
//.meta生成才标识真的创建了文件
if (Path.GetExtension(assetPath) == metaExtension)
{
assetPath = assetPath.Replace(metaExtension, "");
Check(assetPath, "New");
}
}
static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions option)
{
Check(assetPath, "Delete");
return AssetDeleteResult.DidNotDelete; // 表示没拦截删除流程,只做监听
}
static void Check(string assetPath, string behaviourCode)
{
//获取文件的后缀类型
string extension = Path.GetExtension(assetPath);
//如果cs脚本或asm
if (extension == ".cs" || extension == ".asmdef" || extension == ".asmref")
{
reloadCount++;
ManuallyReloadDomainTool.isNewCompile = true;
//不监听 为啥放在这里 因为要标记ManuallyReloadDomainTool.isNewCompile = true,新建/添加都newCompile
if (ManuallyReloadSetting.Instance.IsMonitoringCodeBehavior)
return;
//如果启用
if (ManuallyReloadSetting.Instance.IsEnableManuallyReload)
{
Debug.Log($"Force Reload Domain, {behaviourCode} File: {assetPath}");
if (reloadCount == 1)
{
//强制reload domain 延迟一帧 保证只触发一次
EditorApplication.delayCall += () =>
{
ManuallyReloadDomainTool.ForceReloadDomain();
reloadCount = 0;
};
}
}
}
}
}
}