Merge branch 'unload_alc_fix' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-unload_alc_fix

This commit is contained in:
Wojtek Figat
2025-03-18 14:20:20 +01:00
31 changed files with 539 additions and 140 deletions

View File

@@ -196,6 +196,15 @@ namespace FlaxEditor.CustomEditors
Presenter.AfterLayout?.Invoke(layout); Presenter.AfterLayout?.Invoke(layout);
} }
/// <inheritdoc />
protected override void Deinitialize()
{
Editor = null;
_overrideEditor = null;
base.Deinitialize();
}
/// <inheritdoc /> /// <inheritdoc />
protected override void OnModified() protected override void OnModified()
{ {

View File

@@ -205,7 +205,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
if (_linkedPrefabId != Guid.Empty) if (_linkedPrefabId != Guid.Empty)
{ {
_linkedPrefabId = Guid.Empty; _linkedPrefabId = Guid.Empty;
Editor.Instance.Prefabs.PrefabApplied -= OnPrefabApplying; Editor.Instance.Prefabs.PrefabApplying -= OnPrefabApplying;
Editor.Instance.Prefabs.PrefabApplied -= OnPrefabApplied; Editor.Instance.Prefabs.PrefabApplied -= OnPrefabApplied;
} }
} }

View File

@@ -1057,6 +1057,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
protected override void Deinitialize() protected override void Deinitialize()
{ {
_scriptToggles = null; _scriptToggles = null;
_scripts.Clear();
base.Deinitialize(); base.Deinitialize();
} }

View File

