Plugin system RFC
A long await solution to #13. The goal of this document is to provide a description of a plugin system, both in terms of usability and user experience and in terms of steps necessary for its implementation. The discussion underneath should result in materializing a concrete task list which will be implemented over time.
TL;DR
Let's include a Plugins property to Package definition that defines additional assemblies loaded by GameStudio and AssetCompiler. And a bunch of other things to help write/use plugins. It sounds simple but there's a lot of work behind the scenes to make it happen.
Motivation
Stride offers a lot out of the box, however, there's many instances where a user may want to execute custom actions in the GameStudio to help their tasks, have custom assets and custom editors, without necessarily having to recompile Stride from sources. Moreover, allowing plugins would enable Stride users to share their custom code without having to extend the engine in the main repo, or doing painful merges of forks. Also, a plugin oriented architecture should allow for easier development down the line.
Extensibility points
It's important to highlight areas that extensibility will focus around.
Runtime
Runtime libraries, i.e. libraries with components, processors and systems are already in a decent state when it comes to extensibility. Previous work has been done to enable e.g. Stride.Physics, Stride.Video to depend on Stride.Engine and be optional for the user to include. Following their pattern users can create new runtime libraries, inclusion of which will allow their code to be executed. Therefore, this document will not look into further extensibility points to the runtime.
Asset pipeline
The assets related projects (assets compiler and its dependencies) have been designed in a modular way that allows for creating custom assets and custom assets compilers. I've previously shown to be able to create custom assets (here), however, it required my game to take a dependency on Stride.Core.Assets, which currently is compiled under StrideEditorTargetFramework (windows only). The later outlined idea of design projects allows to skip fiddling with the build system and greater cross-platform compatibility.
Editor
The Stride GameStudio is the central point for any asset related operations. It has the most potential for extensibility and is also the least familiar to the users in terms of its internals. The following are some things we would want to enable in plugins:
The good news is: there already is an extensibility point for most of those things - StrideAssetsPlugin allows to explicitly register anything that isn't being picked up by attribute or as an interface implementation. A custom plugin just needs a good entry point to be registered (see test for a less than ideal solution).
Usability scenarios
When talking about a plugin system we need to consider how users will interact with it. The following scenarios describe the proposed solution of referencing plugin assemblies.
-
External plugin - the user includes a reference to a NuGet package in their game project. This package will contain runtime classes. Another package would contain the plugins to allow the user to work with assets later used in their game. A potential way to include the design package would be to extend Package (sdpkg file) with a Plugins property that is a list of {Name: version} NuGet packages resolved and loaded as plugins.
-
Internal plugin - the user creates a project in their solution that references their game project and Stride.Assets.Presentation (or just Stride.Core.Assets), but that project is not referenced by executable projects. The project is also added to the Plugins list in the game's sdpkg file. This project is considered for assembly reloading and any assets defined in it must be fully reloaded (or not - this could be a flag).
Considerations
- Editor plugins are currently only additive - internal user plugins with reloading would require modifications to allow plugin removal and dynamic reinitialization - for the stage 1 of implementing plugins I would disable reloading even for local plugins, but this has an unfortunate implication that none of the projects a plugin depends on can be reloaded either or things will break.
- We can allow for loading any NuGet package into memory as a plugin, but should we have them register themselves in the
AssemblyRegistry under Assets category, or would this be done by the plugin loader? Ideally we would want to block Module initializer execution for safety reasons, but that has been used a lot in Stride own libraries. Now that I think about it, safety goes out the window any time we execute any user code, but at least it would have to be implemented with Stride.
- Error handling when plugin is unavailable to be resolved
- Should there be an attribute for editor plugins discovery?
- We should add an option in GameStudio to generate a plugin project and add reference to it into the game project.
- Good documentation will be crucial for success of plugins.
- Searching for plugins - maybe a "strideplugin" tag on NuGet.org? We could include a plugin browser in the editor for users to add them directly in the editor. There would have to be something that link the runtime package with the design package.
Task list
To be added after a discussion.
Plugin system RFC
A long await solution to #13. The goal of this document is to provide a description of a plugin system, both in terms of usability and user experience and in terms of steps necessary for its implementation. The discussion underneath should result in materializing a concrete task list which will be implemented over time.
TL;DR
Let's include a
Pluginsproperty toPackagedefinition that defines additional assemblies loaded by GameStudio and AssetCompiler. And a bunch of other things to help write/use plugins. It sounds simple but there's a lot of work behind the scenes to make it happen.Motivation
Stride offers a lot out of the box, however, there's many instances where a user may want to execute custom actions in the GameStudio to help their tasks, have custom assets and custom editors, without necessarily having to recompile Stride from sources. Moreover, allowing plugins would enable Stride users to share their custom code without having to extend the engine in the main repo, or doing painful merges of forks. Also, a plugin oriented architecture should allow for easier development down the line.
Extensibility points
It's important to highlight areas that extensibility will focus around.
Runtime
Runtime libraries, i.e. libraries with components, processors and systems are already in a decent state when it comes to extensibility. Previous work has been done to enable e.g. Stride.Physics, Stride.Video to depend on Stride.Engine and be optional for the user to include. Following their pattern users can create new runtime libraries, inclusion of which will allow their code to be executed. Therefore, this document will not look into further extensibility points to the runtime.
Asset pipeline
The assets related projects (assets compiler and its dependencies) have been designed in a modular way that allows for creating custom assets and custom assets compilers. I've previously shown to be able to create custom assets (here), however, it required my game to take a dependency on
Stride.Core.Assets, which currently is compiled underStrideEditorTargetFramework(windows only). The later outlined idea of design projects allows to skip fiddling with the build system and greater cross-platform compatibility.Editor
The Stride GameStudio is the central point for any asset related operations. It has the most potential for extensibility and is also the least familiar to the users in terms of its internals. The following are some things we would want to enable in plugins:
[AssetCompiler]underThumbnailCompilationContext)The good news is: there already is an extensibility point for most of those things - StrideAssetsPlugin allows to explicitly register anything that isn't being picked up by attribute or as an interface implementation. A custom plugin just needs a good entry point to be registered (see test for a less than ideal solution).
Usability scenarios
When talking about a plugin system we need to consider how users will interact with it. The following scenarios describe the proposed solution of referencing plugin assemblies.
External plugin - the user includes a reference to a NuGet package in their game project. This package will contain runtime classes. Another package would contain the plugins to allow the user to work with assets later used in their game. A potential way to include the design package would be to extend Package (
sdpkgfile) with aPluginsproperty that is a list of{Name: version}NuGet packages resolved and loaded as plugins.Internal plugin - the user creates a project in their solution that references their game project and
Stride.Assets.Presentation(or justStride.Core.Assets), but that project is not referenced by executable projects. The project is also added to thePluginslist in the game'ssdpkgfile. This project is considered for assembly reloading and any assets defined in it must be fully reloaded (or not - this could be a flag).Considerations
AssemblyRegistryunderAssetscategory, or would this be done by the plugin loader? Ideally we would want to block Module initializer execution for safety reasons, but that has been used a lot in Stride own libraries. Now that I think about it, safety goes out the window any time we execute any user code, but at least it would have to be implemented with Stride.Task list
To be added after a discussion.