Skip to content

Instantly share code, notes, and snippets.

@manio143
Last active April 10, 2024 02:03
Show Gist options
  • Save manio143/b6666eedb1403deb5525961697d0c25d to your computer and use it in GitHub Desktop.
Save manio143/b6666eedb1403deb5525961697d0c25d to your computer and use it in GitHub Desktop.
Stride Editor current design document

Current Stride Editor design document

This document is meant to describe the architecture of the current Stride Editor, so that it's easier to read its source code and rewrite it in a better way. This is in now way a complete document as the Editor is about 35k LOC. I'm slowly going to read through the more interesting parts and note down my thoughts.

View hierarchy

  • GameStudioWindow
    • static top bar menu
    • static asset context menu
    • static key bindings (start/cancel build, run game, open debug window, copy asset, show add asset dialog)
    • static toolbar tray (icon buttons)
    • routed commands (new, open, save, undo, redo)
    • docking manager (AvalonDock)
      • Solution Explorer
        • buttons
        • tree view (with context menu)
      • Asset View via AssetViewUserControl
      • References via AssetViewUserControl
      • Asset log via GridLogViewer
      • Build log via TextLogViewer
        • tabbed (BuildLog, LiveScriptingLog)
      • Property Grid
        • the whole grid is implemented here
      • Asset Preview via ContentPresenter
      • Action History
        • listbox with a data template
      • any instance of IEditorView managed by AssetEditorsManager is taking up the upper part of the docking grid
    • status bar

Internal plugin system

PluginService manages plugins registered in AssetsPlugin. A plugin may register IAssetEditorViewModels, and AssetViewModels. The registering methods look within the plugins assembly for types to register.

There's 3 plugins implemented that extend on AssetsPlugin functionality:

  • Stride.Core.Assets.Editor.AssetsEditorPlugin - registers some resources and paste processors
  • Stride.Editor.StrideEditorPlugin - creates services (GameSettings, GameStudioBuilder, GameStudioPreview, GameStudioThumbnail, StrideDebug)
  • Stride.Assets.Presentation.StrideDefaultAssetsPlugin - (among others) registers node presenters/updaters for some more advanced assets, suggested packages (that your project can reference), copy/paste processors, effect settings

Existing Asset Editors

  • Stride.Assets.Presentation.GameEditorViewModel - Base class for the view model of asset editors that runs an instance of a Game.
    • AssetCompositeEditorViewModel - Base class for the view model of an AssetCompositeViewModel<TAsset> editor.
      • AssetCompositeHierarchyEditorViewModel<TAssetPartDesign, TAssetPart, TItemViewModel> - Base class for the view model of an AssetCompositeHierarchyViewModel<TAssetPartDesign,TAssetPart> editor.
        • EntityHierarchyEditorViewModel - Base class for the view model of an EntityHierarchyViewModel editor.
          • SceneEditorViewModel - View model of a SceneViewModel editor. Corresponds to SceneEditorView, managed by SceneEditorController.
          • PrefabEditorViewModel - View model of a PrefabViewModel editor. Corresponds to PrefabEditorView, managed by PrefabEditorController.
        • UIEditorBaseViewModel - Base class for the view model of an UIBaseViewModel editor.
          • UILibraryEditorViewModel - View model for a UILibraryViewModel editor. Editor for custom UI controls.
          • UIPageEditorViewModel - View model for a UIPageViewModel editor.
  • Stride.Assets.Presentation.GraphicsCompositorEditorViewModel - graph editor for the GraphicsCompositorAsset.
  • Stride.Assets.Presentation.ScriptEditorViewModel - View model for the script editor (using Roslyn & AvalonEdit and RoslynPad).
  • Stride.Assets.Presentation.SpriteSheetEditorViewModel - sprite region selection tool for images that creates a sprite sheet.
  • Stride.Assets.Presentation.VisualScriptEditorViewModel - editor of VisualScriptAsset

Quantum graph presentation

First we need to understand how the graph works. A graph is a set of nodes connected by edges (here by members). Here our nodes implement Stride.Core.Quantum.IGraphNode. An object can be represented by a IObjectNode with its properties as IMemberNode. The nodes are stored in a INodeContainer with a INodeBuilder that creates IObjectNode from an object. If the object node is a collection or a dictionary it will have ItemReferences populated with references to those child items.

So basically, we are recreating the object graph in a metadata graph with references to the proper objects.

Then there's the Stride.Core.Assets.Quantum.AssetPropertyGraph composed of AssetObjectNodes. The object nodes are enriched with additional context of AssetObjectNodeExtended which deals with data inheritance/overrides for archetypes/prefabs. The AssetNodeContainer introduces more primitive types (vectors, colors, etc.) - a graph node of a primitive type is always a leaf.

There's a little more about how AssetPropertyGraph should behave described in a AssetPropertyGraphDefinition. In particular Stride.Assets.Presentation.Quantum.GraphicsCompositorAssetPropertyGraphDefinition describes which types of its members need to be references.