@@ -819,6 +819,15 @@ namespace FlaxEditor.CustomEditors.Editors
OnGroupsEnd(); OnGroupsEnd();
} }
/// <inheritdoc />
protected override void Deinitialize()
{
_visibleIfCaches = null;
_visibleIfPropertiesListsCache = null;
base.Deinitialize();
}
/// <inheritdoc /> /// <inheritdoc />
public override void Refresh() public override void Refresh()
{ {

View File

@@ -464,7 +464,7 @@ namespace FlaxEditor.GUI.Docking
{ {
base.Focus(); base.Focus();
SelectTab(); SelectTab(false);
BringToFront(); BringToFront();
} }

View File

@@ -21,6 +21,7 @@ namespace FlaxEditor.Modules
private bool _enableEvents; private bool _enableEvents;
private bool _isDuringFastSetup; private bool _isDuringFastSetup;
private bool _rebuildFlag; private bool _rebuildFlag;
private bool _rebuildInitFlag;
private int _itemsCreated; private int _itemsCreated;
private int _itemsDeleted; private int _itemsDeleted;
private readonly HashSet<MainContentTreeNode> _dirtyNodes = new HashSet<MainContentTreeNode>(); private readonly HashSet<MainContentTreeNode> _dirtyNodes = new HashSet<MainContentTreeNode>();
@@ -61,7 +62,7 @@ namespace FlaxEditor.Modules
public event Action WorkspaceModified; public event Action WorkspaceModified;
/// <summary> /// <summary>
/// Occurs when workspace has will be rebuilt. /// Occurs when workspace will be rebuilt.
/// </summary> /// </summary>
public event Action WorkspaceRebuilding; public event Action WorkspaceRebuilding;
@@ -88,6 +89,9 @@ namespace FlaxEditor.Modules
// Register AssetItems serialization helper (serialize ref ID only) // Register AssetItems serialization helper (serialize ref ID only)
FlaxEngine.Json.JsonSerializer.Settings.Converters.Add(new AssetItemConverter()); FlaxEngine.Json.JsonSerializer.Settings.Converters.Add(new AssetItemConverter());
ScriptsBuilder.ScriptsReload += OnScriptsReload;
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
} }
private void OnContentAssetDisposing(Asset asset) private void OnContentAssetDisposing(Asset asset)
@@ -817,6 +821,7 @@ namespace FlaxEditor.Modules
Profiler.BeginEvent("ContentDatabase.Rebuild"); Profiler.BeginEvent("ContentDatabase.Rebuild");
var startTime = Platform.TimeSeconds; var startTime = Platform.TimeSeconds;
_rebuildFlag = false; _rebuildFlag = false;
_rebuildInitFlag = false;
_enableEvents = false; _enableEvents = false;
// Load all folders // Load all folders
@@ -1230,8 +1235,6 @@ namespace FlaxEditor.Modules
LoadProjects(Game.Project); LoadProjects(Game.Project);
} }
RebuildInternal();
Editor.ContentImporting.ImportFileEnd += (obj, failed) => Editor.ContentImporting.ImportFileEnd += (obj, failed) =>
{ {
var path = obj.ResultUrl; var path = obj.ResultUrl;
@@ -1239,6 +1242,15 @@ namespace FlaxEditor.Modules
FlaxEngine.Scripting.InvokeOnUpdate(() => OnImportFileDone(path)); FlaxEngine.Scripting.InvokeOnUpdate(() => OnImportFileDone(path));
}; };
_enableEvents = true; _enableEvents = true;
_rebuildInitFlag = true;
}
/// <inheritdoc />
public override void OnEndInit()
{
// Handle init when project was loaded without scripts loading ()
if (_rebuildInitFlag)
RebuildInternal();
} }
private void OnImportFileDone(string path) private void OnImportFileDone(string path)
@@ -1313,6 +1325,52 @@ namespace FlaxEditor.Modules
} }
} }
private void OnScriptsReload()
{
var enabledEvents = _enableEvents;
_enableEvents = false;
_isDuringFastSetup = true;
var startItems = _itemsCreated;
foreach (var project in Projects)
{
if (project.Content != null)
{
//Dispose(project.Content.Folder);
for (int i = 0; i < project.Content.Folder.Children.Count; i++)
{
Dispose(project.Content.Folder.Children[i]);
i--;
}
}
if (project.Source != null)
{
//Dispose(project.Source.Folder);
for (int i = 0; i < project.Source.Folder.Children.Count; i++)
{
Dispose(project.Source.Folder.Children[i]);
i--;
}
}
}
List<ContentProxy> removeProxies = new List<ContentProxy>();
foreach (var proxy in Editor.Instance.ContentDatabase.Proxy)
{
if (proxy.GetType().IsCollectible)
removeProxies.Add(proxy);
}
foreach (var proxy in removeProxies)
RemoveProxy(proxy, false);
_isDuringFastSetup = false;
_enableEvents = enabledEvents;
}
private void OnScriptsReloadEnd()
{
RebuildInternal();
}
/// <inheritdoc /> /// <inheritdoc />
public override void OnUpdate() public override void OnUpdate()
{ {
@@ -1340,6 +1398,8 @@ namespace FlaxEditor.Modules
public override void OnExit() public override void OnExit()
{ {
FlaxEngine.Content.AssetDisposing -= OnContentAssetDisposing; FlaxEngine.Content.AssetDisposing -= OnContentAssetDisposing;
ScriptsBuilder.ScriptsReload -= OnScriptsReload;
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
// Disable events // Disable events
_enableEvents = false; _enableEvents = false;

View File

@@ -391,6 +391,20 @@ namespace FlaxEditor.Modules
public override void OnInit() public override void OnInit()
{ {
ImportFileEntry.RegisterDefaultTypes(); ImportFileEntry.RegisterDefaultTypes();
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
}
private void OnScriptsReloadBegin()
{
// Remove import file types from scripting assemblies
List<string> removeFileTypes = new List<string>();
foreach (var pair in ImportFileEntry.FileTypes)
{
if (pair.Value.Method.IsCollectible || (pair.Value.Target != null && pair.Value.Target.GetType().IsCollectible))
removeFileTypes.Add(pair.Key);
}
foreach (var fileType in removeFileTypes)
ImportFileEntry.FileTypes.Remove(fileType);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -451,6 +465,7 @@ namespace FlaxEditor.Modules
/// <inheritdoc /> /// <inheritdoc />
public override void OnExit() public override void OnExit()
{ {
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
EndWorker(); EndWorker();
} }
} }

View File

@@ -58,7 +58,7 @@ namespace FlaxEditor.Modules
: base(editor) : base(editor)
{ {
// After editor cache but before the windows // After editor cache but before the windows
InitOrder = -900; InitOrder = -800;
} }
/// <summary> /// <summary>

View File

@@ -160,7 +160,7 @@ namespace FlaxEditor.Modules
internal UIModule(Editor editor) internal UIModule(Editor editor)
: base(editor) : base(editor)
{ {
InitOrder = -90; InitOrder = -70;
VisjectSurfaceBackground = FlaxEngine.Content.LoadAsyncInternal<Texture>("Editor/VisjectSurface"); VisjectSurfaceBackground = FlaxEngine.Content.LoadAsyncInternal<Texture>("Editor/VisjectSurface");
ColorValueBox.ShowPickColorDialog += ShowPickColorDialog; ColorValueBox.ShowPickColorDialog += ShowPickColorDialog;
} }

View File

@@ -5,10 +5,12 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using FlaxEditor.Content; using FlaxEditor.Content;
using FlaxEditor.GUI.Dialogs; using FlaxEditor.GUI.Dialogs;
using FlaxEditor.GUI.Docking;
using FlaxEditor.Windows; using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets; using FlaxEditor.Windows.Assets;
using FlaxEditor.Windows.Profiler; using FlaxEditor.Windows.Profiler;
@@ -39,6 +41,7 @@ namespace FlaxEditor.Modules
public DockState DockState; public DockState DockState;
public DockPanel DockedTo; public DockPanel DockedTo;
public int DockedTabIndex;
public float? SplitterValue = null; public float? SplitterValue = null;
public bool SelectOnShow = false; public bool SelectOnShow = false;
@@ -48,6 +51,8 @@ namespace FlaxEditor.Modules
public Float2 FloatSize; public Float2 FloatSize;
public Float2 FloatPosition; public Float2 FloatPosition;
public Guid AssetItemID;
// Constructor, to allow for default values // Constructor, to allow for default values
public WindowRestoreData() public WindowRestoreData()
{ {
@@ -803,43 +808,64 @@ namespace FlaxEditor.Modules
Level.SceneSaving += OnSceneSaving; Level.SceneSaving += OnSceneSaving;
Level.SceneUnloaded += OnSceneUnloaded; Level.SceneUnloaded += OnSceneUnloaded;
Level.SceneUnloading += OnSceneUnloading; Level.SceneUnloading += OnSceneUnloading;
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd; Editor.ContentDatabase.WorkspaceRebuilt += OnWorkspaceRebuilt;
Editor.StateMachine.StateChanged += OnEditorStateChanged; Editor.StateMachine.StateChanged += OnEditorStateChanged;
} }
internal void AddToRestore(AssetEditorWindow win)
{
AddToRestore(win, win.GetType(), new WindowRestoreData
{
AssetItemID = win.Item.ID,
});
}
internal void AddToRestore(CustomEditorWindow win) internal void AddToRestore(CustomEditorWindow win)
{ {
var type = win.GetType(); AddToRestore(win.Window, win.GetType(), new WindowRestoreData());
}
private void AddToRestore(EditorWindow win, Type type, WindowRestoreData winData)
{
// Validate if can restore type // Validate if can restore type
var constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); var constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
if (constructor == null || type.IsGenericType) if (constructor == null || type.IsGenericType)
return; return;
var winData = new WindowRestoreData(); var panel = win.ParentDockPanel;
var panel = win.Window.ParentDockPanel;
// Ensure that this window is only selected following recompilation // Ensure that this window is only selected following recompilation
// if it was the active tab in its dock panel. Otherwise, there is a // if it was the active tab in its dock panel. Otherwise, there is a
// risk of interrupting the user's workflow by potentially selecting // risk of interrupting the user's workflow by potentially selecting
// background tabs. // background tabs.
winData.SelectOnShow = panel.SelectedTab == win.Window; var window = win.RootWindow.Window;
if (panel is FloatWindowDockPanel) winData.SelectOnShow = panel.SelectedTab == win;
winData.DockedTabIndex = 0;
if (panel is FloatWindowDockPanel && window != null && panel.TabsCount == 1)
{ {
winData.DockState = DockState.Float; winData.DockState = DockState.Float;
var window = win.Window.RootWindow.Window;
winData.FloatPosition = window.Position; winData.FloatPosition = window.Position;
winData.FloatSize = window.ClientSize; winData.FloatSize = window.ClientSize;
winData.Maximize = window.IsMaximized; winData.Maximize = window.IsMaximized;
winData.Minimize = window.IsMinimized; winData.Minimize = window.IsMinimized;
winData.DockedTo = panel;
} }
else else
{ {
for (int i = 0; i < panel.Tabs.Count; i++)
{
if (panel.Tabs[i] == win)
{
winData.DockedTabIndex = i;
break;
}
}
if (panel.TabsCount > 1) if (panel.TabsCount > 1)
{ {
winData.DockState = DockState.DockFill; winData.DockState = DockState.DockFill;
winData.DockedTo = panel; winData.DockedTo = panel;
}else }
else
{ {
winData.DockState = panel.TryGetDockState(out var splitterValue); winData.DockState = panel.TryGetDockState(out var splitterValue);
winData.DockedTo = panel.ParentDockPanel; winData.DockedTo = panel.ParentDockPanel;
@@ -851,38 +877,93 @@ namespace FlaxEditor.Modules
_restoreWindows.Add(winData); _restoreWindows.Add(winData);
} }
private void OnScriptsReloadEnd() private void OnWorkspaceRebuilt()
{ {
for (int i = 0; i < _restoreWindows.Count; i++) // Go in reverse order to create floating Prefab windows first before docked windows
for (int i = _restoreWindows.Count - 1; i >= 0; i--)
{ {
var winData = _restoreWindows[i]; var winData = _restoreWindows[i];
try try
{ {
var assembly = Utils.GetAssemblyByName(winData.AssemblyName); var assembly = Utils.GetAssemblyByName(winData.AssemblyName);
if (assembly != null) if (assembly == null)
continue;
var type = assembly.GetType(winData.TypeName);
if (type == null)
continue;
if (type.IsAssignableTo(typeof(AssetEditorWindow)))
{ {
var type = assembly.GetType(winData.TypeName); var ctor = type.GetConstructor(new Type[] { typeof(Editor), typeof(AssetItem) });
if (type != null) var assetItem = Editor.ContentDatabase.FindAsset(winData.AssetItemID);
var win = (AssetEditorWindow)ctor.Invoke(new object[] { Editor.Instance, assetItem });
win.Show(winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue);
if (winData.DockState == DockState.Float)
{ {
var win = (CustomEditorWindow)Activator.CreateInstance(type); var window = win.RootWindow.Window;
win.Show(winData.DockState, winData.DockedTo, winData.SelectOnShow, winData.SplitterValue); window.Position = winData.FloatPosition;
if (winData.DockState == DockState.Float) if (winData.Maximize)
{ {
var window = win.Window.RootWindow.Window; window.Maximize();
window.Position = winData.FloatPosition; }
if (winData.Maximize) else if (winData.Minimize)
{ {
window.Maximize(); window.Minimize();
} }
else if (winData.Minimize) else
{ {
window.Minimize(); window.ClientSize = winData.FloatSize;
} }
else
{ // Update panel reference in other windows docked to this panel
window.ClientSize = winData.FloatSize; foreach (ref var otherData in CollectionsMarshal.AsSpan(_restoreWindows))
} {
if (otherData.DockedTo == winData.DockedTo)
otherData.DockedTo = win.ParentDockPanel;
}
}
var panel = win.ParentDockPanel;
int currentTabIndex = 0;
for (int pi = 0; pi < panel.TabsCount; pi++)
{
if (panel.Tabs[pi] == win)
{
currentTabIndex = pi;
break;
}
}
while (currentTabIndex > winData.DockedTabIndex)
{
win.ParentDockPanel.MoveTabLeft(currentTabIndex);
currentTabIndex--;
}
while (currentTabIndex < winData.DockedTabIndex)
{
win.ParentDockPanel.MoveTabRight(currentTabIndex);
currentTabIndex++;
}
}
else
{
var win = (CustomEditorWindow)Activator.CreateInstance(type);
win.Show(winData.DockState, winData.DockedTo, winData.SelectOnShow, winData.SplitterValue);
if (winData.DockState == DockState.Float)
{
var window = win.Window.RootWindow.Window;
window.Position = winData.FloatPosition;
if (winData.Maximize)
{
window.Maximize();
}
else if (winData.Minimize)
{
window.Minimize();
}
else
{
window.ClientSize = winData.FloatSize;
} }
} }
} }
@@ -893,6 +974,11 @@ namespace FlaxEditor.Modules
Editor.LogWarning(string.Format("Failed to restore window {0} (assembly: {1})", winData.TypeName, winData.AssemblyName)); Editor.LogWarning(string.Format("Failed to restore window {0} (assembly: {1})", winData.TypeName, winData.AssemblyName));
} }
} }
// Restored windows stole the focus from Editor
if (_restoreWindows.Count > 0)
Editor.Instance.Windows.MainWindow.Focus();
_restoreWindows.Clear(); _restoreWindows.Clear();
} }
@@ -1048,7 +1134,7 @@ namespace FlaxEditor.Modules
Level.SceneSaving -= OnSceneSaving; Level.SceneSaving -= OnSceneSaving;
Level.SceneUnloaded -= OnSceneUnloaded; Level.SceneUnloaded -= OnSceneUnloaded;
Level.SceneUnloading -= OnSceneUnloading; Level.SceneUnloading -= OnSceneUnloading;
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd; Editor.ContentDatabase.WorkspaceRebuilt -= OnWorkspaceRebuilt;
Editor.StateMachine.StateChanged -= OnEditorStateChanged; Editor.StateMachine.StateChanged -= OnEditorStateChanged;
// Close main window // Close main window

View File

@@ -469,6 +469,7 @@ namespace FlaxEditor.SceneGraph
{ {
ChildNodes[i].OnDispose(); ChildNodes[i].OnDispose();
} }
ChildNodes.Clear();
SceneGraphFactory.Nodes.Remove(ID); SceneGraphFactory.Nodes.Remove(ID);
} }

View File

@@ -161,6 +161,8 @@ namespace FlaxEditor.Surface
private void OnScriptsReloadBegin() private void OnScriptsReloadBegin()
{ {
_nodesCache.Clear();
// Check if any of the nodes comes from the game scripts - those can be reloaded at runtime so prevent crashes // Check if any of the nodes comes from the game scripts - those can be reloaded at runtime so prevent crashes
bool hasTypeFromGameScripts = Editor.Instance.CodeEditing.AnimGraphNodes.HasTypeFromGameScripts; bool hasTypeFromGameScripts = Editor.Instance.CodeEditing.AnimGraphNodes.HasTypeFromGameScripts;

View File

@@ -24,6 +24,7 @@ namespace FlaxEditor.Surface
public BehaviorTreeSurface(IVisjectSurfaceOwner owner, Action onSave, FlaxEditor.Undo undo) public BehaviorTreeSurface(IVisjectSurfaceOwner owner, Action onSave, FlaxEditor.Undo undo)
: base(owner, onSave, undo, CreateStyle()) : base(owner, onSave, undo, CreateStyle())
{ {
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
} }
private static SurfaceStyle CreateStyle() private static SurfaceStyle CreateStyle()
@@ -35,6 +36,11 @@ namespace FlaxEditor.Surface
return style; return style;
} }
private void OnScriptsReloadBegin()
{
_nodesCache.Clear();
}
private static void DrawBox(Box box) private static void DrawBox(Box box)
{ {
var rect = new Rectangle(Float2.Zero, box.Size); var rect = new Rectangle(Float2.Zero, box.Size);
@@ -186,6 +192,7 @@ namespace FlaxEditor.Surface
{ {
if (IsDisposing) if (IsDisposing)
return; return;
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
_nodesCache.Wait(); _nodesCache.Wait();
base.OnDestroy(); base.OnDestroy();

View File

@@ -415,6 +415,15 @@ namespace FlaxEditor.Surface
// Init drag handlers // Init drag handlers
DragHandlers.Add(_dragAssets = new DragAssets<DragDropEventArgs>(ValidateDragItem)); DragHandlers.Add(_dragAssets = new DragAssets<DragDropEventArgs>(ValidateDragItem));
DragHandlers.Add(_dragParameters = new DragNames<DragDropEventArgs>(SurfaceParameter.DragPrefix, ValidateDragParameter)); DragHandlers.Add(_dragParameters = new DragNames<DragDropEventArgs>(SurfaceParameter.DragPrefix, ValidateDragParameter));
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
}
private void OnScriptsReloadBegin()
{
_activeVisjectCM = null;
_cmPrimaryMenu?.Dispose();
_cmPrimaryMenu = null;
} }
/// <summary> /// <summary>
@@ -1023,6 +1032,8 @@ namespace FlaxEditor.Surface
_activeVisjectCM = null; _activeVisjectCM = null;
_cmPrimaryMenu?.Dispose(); _cmPrimaryMenu?.Dispose();
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
base.OnDestroy(); base.OnDestroy();
} }
} }

View File

@@ -62,6 +62,12 @@ namespace FlaxEditor.Surface
{ {
_supportsImplicitCastFromObjectToBoolean = true; _supportsImplicitCastFromObjectToBoolean = true;
DragHandlers.Add(_dragActors = new DragActors(ValidateDragActor)); DragHandlers.Add(_dragActors = new DragActors(ValidateDragActor));
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
}
private void OnScriptsReloadBegin()
{
_nodesCache.Clear();
} }
private bool ValidateDragActor(ActorNode actor) private bool ValidateDragActor(ActorNode actor)
@@ -631,6 +637,7 @@ namespace FlaxEditor.Surface
{ {
if (IsDisposing) if (IsDisposing)
return; return;
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
_nodesCache.Wait(); _nodesCache.Wait();
base.OnDestroy(); base.OnDestroy();

View File

@@ -58,6 +58,8 @@ namespace FlaxEditor.Windows.Assets
InputActions.Add(options => options.Save, Save); InputActions.Add(options => options.Save, Save);
UpdateTitle(); UpdateTitle();
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
} }
/// <summary> /// <summary>
@@ -151,6 +153,8 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc /> /// <inheritdoc />
public override void OnDestroy() public override void OnDestroy()
{ {
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
if (_item != null) if (_item != null)
{ {
// Ensure to remove linkage to the item // Ensure to remove linkage to the item
@@ -160,6 +164,15 @@ namespace FlaxEditor.Windows.Assets
base.OnDestroy(); base.OnDestroy();
} }
/// <inheritdoc />
protected virtual void OnScriptsReloadBegin()
{
if (!IsHidden)
{
Editor.Instance.Windows.AddToRestore(this);
}
}
#region IEditable Implementation #region IEditable Implementation
private bool _isEdited; private bool _isEdited;

View File

@@ -268,8 +268,11 @@ namespace FlaxEditor.Windows.Assets
UpdateKnowledge(); UpdateKnowledge();
} }
private void OnScriptsReloadBegin() /// <inheritdoc />
protected override void OnScriptsReloadBegin()
{ {
base.OnScriptsReloadBegin();
// TODO: impl hot-reload for BT to nicely refresh state (save asset, clear undo/properties, reload surface) // TODO: impl hot-reload for BT to nicely refresh state (save asset, clear undo/properties, reload surface)
Close(); Close();
} }

View File

@@ -124,8 +124,10 @@ namespace FlaxEditor.Windows.Assets
UpdateToolstrip(); UpdateToolstrip();
} }
private void OnScriptsReloadBegin() /// <inheritdoc />
protected override void OnScriptsReloadBegin()
{ {
base.OnScriptsReloadBegin();
Close(); Close();
} }

View File

@@ -198,7 +198,6 @@ namespace FlaxEditor.Windows.Assets
Editor.Prefabs.PrefabApplied += OnPrefabApplied; Editor.Prefabs.PrefabApplied += OnPrefabApplied;
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin; ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
// Setup input actions // Setup input actions
InputActions.Add(options => options.Undo, () => InputActions.Add(options => options.Undo, () =>
@@ -291,8 +290,10 @@ namespace FlaxEditor.Windows.Assets
return false; return false;
} }
private void OnScriptsReloadBegin() /// <inheritdoc />
protected override void OnScriptsReloadBegin()
{ {
base.OnScriptsReloadBegin();
_isScriptsReloading = true; _isScriptsReloading = true;
if (_asset == null || !_asset.IsLoaded) if (_asset == null || !_asset.IsLoaded)
@@ -320,19 +321,8 @@ namespace FlaxEditor.Windows.Assets
Graph.MainActor = null; Graph.MainActor = null;
_viewport.Prefab = null; _viewport.Prefab = null;
_undo?.Clear(); // TODO: maybe don't clear undo? _undo?.Clear(); // TODO: maybe don't clear undo?
}
private void OnScriptsReloadEnd() Close();
{
_isScriptsReloading = false;
if (_asset == null || !_asset.IsLoaded)
return;
// Restore
OnPrefabOpened();
_undo.Clear();
ClearEditedFlag();
} }
private void OnUndoEvent(IUndoAction action) private void OnUndoEvent(IUndoAction action)
@@ -551,7 +541,6 @@ namespace FlaxEditor.Windows.Assets
{ {
Editor.Prefabs.PrefabApplied -= OnPrefabApplied; Editor.Prefabs.PrefabApplied -= OnPrefabApplied;
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin; ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
_undo.Dispose(); _undo.Dispose();
Graph.Dispose(); Graph.Dispose();

View File

@@ -29,6 +29,7 @@ namespace FlaxEditor.Windows
private const string ProjectDataLastViewedFolder = "LastViewedFolder"; private const string ProjectDataLastViewedFolder = "LastViewedFolder";
private bool _isWorkspaceDirty; private bool _isWorkspaceDirty;
private string _workspaceRebuildLocation; private string _workspaceRebuildLocation;
private string _lastViewedFolderBeforeReload;
private SplitPanel _split; private SplitPanel _split;
private Panel _contentViewPanel; private Panel _contentViewPanel;
private Panel _contentTreePanel; private Panel _contentTreePanel;
@@ -144,26 +145,6 @@ namespace FlaxEditor.Windows
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this); FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
// Content database events
editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true;
editor.ContentDatabase.ItemRemoved += OnContentDatabaseItemRemoved;
editor.ContentDatabase.WorkspaceRebuilding += () => { _workspaceRebuildLocation = SelectedNode?.Path; };
editor.ContentDatabase.WorkspaceRebuilt += () =>
{
var selected = Editor.ContentDatabase.Find(_workspaceRebuildLocation);
if (selected is ContentFolder selectedFolder)
{
_navigationUnlocked = false;
RefreshView(selectedFolder.Node);
_tree.Select(selectedFolder.Node);
UpdateItemsSearch();
_navigationUnlocked = true;
UpdateUI();
}
else
ShowRoot();
};
var options = Editor.Options; var options = Editor.Options;
options.OptionsChanged += OnOptionsChanged; options.OptionsChanged += OnOptionsChanged;
@@ -1036,6 +1017,61 @@ namespace FlaxEditor.Windows
/// <inheritdoc /> /// <inheritdoc />
public override void OnInit() public override void OnInit()
{
// Content database events
Editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true;
Editor.ContentDatabase.ItemRemoved += OnContentDatabaseItemRemoved;
Editor.ContentDatabase.WorkspaceRebuilding += () => { _workspaceRebuildLocation = SelectedNode?.Path; };
Editor.ContentDatabase.WorkspaceRebuilt += () =>
{
var selected = Editor.ContentDatabase.Find(_workspaceRebuildLocation);
if (selected is ContentFolder selectedFolder)
{
_navigationUnlocked = false;
RefreshView(selectedFolder.Node);
_tree.Select(selectedFolder.Node);
UpdateItemsSearch();
_navigationUnlocked = true;
UpdateUI();
}
else if (_root != null)
ShowRoot();
};
Refresh();
// Load last viewed folder
if (Editor.ProjectCache.TryGetCustomData(ProjectDataLastViewedFolder, out string lastViewedFolder))
{
if (Editor.ContentDatabase.Find(lastViewedFolder) is ContentFolder folder)
_tree.Select(folder.Node);
}
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
}
private void OnScriptsReloadBegin()
{
var lastViewedFolder = _tree.Selection.Count == 1 ? _tree.SelectedNode as ContentTreeNode : null;
_lastViewedFolderBeforeReload = lastViewedFolder?.Path ?? string.Empty;
_tree.RemoveChild(_root);
_root = null;
}
private void OnScriptsReloadEnd()
{
Refresh();
if (!string.IsNullOrEmpty(_lastViewedFolderBeforeReload))
{
if (Editor.ContentDatabase.Find(_lastViewedFolderBeforeReload) is ContentFolder folder)
_tree.Select(folder.Node);
}
}
private void Refresh()
{ {
// Setup content root node // Setup content root node
_root = new RootContentTreeNode _root = new RootContentTreeNode
@@ -1072,13 +1108,6 @@ namespace FlaxEditor.Windows
// Update UI layout // Update UI layout
_isLayoutLocked = false; _isLayoutLocked = false;
PerformLayout(); PerformLayout();
// Load last viewed folder
if (Editor.ProjectCache.TryGetCustomData(ProjectDataLastViewedFolder, out string lastViewedFolder))
{
if (Editor.ContentDatabase.Find(lastViewedFolder) is ContentFolder folder)
_tree.Select(folder.Node);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -1226,6 +1255,8 @@ namespace FlaxEditor.Windows
_viewDropdown = null; _viewDropdown = null;
Editor.Options.OptionsChanged -= OnOptionsChanged; Editor.Options.OptionsChanged -= OnOptionsChanged;
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
base.OnDestroy(); base.OnDestroy();
} }

View File

@@ -150,6 +150,22 @@ namespace FlaxEditor.Windows
_searchBox.Clear(); _searchBox.Clear();
_groupSearch.DisposeChildren(); _groupSearch.DisposeChildren();
_groupSearch.PerformLayout(); _groupSearch.PerformLayout();
// Remove tabs
var tabs = new List<Tab>();
foreach (var child in _actorGroups.Children)
{
if (child is Tab tab)
{
if (tab.Text != "Search")
tabs.Add(tab);
}
}
foreach (var tab in tabs)
{
var group = _actorGroups.Children.Find(T => T == tab);
group.Dispose();
}
} }
private void OnScriptsReloadEnd() private void OnScriptsReloadEnd()

View File

@@ -176,6 +176,7 @@ namespace FlaxEngine.Interop
_managedHandle.Free(); _managedHandle.Free();
_unmanagedData = IntPtr.Zero; _unmanagedData = IntPtr.Zero;
} }
_arrayType = _elementType = null;
ManagedArrayPool.Put(this); ManagedArrayPool.Put(this);
} }
@@ -442,22 +443,25 @@ namespace FlaxEngine.Interop
/// <summary> /// <summary>
/// Tries to free all references to old weak handles so GC can collect them. /// Tries to free all references to old weak handles so GC can collect them.
/// </summary> /// </summary>
internal static void TryCollectWeakHandles() internal static void TryCollectWeakHandles(bool force = false)
{ {
if (weakHandleAccumulator < nextWeakPoolCollection) if (!force)
return; {
if (weakHandleAccumulator < nextWeakPoolCollection)
return;
nextWeakPoolCollection = weakHandleAccumulator + 1000; nextWeakPoolCollection = weakHandleAccumulator + 1000;
// Try to swap pools after garbage collection or whenever the pool gets too large // Try to swap pools after garbage collection or whenever the pool gets too large
var gc0CollectionCount = GC.CollectionCount(0); var gc0CollectionCount = GC.CollectionCount(0);
if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold) if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold)
return; return;
nextWeakPoolGCCollection = gc0CollectionCount + 1; nextWeakPoolGCCollection = gc0CollectionCount + 1;
// Prevent huge allocations from swapping the pools in the middle of the operation // Prevent huge allocations from swapping the pools in the middle of the operation
if (System.Diagnostics.Stopwatch.GetElapsedTime(lastWeakPoolCollectionTime).TotalMilliseconds < WeakPoolCollectionTimeThreshold) if (System.Diagnostics.Stopwatch.GetElapsedTime(lastWeakPoolCollectionTime).TotalMilliseconds < WeakPoolCollectionTimeThreshold)
return; return;
}
lastWeakPoolCollectionTime = System.Diagnostics.Stopwatch.GetTimestamp(); lastWeakPoolCollectionTime = System.Diagnostics.Stopwatch.GetTimestamp();
// Swap the pools and release the oldest pool for GC // Swap the pools and release the oldest pool for GC

