mirror of
https://github.com/Facepunch/sbox-public.git
synced 2025-12-23 22:48:07 -05:00
Restore separate GameEditorSessions (#3453)
* SceneEditorSession: make game vs editor scenes more distinct, Scene is whichever is currently active * Route prefab update, model reload etc events to both scenes if needed * SceneTree update checking a bit cleaner * Bring back GameEditorSessions instead, so undo, selection etc can all be linked 1:1 with scenes again * tweak and tidy
This commit is contained in:
@@ -295,7 +295,7 @@ public static class EditorScene
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
SceneEditorSession.ActiveGameSession.StopPlaying();
|
||||
SceneEditorSession.Active.StopPlaying();
|
||||
|
||||
Game.IsPlaying = false;
|
||||
|
||||
|
||||
27
engine/Sandbox.Tools/Scene/Session/GameEditorSession.cs
Normal file
27
engine/Sandbox.Tools/Scene/Session/GameEditorSession.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
namespace Editor;
|
||||
|
||||
public class GameEditorSession : SceneEditorSession
|
||||
{
|
||||
internal static GameEditorSession Current = null;
|
||||
|
||||
public SceneEditorSession Parent { get; init; }
|
||||
|
||||
public override bool IsPlaying => true;
|
||||
|
||||
public GameEditorSession( SceneEditorSession parent, Scene scene ) : base( scene )
|
||||
{
|
||||
Parent = parent;
|
||||
|
||||
Assert.IsNull( Current, "Attempted to create new GameEditorSession when one already exists!" );
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public override void Destroy()
|
||||
{
|
||||
base.Destroy();
|
||||
|
||||
Current = null;
|
||||
}
|
||||
|
||||
public override void StopPlaying() => Parent.StopPlaying();
|
||||
}
|
||||
@@ -2,22 +2,24 @@
|
||||
|
||||
partial class SceneEditorSession
|
||||
{
|
||||
public Scene ActiveGameScene => activeGameScene;
|
||||
public bool HasActiveGameScene => activeGameScene != null;
|
||||
/// <summary>
|
||||
/// The game session of this editor session, if playing.
|
||||
/// </summary>
|
||||
public GameEditorSession GameSession { get; private set; }
|
||||
|
||||
public static SceneEditorSession ActiveGameSession => All.Where( s => s.HasActiveGameScene ).FirstOrDefault();
|
||||
|
||||
Scene activeGameScene;
|
||||
public virtual bool IsPlaying => GameSession != null;
|
||||
|
||||
public void SetPlaying( Scene scene )
|
||||
{
|
||||
activeGameScene = scene;
|
||||
activeGameScene.Editor = this;
|
||||
GameSession = new GameEditorSession( this, scene );
|
||||
GameSession.MakeActive();
|
||||
}
|
||||
|
||||
public void StopPlaying()
|
||||
public virtual void StopPlaying()
|
||||
{
|
||||
activeGameScene?.Destroy();
|
||||
activeGameScene = null;
|
||||
GameSession?.Destroy();
|
||||
GameSession = null;
|
||||
|
||||
MakeActive();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,20 +15,18 @@ public partial class SceneEditorSession : Scene.ISceneEditorSession
|
||||
/// </summary>
|
||||
public static List<SceneEditorSession> All { get; } = new();
|
||||
|
||||
private static SceneEditorSession _active;
|
||||
|
||||
/// <summary>
|
||||
/// The editor session that is currently active
|
||||
/// </summary>
|
||||
public static SceneEditorSession Active
|
||||
{
|
||||
get => _active;
|
||||
get;
|
||||
private set
|
||||
{
|
||||
if ( _active == value ) return;
|
||||
if ( field == value ) return;
|
||||
|
||||
_active = value;
|
||||
_active?.UpdateEditorTitle();
|
||||
field = value;
|
||||
field?.UpdateEditorTitle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +39,9 @@ public partial class SceneEditorSession : Scene.ISceneEditorSession
|
||||
/// </summary>
|
||||
public bool IsPrefabSession => this is PrefabEditorSession;
|
||||
|
||||
/// <summary>
|
||||
/// The scene for this session
|
||||
/// </summary>
|
||||
public Scene Scene { get; private set; }
|
||||
|
||||
internal Widget SceneDock { get; set; }
|
||||
@@ -57,7 +58,11 @@ public partial class SceneEditorSession : Scene.ISceneEditorSession
|
||||
InitUndo();
|
||||
timeSinceSavedState = 0;
|
||||
|
||||
CreateSceneDock();
|
||||
if ( this is not GameEditorSession )
|
||||
{
|
||||
// create dock - but not for game sessions, those are built into the parent session widget
|
||||
CreateSceneDock();
|
||||
}
|
||||
|
||||
EditorEvent.Register( this );
|
||||
}
|
||||
@@ -120,7 +125,7 @@ public partial class SceneEditorSession : Scene.ISceneEditorSession
|
||||
|
||||
bool _destroyed;
|
||||
|
||||
public void Destroy()
|
||||
public virtual void Destroy()
|
||||
{
|
||||
if ( _destroyed )
|
||||
return;
|
||||
@@ -149,6 +154,9 @@ public partial class SceneEditorSession : Scene.ISceneEditorSession
|
||||
Scene?.Destroy();
|
||||
Scene = null;
|
||||
|
||||
GameSession?.Destroy();
|
||||
GameSession = null;
|
||||
|
||||
SceneDock?.Destroy();
|
||||
SceneDock = default;
|
||||
}
|
||||
@@ -212,18 +220,6 @@ public partial class SceneEditorSession : Scene.ISceneEditorSession
|
||||
else
|
||||
{
|
||||
SceneDock.SetWindowIcon( "grid_4x4" );
|
||||
|
||||
//
|
||||
// Change the title to reflect current mode
|
||||
//
|
||||
if ( HasActiveGameScene )
|
||||
{
|
||||
SceneDock.WindowTitle = $"{title} (active)";
|
||||
}
|
||||
else
|
||||
{
|
||||
SceneDock.WindowTitle = title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +260,7 @@ public partial class SceneEditorSession : Scene.ISceneEditorSession
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes the active editor scene to the current scope
|
||||
/// Pushes the active scene to the current scope
|
||||
/// </summary>
|
||||
public static IDisposable Scope()
|
||||
{
|
||||
@@ -383,7 +379,7 @@ public partial class SceneEditorSession : Scene.ISceneEditorSession
|
||||
/// </summary>
|
||||
public static SceneEditorSession Resolve( Scene scene )
|
||||
{
|
||||
return All.FirstOrDefault( x => x.ActiveGameScene == scene || x.Scene == scene );
|
||||
return All.FirstOrDefault( x => x.Scene == scene );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -312,7 +312,7 @@ class ControlSheetRow : Widget
|
||||
return;
|
||||
|
||||
// Only show "Apply To Scene" if we're editing in an active game session
|
||||
if ( !session.HasActiveGameScene || component.GameObject.Scene != session.ActiveGameScene )
|
||||
if ( !session.IsPlaying || component.GameObject.Scene != session.Scene )
|
||||
return;
|
||||
|
||||
// try to find the version of this component in the editor session
|
||||
|
||||
@@ -90,7 +90,7 @@ public partial class SceneTreeWidget : Widget
|
||||
|
||||
Layout.Add( TreeView, 1 );
|
||||
|
||||
_lastSession = null;
|
||||
_lastScene.SetTarget( null );
|
||||
CheckForChanges();
|
||||
|
||||
EditorUtility.OnInspect -= OnInspect;
|
||||
@@ -125,32 +125,31 @@ public partial class SceneTreeWidget : Widget
|
||||
}
|
||||
}
|
||||
|
||||
SceneEditorSession _lastSession;
|
||||
WeakReference<Scene> _lastScene = new( null );
|
||||
bool queryDirty = false;
|
||||
bool hasActiveGameScene = false;
|
||||
|
||||
[EditorEvent.Frame]
|
||||
public void CheckForChanges()
|
||||
{
|
||||
var activeScene = SceneEditorSession.Active;
|
||||
if ( activeScene is null )
|
||||
var session = SceneEditorSession.Active;
|
||||
if ( session is null )
|
||||
return;
|
||||
|
||||
if ( hasActiveGameScene == activeScene.HasActiveGameScene )
|
||||
{
|
||||
if ( !queryDirty && _lastSession == activeScene )
|
||||
return;
|
||||
}
|
||||
_lastScene.TryGetTarget( out var last );
|
||||
|
||||
// if query AND scene is unchanged - no need to rebuild the tree
|
||||
if ( !queryDirty && ReferenceEquals( last, session.Scene ) )
|
||||
return;
|
||||
|
||||
_lastScene.SetTarget( session.Scene );
|
||||
|
||||
_lastSession = activeScene;
|
||||
queryDirty = false;
|
||||
hasActiveGameScene = _lastSession.HasActiveGameScene;
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
private void Rebuild()
|
||||
{
|
||||
var activeScene = SceneEditorSession.Active;
|
||||
var session = SceneEditorSession.Active;
|
||||
|
||||
Header.Clear( true );
|
||||
|
||||
@@ -161,14 +160,13 @@ public partial class SceneTreeWidget : Widget
|
||||
TreeView.Selection = new SelectionSystem();
|
||||
TreeView.Clear();
|
||||
|
||||
if ( _lastSession is null )
|
||||
if ( session is null )
|
||||
return;
|
||||
|
||||
bool hasSearch = !string.IsNullOrEmpty( Search.Text );
|
||||
SearchClear.Visible = hasSearch;
|
||||
|
||||
var scene = _lastSession.HasActiveGameScene ? _lastSession.ActiveGameScene : _lastSession.Scene;
|
||||
|
||||
var scene = session.Scene;
|
||||
if ( hasSearch )
|
||||
{
|
||||
// flat search view
|
||||
@@ -237,7 +235,7 @@ public partial class SceneTreeWidget : Widget
|
||||
}
|
||||
}
|
||||
|
||||
TreeView.Selection = activeScene.Selection;
|
||||
TreeView.Selection = session.Selection;
|
||||
|
||||
// Go through the current scene
|
||||
// Feel like this could be loads faster
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
/// the dock is hovered or focused. It also destroys the session when the dock
|
||||
/// is closed.
|
||||
/// </summary>
|
||||
/// Sol: does this need to exist? can't we just dock the view widget directly?
|
||||
public partial class SceneDock : Widget
|
||||
{
|
||||
SceneEditorSession Session { get; set; }
|
||||
public SceneEditorSession Session => _editorSession.GameSession ?? _editorSession;
|
||||
private SceneEditorSession _editorSession;
|
||||
|
||||
public SceneDock( SceneEditorSession session ) : base( null )
|
||||
{
|
||||
Session = session;
|
||||
_editorSession = session;
|
||||
|
||||
Layout = Layout.Row();
|
||||
Layout.Add( new SceneViewWidget( session, this ) );
|
||||
@@ -23,12 +25,12 @@ public partial class SceneDock : Widget
|
||||
|
||||
protected override bool OnClose()
|
||||
{
|
||||
if ( Session.HasUnsavedChanges )
|
||||
if ( _editorSession.HasUnsavedChanges )
|
||||
{
|
||||
this.ShowUnsavedChangesDialog(
|
||||
assetName: Session.Scene.Name,
|
||||
assetType: Session.IsPrefabSession ? "prefab" : "scene",
|
||||
onSave: () => Session.Save( false ) );
|
||||
assetName: _editorSession.Scene.Name,
|
||||
assetType: _editorSession.IsPrefabSession ? "prefab" : "scene",
|
||||
onSave: () => _editorSession.Save( false ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -40,8 +42,8 @@ public partial class SceneDock : Widget
|
||||
{
|
||||
base.OnDestroyed();
|
||||
|
||||
Session.Destroy();
|
||||
Session = null;
|
||||
_editorSession.Destroy();
|
||||
_editorSession = null;
|
||||
}
|
||||
|
||||
protected override void OnVisibilityChanged( bool visible )
|
||||
|
||||
@@ -17,7 +17,7 @@ public partial class SceneViewWidget
|
||||
[Event( "scene.play" )]
|
||||
public void OnScenePlay()
|
||||
{
|
||||
if ( !Session.HasActiveGameScene ) return;
|
||||
if ( !Session.IsPlaying ) return;
|
||||
CurrentView = ViewMode.Game;
|
||||
|
||||
OnViewModeChanged();
|
||||
@@ -42,7 +42,7 @@ public partial class SceneViewWidget
|
||||
|
||||
public void ToggleEject()
|
||||
{
|
||||
if ( !Session.HasActiveGameScene ) return;
|
||||
if ( !Session.IsPlaying ) return;
|
||||
|
||||
CurrentView = CurrentView == ViewMode.Game ? ViewMode.GameEjected : ViewMode.Game;
|
||||
|
||||
@@ -59,12 +59,17 @@ public partial class SceneViewWidget
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current view mode changed, we need to hide or show some UI things.
|
||||
/// Current view mode changed
|
||||
/// </summary>
|
||||
void OnViewModeChanged()
|
||||
{
|
||||
_viewportTools.Rebuild();
|
||||
_sidePanel?.Visible = CurrentView != ViewMode.Game;
|
||||
|
||||
foreach ( var viewport in _viewports.Values )
|
||||
{
|
||||
viewport.GizmoInstance.Selection = Session.Selection;
|
||||
}
|
||||
}
|
||||
|
||||
public SceneViewportWidget GetGameTarget()
|
||||
|
||||
@@ -35,7 +35,11 @@ public partial class SceneViewWidget : Widget
|
||||
|
||||
public EditorToolManager Tools { get; private set; }
|
||||
|
||||
public SceneEditorSession Session { get; private set; }
|
||||
/// <summary>
|
||||
/// The currently active session for this scene view. The game session if playing, otherwise the editor session.
|
||||
/// </summary>
|
||||
public SceneEditorSession Session => _editorSession.GameSession ?? _editorSession;
|
||||
private SceneEditorSession _editorSession;
|
||||
|
||||
private List<LinkableSplitter> _splitters;
|
||||
public Dictionary<int, SceneViewportWidget> _viewports;
|
||||
@@ -44,7 +48,7 @@ public partial class SceneViewWidget : Widget
|
||||
|
||||
public SceneViewWidget( SceneEditorSession session, Widget parent ) : base( parent )
|
||||
{
|
||||
Session = session;
|
||||
_editorSession = session;
|
||||
Tools = new EditorToolManager();
|
||||
|
||||
Layout = Layout.Column();
|
||||
|
||||
@@ -5,7 +5,7 @@ public partial class SceneViewportWidget
|
||||
public void StartPlay()
|
||||
{
|
||||
Editor.GameMode.SetPlayWidget( Renderer );
|
||||
Renderer.Scene = Session.ActiveGameScene;
|
||||
Renderer.Scene = Session.Scene;
|
||||
Renderer.Camera = null;
|
||||
Renderer.EnableEngineOverlays = true;
|
||||
ViewportOptions.Visible = false;
|
||||
|
||||
@@ -170,7 +170,6 @@ public partial class SceneViewportWidget
|
||||
[Event( "scene.session.save" )]
|
||||
public void SaveState()
|
||||
{
|
||||
// Don't save the state of play sessions
|
||||
if ( ProjectCookie is null ) return;
|
||||
|
||||
Scene scene = Session.Scene;
|
||||
|
||||
@@ -18,7 +18,7 @@ public partial class SceneViewportWidget : Widget
|
||||
private float TargetFOV { get; set; } = 80;
|
||||
private static float TransitionSpeed => 40;
|
||||
|
||||
SceneEditorSession Session => SceneView.Session;
|
||||
protected virtual SceneEditorSession Session => SceneView.Session;
|
||||
EditorToolManager Tools => SceneView.Tools;
|
||||
|
||||
public SceneRenderingWidget Renderer;
|
||||
@@ -514,7 +514,6 @@ public partial class SceneViewportWidget : Widget
|
||||
Tools.Frame( _activeCamera, Session );
|
||||
|
||||
EditorEvent.RunInterface<EditorEvent.ISceneView>( x => x.DrawGizmos( Session.Scene ) );
|
||||
|
||||
Session.Scene.EditorDraw();
|
||||
|
||||
DrawSelection();
|
||||
|
||||
@@ -57,7 +57,7 @@ partial class ViewportTools
|
||||
o.SetIcon( icon );
|
||||
o.Checkable = true;
|
||||
o.Checked = sceneViewWidget.ViewportLayout == layout;
|
||||
o.Enabled = !sceneViewWidget.Session.HasActiveGameScene; // Not great, but this stuff needs straightening out overall
|
||||
o.Enabled = !sceneViewWidget.Session.IsPlaying; // Not great, but this stuff needs straightening out overall
|
||||
}
|
||||
|
||||
menu.OpenAtCursor();
|
||||
|
||||
@@ -305,7 +305,7 @@ public partial class MovieEditor : Widget, IHotloadManaged
|
||||
void UpdateEditorContext()
|
||||
{
|
||||
var activeEditorSession = SceneEditorSession.Active;
|
||||
var activeScene = activeEditorSession?.ActiveGameScene ?? activeEditorSession?.Scene;
|
||||
var activeScene = activeEditorSession?.Scene;
|
||||
|
||||
// The current session exists
|
||||
if ( Session is { } session )
|
||||
@@ -367,7 +367,7 @@ public partial class MovieEditor : Widget, IHotloadManaged
|
||||
|
||||
public void CreateNewPlayer()
|
||||
{
|
||||
using ( SceneEditorSession.Active.Scene.Push() )
|
||||
using ( SceneEditorSession.Scope() )
|
||||
{
|
||||
var go = new GameObject( true, "New Movie Player" );
|
||||
go.Components.Create<MoviePlayer>();
|
||||
|
||||
Reference in New Issue
Block a user