The Stride.Core.Assets.Quantum.AssetQuantumRegistry is responsible for constructing the AssetPropertyGraph into a AssetPropertyGraphContainer. Depending on the asset type (there's an attribute mapping asset property graph to type of asset) we can get an extension on the graph - currently there's EntityHierarchyPropertyGraph and UIAssetPropertyGraph (in Stride.Assets.Presentation.Quantum) for their hierarchical assets.

Note: here we can see my issue with domain boundries - EntityHierarchyPropertyGraph is coupled with presentation logic. For the rewrite it will have to be decoupled and logical parts moved into Stride.Core.Assets.Quantum.

Now that we have a graph, we want to show it. This will be WPF dependent code. The projects involved are: Stride.Core.Presentation, Stride.Core.Presentation.Quantum, Stride.Core.Assets.Editor (Quantum) and Stride.Assets.Presentation. Presentation.Quantum is mostly abstract and doesn't directly depend on WPF.

Nodes of a graph are included in the Stride.Core.Presentation.Quantum.ViewModels.NodeViewModel and presented with a Stride.Core.Presentation.Quantum.Presenters.INodePresenter (e.g. ItemNodePresenter for collecions and MemberNodePresenter for members). How it is displayed will depend on the Stride.Core.Presentation.Quantum.View.NodeViewModelTemplateProvider.

For the purpose of enriching the NodeViewModel there are Updaters that allow populating its AssociatedData (by INodePresenter.AttachedProperties.Add()) in Stride.Core.Assets.Editor.Quantum.NodePresenters.Updaters. Any behavior is defined with a command in Stride.Core.Assets.Editor.Quantum.NodePresenters.Commands. Commands and Updaters are registered in Stride.Core.Assets.Editor.Components.Properties.PropertiesViewModel (and its subclasses).

The actual XAML views are stored in a template definition which references the NodeViewModelTemplateProviders. The template providers code is defined in Stride.Core.Assets.Editor.View.TemplateProviders and the views are all in one file (14k LOC) Stride.Core.Assets.Editor.View.DefaultPropertyTemplateProviders.xaml. This needs to be divided up. It's included as a ResourceDictionary in Stride.Core.Assets.Editor.View.EditorDialogService.RegisterDefaultTemplateProviders() and called in the constructor of Stride.Core.Assets.Editor.ViewModel.EditorViewModel.

So in the EditorViewModel we access SessionViewModel (Session) on which we can set SessionObjectPropertiesViewModel (AssetViewProperties) which is the data context for the Property Grid (defined in GameStudioWindow's view). It's a subclass of ProperiesViewModel which has a GraphViewModel member, which is constructed with a list of INodePresenter and that is how NodeViewModels are created and displayed.

Class responsibilities

Stride.GameStudio.Program

  • check if x64, else exit
  • check registry for Privacy Policy acceptance (via PrivacyPolicyHelper)
  • initialize EditorSettings
  • install Metrics
  • load previous settings if project was reloaded
  • process arguments
    • connection with launcher process
    • create new project vs load existing
    • debug editor graphics (embedded game window)
    • reattach to VS debbuger
    • log effects to file
  • run module init for Stride.Core.Assets
  • create WindowManager with new App and run the App
  • dispatch async Startup()
    • initialize language settings
    • create GameStudioViewModel for the GameStudioWindow
    • add asset plugins
      • StrideEditorPlugin creates services (GameSettings, GameStudioBuilder, GameStudioPreview, GameStudioThumbnail, StrideDebug)
      • StrideDefaultAssetsPlugin (among others) registers node presenters/updaters for some more advanced assets, suggested packages (that your project can reference), copy/paste processors, effect settings
    • show main window or project selection/creation dialog

Stride.GameStudio.GameStudioWindow

  • create DockingLayoutManager, which manages where is what window docked
    • There's an "editor" layout and a "normal" layout.
  • create AssetEditorsManager
    • This manager is responsible for filling the editor window.
    • Each opened assset (identified by AssetViewModel) has a corresponding IAssetEditorViewModel, and each of those should have a layout pane in the editor.
    • The AssetEditorsManager uses IAssetsPluginService implementation Stride.Core.Assets.Editor.Services.PluginService to construct a new IEditorView, which during initialization constructs a IAssetEditorViewModel.
    • It's also possible to have one editor working over multiple assets with IMultipleAssetEditorViewModel.
  • registers asset editors manager in IEditorDialogService
    • This service handles tihngs like notifications, settings window, progress window, all dialog windows.
  • sets up commands used in XAML
  • opens a Metrics session
  • handles closing the window, registering assets previews, saving the whole session
  • upon being loaded for the first time;
    • resizes the window to fit the screen
    • loads docking layout
    • loads settings (incl. status of opened panes)
    • initializes plugins of IAssetsPluginService
    • opens previously opened editors
    • registers a clipboard monitor listener
    • notifies launcher GameStudio has started (launcher may close itself now)
    • links other opened windows as children using windows native API
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment