-
Notifications
You must be signed in to change notification settings - Fork 102
/
Copy pathRuntimeUnityEditorCore.cs
264 lines (224 loc) · 11 KB
/
RuntimeUnityEditorCore.cs
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
using System;
using System.Collections.Generic;
using System.Linq;
using RuntimeUnityEditor.Core.ObjectTree;
using RuntimeUnityEditor.Core.ObjectView;
using RuntimeUnityEditor.Core.Profiler;
using RuntimeUnityEditor.Core.REPL;
using RuntimeUnityEditor.Core.UI;
using RuntimeUnityEditor.Core.Utils;
using RuntimeUnityEditor.Core.Utils.Abstractions;
using UnityEngine;
#pragma warning disable CS0618
namespace RuntimeUnityEditor.Core
{
/// <summary>
/// Main class of RUE. It initializes and manages all of the features, and propagates events to them.
/// To access individual features, reference them directly by using <see cref="FeatureBase{T}.Initialized"/> and <see cref="FeatureBase{T}.Instance"/>.
/// </summary>
public class RuntimeUnityEditorCore
{
/// <summary>
/// Version constant for use in version checks.
/// Beware that this is a const and it will be burned as a string into your assembly at build time.
/// To see the version that is currently installed use <see cref="InstalledVersion"/>.
/// </summary>
public const string Version = Constants.Version;
/// <summary>
/// Get the currently installed version at runtime.
/// For use whenever the running instance version number is needed, instead of the version of the RUE assembly your plugin was compiled against.
/// </summary>
public static string InstalledVersion => Version;
/// <summary>
/// GUID for use in version and dependency checks.
/// Beware that this is a const and it will be burned as a string into your assembly. This shouldn't be an issue since this should never change.
/// </summary>
public const string GUID = "RuntimeUnityEditor";
#region Obsolete
/// <summary> Obsolete, do not use. Will be removed soon. </summary>
[Obsolete("Use window Instance instead", true)] public Inspector.Inspector Inspector => Core.Inspector.Inspector.Initialized ? Core.Inspector.Inspector.Instance : null;
/// <summary> Obsolete, do not use. Will be removed soon. </summary>
[Obsolete("Use window Instance instead", true)] public ObjectTreeViewer TreeViewer => ObjectTreeViewer.Initialized ? ObjectTreeViewer.Instance : null;
/// <summary> Obsolete, do not use. Will be removed soon. </summary>
[Obsolete("Use window Instance instead", true)] public ObjectViewWindow PreviewWindow => ObjectViewWindow.Initialized ? ObjectViewWindow.Instance : null;
/// <summary> Obsolete, do not use. Will be removed soon. </summary>
[Obsolete("Use window Instance instead", true)] public ProfilerWindow ProfilerWindow => ProfilerWindow.Initialized ? ProfilerWindow.Instance : null;
/// <summary> Obsolete, do not use. Will be removed soon. </summary>
[Obsolete("Use window Instance instead", true)] public ReplWindow Repl => ReplWindow.Initialized ? ReplWindow.Instance : null;
/// <summary> Obsolete, do not use. Will be removed soon. </summary>
[Obsolete("No longer works", true)] public event EventHandler SettingsChanged;
/// <summary>
/// Hotkey used to show/hide RuntimeUnityEditor. Changing this at runtime also updates the config file, so the value will be set on the next start.
/// </summary>
[Obsolete("Avoid changing the hotkey through code since it will overwrite user setting. Set the Show property instead if you need to show/hide RUE at specific times.")]
public KeyCode ShowHotkey
{
get =>_showHotkey?.Value ?? KeyCode.F12;
set => _showHotkey.Value = value;
}
/// <summary> Obsolete, do not use. Will be removed soon. </summary>
[Obsolete("Use window Instance instead", true)]
public bool ShowRepl
{
get => ReplWindow.Initialized && ReplWindow.Instance.Enabled;
set { if (ReplWindow.Initialized) ReplWindow.Instance.Enabled = value; }
}
/// <summary> Obsolete, do not use. Will be removed soon. </summary>
[Obsolete("Use window Instance instead", true)]
public bool EnableMouseInspect
{
get => MouseInspect.Initialized && MouseInspect.Instance.Enabled;
set { if (MouseInspect.Initialized) MouseInspect.Instance.Enabled = value; }
}
/// <summary> Obsolete, do not use. Will be removed soon. </summary>
[Obsolete("Use window Instance instead", true)]
public bool ShowInspector
{
get => Core.Inspector.Inspector.Initialized && Core.Inspector.Inspector.Instance.Enabled;
set { if (Core.Inspector.Inspector.Initialized) Core.Inspector.Inspector.Instance.Enabled = value; }
}
#endregion
#region Public API
/// <summary>
/// Current instance of RuntimeUnityEditor.
/// Use <see cref="IsInitialized"/> to check if initialization has been finished.
/// </summary>
public static RuntimeUnityEditorCore Instance { get; private set; }
/// <summary>
/// Check if RuntimeUnityEditor has finished initializing.
/// If this method is called from a background thread and the initialization is currently
/// in progress, it will block until initialization finishes (or fails).
/// </summary>
public static bool IsInitialized()
{
lock (GUID)
{
return Instance != null;
}
}
/// <summary>
/// Show RuntimeUnityEditor interface (global toggle controlled by the hotkey).
/// When hidden, most of the features are disabled/paused to minimize performance penalty.
/// </summary>
public bool Show
{
get => Taskbar.Instance.Enabled;
set
{
if (Taskbar.Instance.Enabled == value) return;
Taskbar.Instance.Enabled = value;
for (var index = 0; index < _initializedFeatures.Count; index++)
_initializedFeatures[index].OnEditorShownChanged(value);
}
}
/// <summary>
/// Features that have been successfully initialized so far and are available to the user.
/// </summary>
public IEnumerable<IFeature> InitializedFeatures => _initializedFeatures;
/// <summary>
/// Add a new feature to RuntimeUnityEditor.
/// Will throw if the feature fails to initialize.
/// </summary>
public void AddFeature(IFeature feature)
{
AddFeatureInt(feature);
Taskbar.Instance.SetFeatures(_initializedFeatures);
}
internal bool RemoveFeature(IFeature feature)
{
if (_initializedFeatures.Remove(feature))
{
Taskbar.Instance.SetFeatures(_initializedFeatures);
return true;
}
return false;
}
#endregion
internal static MonoBehaviour PluginObject => _initSettings.PluginMonoBehaviour;
internal static ILoggerWrapper Logger => _initSettings.LoggerWrapper;
private static InitSettings _initSettings;
private readonly List<IFeature> _initializedFeatures = new List<IFeature>();
private readonly InitSettings.Setting<KeyCode> _showHotkey;
/// <summary>
/// Initialize RuntimeUnityEditor. Can only be ran once. Must run on the main Unity thread.
/// Must complete before accessing any of RuntimeUnityEditor's features or they may not be initialized.
/// </summary>
internal RuntimeUnityEditorCore(InitSettings initSettings)
{
lock (GUID)
{
if (Instance != null)
throw new InvalidOperationException("Can create only one instance of the Core object");
_initSettings = initSettings;
Instance = this;
try
{
_showHotkey = initSettings.RegisterSetting("General", "Open/close runtime editor", KeyCode.F12, "");
var iFeatureType = typeof(IFeature);
// Create all instances first so they are accessible in Initialize methods in case there's crosslinking spaghetti
var allFeatures = typeof(RuntimeUnityEditorCore).Assembly.GetTypesSafe()
.Where(t => !t.IsAbstract && iFeatureType.IsAssignableFrom(t))
.Select(Activator.CreateInstance)
.Cast<IFeature>().ToList();
foreach (var feature in allFeatures)
{
try
{
AddFeatureInt(feature);
}
catch (Exception e)
{
if (feature is Taskbar)
throw new InvalidOperationException("WindowManager somehow failed to initialize! I am die, thank you forever.", e);
Logger.Log(LogLevel.Warning, $"Failed to initialize {feature.GetType().Name} - {(e is NotSupportedException ? e.Message : e.ToString())}");
}
}
Taskbar.Instance.SetFeatures(_initializedFeatures);
Logger.Log(LogLevel.Info, $"Successfully initialized {_initializedFeatures.Count}/{allFeatures.Count} features: {string.Join(", ", _initializedFeatures.Select(x => x.GetType().Name).ToArray())}");
}
catch
{
Instance = null;
throw;
}
}
}
private void AddFeatureInt(IFeature feature)
{
feature.OnInitialize(_initSettings);
_initializedFeatures.Add(feature);
}
#region Unity callbacks (called by the modloader-specific part of this plugin)
internal void OnGUI()
{
if (Show)
{
var originalSkin = GUI.skin;
GUI.skin = InterfaceMaker.CustomSkin;
for (var index = 0; index < _initializedFeatures.Count; index++)
_initializedFeatures[index].OnOnGUI();
// Restore old skin for maximum compatibility
GUI.skin = originalSkin;
}
}
internal void Update()
{
if (UnityInput.Current.GetKeyDown(ShowHotkey))
Show = !Show;
if (Show)
{
for (var index = 0; index < _initializedFeatures.Count; index++)
_initializedFeatures[index].OnUpdate();
}
}
internal void LateUpdate()
{
if (Show)
{
for (var index = 0; index < _initializedFeatures.Count; index++)
_initializedFeatures[index].OnLateUpdate();
}
}
#endregion
}
}