View File

@@ -1054,7 +1054,49 @@ namespace FlaxEngine.Interop
} }
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
internal static void ReloadScriptingAssemblyLoadContext() internal static void CreateScriptingAssemblyLoadContext()
{
#if FLAX_EDITOR
if (scriptingAssemblyLoadContext != null)
{
// Wait for previous ALC to finish unloading, track it without holding strong references to it
GCHandle weakRef = GCHandle.Alloc(scriptingAssemblyLoadContext, GCHandleType.WeakTrackResurrection);
scriptingAssemblyLoadContext = null;
#if true
// In case the ALC doesn't unload properly: https://learn.microsoft.com/en-us/dotnet/standard/assembly/unloadability#debug-unloading-issues
while (true)
#else
for (int attempts = 5; attempts > 0; attempts--)
#endif
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (!IsHandleAlive(weakRef))
break;
System.Threading.Thread.Sleep(1);
}
if (IsHandleAlive(weakRef))
Debug.LogWarning("Scripting AssemblyLoadContext was not unloaded.");
weakRef.Free();
static bool IsHandleAlive(GCHandle weakRef)
{
// Checking the target in scope somehow holds a reference to it...?
return weakRef.Target != null;
}
}
scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible: true);
scriptingAssemblyLoadContext.Resolving += OnScriptingAssemblyLoadContextResolving;
#else
scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible: false);
#endif
DelegateHelpers.InitMethods();
}
[UnmanagedCallersOnly]
internal static void UnloadScriptingAssemblyLoadContext()
{ {
#if FLAX_EDITOR #if FLAX_EDITOR
// Clear all caches which might hold references to assemblies in collectible ALC // Clear all caches which might hold references to assemblies in collectible ALC
@@ -1072,24 +1114,87 @@ namespace FlaxEngine.Interop
handle.Free(); handle.Free();
propertyHandleCacheCollectible.Clear(); propertyHandleCacheCollectible.Clear();
foreach (var key in assemblyHandles.Keys.Where(x => x.IsCollectible))
assemblyHandles.Remove(key);
foreach (var key in assemblyOwnedNativeLibraries.Keys.Where(x => x.IsCollectible))
assemblyOwnedNativeLibraries.Remove(key);
_typeSizeCache.Clear(); _typeSizeCache.Clear();
foreach (var pair in classAttributesCacheCollectible) foreach (var pair in classAttributesCacheCollectible)
pair.Value.Free(); pair.Value.Free();
classAttributesCacheCollectible.Clear(); classAttributesCacheCollectible.Clear();
ArrayFactory.marshalledTypes.Clear();
ArrayFactory.arrayTypes.Clear();
ArrayFactory.createArrayDelegates.Clear();
FlaxEngine.Json.JsonSerializer.ResetCache(); FlaxEngine.Json.JsonSerializer.ResetCache();
DelegateHelpers.Release();
// Ensure both pools are empty
ManagedHandle.ManagedHandlePool.TryCollectWeakHandles(true);
ManagedHandle.ManagedHandlePool.TryCollectWeakHandles(true);
GC.Collect();
GC.WaitForPendingFinalizers();
{
// HACK: Workaround for TypeDescriptor holding references to collectible types (https://github.com/dotnet/runtime/issues/30656)
Type TypeDescriptionProviderType = typeof(System.ComponentModel.TypeDescriptionProvider);
MethodInfo clearCacheMethod = TypeDescriptionProviderType?.Assembly.GetType("System.ComponentModel.ReflectionCachesUpdateHandler")?.GetMethod("ClearCache");
if (clearCacheMethod != null)
clearCacheMethod.Invoke(null, new object[] { null });
Type TypeDescriptorType = typeof(System.ComponentModel.TypeDescriptor);
object s_providerTable = TypeDescriptorType?.GetField("s_providerTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
// Added in .NET runtime 8.0.10, used as the main locking object
object s_commonSyncObject = TypeDescriptorType?.GetField("s_commonSyncObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
if (s_commonSyncObject == null)
s_commonSyncObject = s_providerTable;
// Removed in .NET runtime 8.0.7
object s_internalSyncObject = TypeDescriptorType?.GetField("s_internalSyncObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
object s_defaultProviders = TypeDescriptorType?.GetField("s_defaultProviders", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
if (s_internalSyncObject != null && s_defaultProviders != null)
{
lock (s_internalSyncObject)
InvokeClear(s_defaultProviders);
}
// Replaces s_defaultProviders in 8.0.7
object s_defaultProviderInitialized = TypeDescriptorType?.GetField("s_defaultProviderInitialized", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
if (s_commonSyncObject != null && s_defaultProviderInitialized != null)
{
lock (s_commonSyncObject)
InvokeClear(s_defaultProviderInitialized);
}
object s_providerTypeTable = TypeDescriptorType?.GetField("s_providerTypeTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
if (s_providerTable != null && s_providerTypeTable != null)
{
lock (s_commonSyncObject)
InvokeClear(s_providerTypeTable);
InvokeClear(s_providerTable);
}
static void InvokeClear(object instance)
{
Type type = instance.GetType();
FlaxEngine.Assertions.Assert.IsTrue(type.Name == "ConcurrentDictionary`2" || type.Name == "Hashtable" || type.Name == "WeakHashtable");
type.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(x => x.Name == "Clear")
?.Invoke(instance, new object[] { });
}
}
// Unload the ALC // Unload the ALC
bool unloading = true;
scriptingAssemblyLoadContext.Unloading += (alc) => { unloading = false; };
scriptingAssemblyLoadContext.Unload(); scriptingAssemblyLoadContext.Unload();
scriptingAssemblyLoadContext.Resolving -= OnScriptingAssemblyLoadContextResolving;
while (unloading) GC.Collect();
System.Threading.Thread.Sleep(1); GC.WaitForPendingFinalizers();
InitScriptingAssemblyLoadContext();
DelegateHelpers.InitMethods();
#endif #endif
} }

View File

@@ -73,19 +73,6 @@ namespace FlaxEngine.Interop
return nativeLibrary; return nativeLibrary;
} }
private static void InitScriptingAssemblyLoadContext()
{
#if FLAX_EDITOR
var isCollectible = true;
#else
var isCollectible = false;
#endif
scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible);
#if FLAX_EDITOR
scriptingAssemblyLoadContext.Resolving += OnScriptingAssemblyLoadContextResolving;
#endif
}
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
internal static unsafe void Init() internal static unsafe void Init()
{ {
@@ -97,8 +84,6 @@ namespace FlaxEngine.Interop
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
InitScriptingAssemblyLoadContext();
DelegateHelpers.InitMethods();
} }
#if FLAX_EDITOR #if FLAX_EDITOR
@@ -1475,11 +1460,11 @@ namespace FlaxEngine.Interop
internal static class ArrayFactory internal static class ArrayFactory
{ {
private delegate Array CreateArrayDelegate(long size); internal delegate Array CreateArrayDelegate(long size);
private static ConcurrentDictionary<Type, Type> marshalledTypes = new ConcurrentDictionary<Type, Type>(1, 3); internal static ConcurrentDictionary<Type, Type> marshalledTypes = new ConcurrentDictionary<Type, Type>(1, 3);
private static ConcurrentDictionary<Type, Type> arrayTypes = new ConcurrentDictionary<Type, Type>(1, 3); internal static ConcurrentDictionary<Type, Type> arrayTypes = new ConcurrentDictionary<Type, Type>(1, 3);
private static ConcurrentDictionary<Type, CreateArrayDelegate> createArrayDelegates = new ConcurrentDictionary<Type, CreateArrayDelegate>(1, 3); internal static ConcurrentDictionary<Type, CreateArrayDelegate> createArrayDelegates = new ConcurrentDictionary<Type, CreateArrayDelegate>(1, 3);
internal static Type GetMarshalledType(Type elementType) internal static Type GetMarshalledType(Type elementType)
{ {
@@ -1645,17 +1630,6 @@ namespace FlaxEngine.Interop
return RegisterType(type, true).typeHolder; return RegisterType(type, true).typeHolder;
} }
internal static (TypeHolder typeHolder, ManagedHandle handle) GetTypeHolderAndManagedHandle(Type type)
{
if (managedTypes.TryGetValue(type, out (TypeHolder typeHolder, ManagedHandle handle) tuple))
return tuple;
#if FLAX_EDITOR
if (managedTypesCollectible.TryGetValue(type, out tuple))
return tuple;
#endif
return RegisterType(type, true);
}
/// <summary> /// <summary>
/// Returns a static ManagedHandle to TypeHolder for given Type, and caches it if needed. /// Returns a static ManagedHandle to TypeHolder for given Type, and caches it if needed.
/// </summary> /// </summary>
@@ -1781,6 +1755,14 @@ namespace FlaxEngine.Interop
#endif #endif
} }
internal static void Release()
{
MakeNewCustomDelegateFunc = null;
#if FLAX_EDITOR
MakeNewCustomDelegateFuncCollectible = null;
#endif
}
internal static Type MakeNewCustomDelegate(Type[] parameters) internal static Type MakeNewCustomDelegate(Type[] parameters)
{ {
#if FLAX_EDITOR #if FLAX_EDITOR

View File

@@ -45,9 +45,16 @@ public:
/// </summary> /// </summary>
static void UnloadEngine(); static void UnloadEngine();
/// <summary>
/// Creates the assembly load context for assemblies used by Scripting.
/// </summary>
static void CreateScriptingAssemblyLoadContext();
#if USE_EDITOR #if USE_EDITOR
// Called by Scripting in a middle of hot-reload (after unloading modules but before loading them again). /// <summary>
static void ReloadScriptingAssemblyLoadContext(); /// Called by Scripting in a middle of hot-reload (after unloading modules but before loading them again).
/// </summary>
static void UnloadScriptingAssemblyLoadContext();
#endif #endif
public: public:

View File

@@ -330,9 +330,15 @@ void MCore::UnloadEngine()
ShutdownHostfxr(); ShutdownHostfxr();
} }
void MCore::CreateScriptingAssemblyLoadContext()
{
static void* CreateScriptingAssemblyLoadContextPtr = GetStaticMethodPointer(TEXT("CreateScriptingAssemblyLoadContext"));
CallStaticMethod<void>(CreateScriptingAssemblyLoadContextPtr);
}
#if USE_EDITOR #if USE_EDITOR
void MCore::ReloadScriptingAssemblyLoadContext() void MCore::UnloadScriptingAssemblyLoadContext()
{ {
// Clear any cached class attributes (see https://github.com/FlaxEngine/FlaxEngine/issues/1108) // Clear any cached class attributes (see https://github.com/FlaxEngine/FlaxEngine/issues/1108)
for (auto e : CachedClassHandles) for (auto e : CachedClassHandles)
@@ -377,8 +383,8 @@ void MCore::ReloadScriptingAssemblyLoadContext()
} }
} }
static void* ReloadScriptingAssemblyLoadContextPtr = GetStaticMethodPointer(TEXT("ReloadScriptingAssemblyLoadContext")); static void* UnloadScriptingAssemblyLoadContextPtr = GetStaticMethodPointer(TEXT("UnloadScriptingAssemblyLoadContext"));
CallStaticMethod<void>(ReloadScriptingAssemblyLoadContextPtr); CallStaticMethod<void>(UnloadScriptingAssemblyLoadContextPtr);
} }
#endif #endif

View File

@@ -715,9 +715,13 @@ void MCore::UnloadEngine()
#endif #endif
} }
void MCore::CreateScriptingAssemblyLoadContext()
{
}
#if USE_EDITOR #if USE_EDITOR
void MCore::ReloadScriptingAssemblyLoadContext() void MCore::UnloadScriptingAssemblyLoadContext()
{ {
} }

View File

@@ -58,9 +58,13 @@ void MCore::UnloadEngine()
MRootDomain = nullptr; MRootDomain = nullptr;
} }
void MCore::CreateScriptingAssemblyLoadContext()
{
}
#if USE_EDITOR #if USE_EDITOR
void MCore::ReloadScriptingAssemblyLoadContext() void MCore::UnloadScriptingAssemblyLoadContext()
{ {
} }

View File

@@ -182,6 +182,8 @@ bool ScriptingService::Init()
return true; return true;
} }
MCore::CreateScriptingAssemblyLoadContext();
// Cache root domain // Cache root domain
_rootDomain = MCore::GetRootDomain(); _rootDomain = MCore::GetRootDomain();
@@ -710,7 +712,8 @@ void Scripting::Reload(bool canTriggerSceneReload)
_hasGameModulesLoaded = false; _hasGameModulesLoaded = false;
// Release and create a new assembly load context for user assemblies // Release and create a new assembly load context for user assemblies
MCore::ReloadScriptingAssemblyLoadContext(); MCore::UnloadScriptingAssemblyLoadContext();
MCore::CreateScriptingAssemblyLoadContext();
// Give GC a try to cleanup old user objects and the other mess // Give GC a try to cleanup old user objects and the other mess
MCore::GC::Collect(); MCore::GC::Collect();

View File

@@ -170,6 +170,10 @@ namespace FlaxEngine
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
Localization.LocalizationChanged += OnLocalizationChanged; Localization.LocalizationChanged += OnLocalizationChanged;
#if FLAX_EDITOR
FlaxEditor.ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
FlaxEditor.ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
#endif
OnLocalizationChanged(); OnLocalizationChanged();
if (!Engine.IsEditor) if (!Engine.IsEditor)
@@ -178,6 +182,19 @@ namespace FlaxEngine
} }
} }
#if FLAX_EDITOR
private static void OnScriptsReloadBegin()
{
// Tooltip might hold references to scripting assemblies
Style.Current.SharedTooltip = null;
}
private static void OnScriptsReloadEnd()
{
Style.Current.SharedTooltip = new Tooltip();
}
#endif
private static void OnLocalizationChanged() private static void OnLocalizationChanged()
{ {
// Invariant-globalization only (see InitHostfxr with Mono) // Invariant-globalization only (see InitHostfxr with Mono)
@@ -359,6 +376,10 @@ namespace FlaxEngine
MainThreadTaskScheduler.Dispose(); MainThreadTaskScheduler.Dispose();
Json.JsonSerializer.Dispose(); Json.JsonSerializer.Dispose();
#if FLAX_EDITOR
FlaxEditor.ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
FlaxEditor.ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
#endif
} }
/// <summary> /// <summary>

View File

@@ -120,6 +120,7 @@ namespace FlaxEngine.GUI
// Unlink // Unlink
IsLayoutLocked = true; IsLayoutLocked = true;
Parent = null; Parent = null;
_showTarget = null;
// Close window // Close window
if (_window) if (_window)