Open raulsntos opened 6 months ago
Thanks! This looks really great. :-)
When this gets to the implementation stage, I'm excited to try to add support for godot-cpp - I think the folks who build their game logic in C++ will find this really useful.
When the user confirms, the editor will call
create_class_source
to let the plugin create the file. The plugin will receive all the information provided by the user in the dialog. This allows the plugin to fully control the creation of the file, including how templating works
There's a number of tricky technical details, but for godot-cpp, it'd be really great if this could add the GDREGISTER_CLASS()
to the register_types.cpp
. And if so, it'd be really great to give an option to make a runtime class in the creation dialog (resulting in GDREGISTER_RUNTIME_CLASS()
instead), which I think will be the most popular option for folks doing game logic.
Feature that would be really useful for the godot-cpp side is to fetch required includes, to ensure the class has the necessary stuff, see for example:
Describe the project you are working on
Godot :slightly_smiling_face:
Describe the problem or limitation you are having in your project
GDExtension classes are treated as opaque binary blobs that can't be manipulated, so they can't participate in the same features available to scripts. The Godot editor can't create GDExtension classes, it can't open their source code, and it can't add signal callbacks.
Even though GDExtensions are built libraries, sometimes their source code is available. For example, when the user is using language bindings like
godot-cpp
orgodot-rust
to develop their game.Describe the feature / enhancement and how it helps to overcome the problem or limitation
GDExtensions / addons will be able to implement a new editor plugin type
EditorExtensionSourceCodePlugin
that allows providing the necessary callbacks for the editor to let certain extension classes participate in editor features.For some of these features, the editor will need new UI to indicate that the extension class can participate in these features, or where the dialogs made for scripting are not sufficient and need to display different data.
The editor features that this proposal focuses on are the following:
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
Add a new plugin type
EditorExtensionSourceCodePlugin
that can be implemented by a GDExtension or an addon (e.g.: GDScript) to provide editor features:Opening classes source code
This API is similar to the
ScriptLanguage::overrides_external_editor
andScriptLanguage::open_in_external_editor
APIs.The editor can show a button in the scene tree like it does for scripts today. The mockup below uses the PluginScript for illustrative purposes:
The editor will call the
open_in_external_editor
API to open the source code of an extension class. The editor delegates the opening implementation to the plugin which can choose how to integrate best with the preferred external editor.The plugin can return
false
inoverrides_external_editor
to indicate that it doesn't support this feature.To get the path to the file that defines an extension class, the editor will call
get_source_path
. Allowing the plugin to indicate the absolute path to the source code file. The file path can be outside of the project directory.Creating projects or files
The Create New popup menu will allow creating native classes and the user can use templates, if any is available, for the initial class source code. Similarly to how the Create Script dialog works. The mockup below uses the PluginScript icon for illustrative purposes:
The dialog will allow users to choose the language of the GDExtension class based on the registered plugins. The name of the language is provided by the
get_language_name
API. The user will be able to name the class and choose a base class, as well as select a template and configure its options.The editor will retrieve the list of available templates from the plugin by calling
get_available_templates
with the name of the base class selected. The plugin is encouraged to only provide templates that apply to the selected base class (for example, only provide templates for 3D character controllers when the class derives fromCharacterBody3D
).When a template is selected, the editor will call
get_template_options
to populate the creation dialog with additional options provided by the selected template. Similar to how theExportPlugin::_get_export_options
API allows providing additional export options.When the user confirms, the editor will call
create_class_source
to let the plugin create the file. The plugin will receive all the information provided by the user in the dialog. This allows the plugin to fully control the creation of the file, including how templating works (for example this could allow .NET to use the existing templating support withdotnet new
). If the language requires more than one file, the plugin is able to create as many files as needed, or modify existing ones (like adding the newly created files to the build system so they are included in the compilation).Some of the templating described may overlap with this other proposal:
Adding signal callbacks
The Connect Signal dialog will allow selecting native classes to connect a signal to existing methods or, if the native class supports it, to a new method that will be created subsequently.
Signals can already be connected to methods of native classes (both built-in classes and GDExtension classes). But it doesn't allow creating new methods while connecting signals.
In order to support creating a new method, the native class will need to provide a callback that the editor can use to create and add the method to the native class source code. Scripts support this by implementing the
ScriptLanguage::make_function
API but it relies on the editor adding the returned string to the source code which won't work properly with some languages (see https://github.com/godotengine/godot/issues/12908).With the new plugin, the editor will call the
add_method_func
API which delegates all of the necessary file manipulation to the plugin. Thep_args
parameter contains a string array with the types of the signal parameters, the format would be the same as the one used by theScriptLanguage::make_function
API:{PARAMETER_NAME}:{PARAMETER_TYPE}
.Open questions
How do we determine which GDExtension classes are source-available and can be handled by one of these plugins? And how do we pick the right plugin instance? We could use the GDExtension path or its
GDExtensionLoader
as discriminators. Is that enough? What if multiple plugins can handle the same extension?When creating class files, if there are multiple source-available GDExtensions, how do we determine which GDExtension to add the files to? The create dialog could have a dropdown to allow selecting among the available GDExtensions. Do we need additional API in the plugin for this? At the very least we'd need a way to identify which GDExtensions are source-available which goes back to the previous open question.
How would these plugins locate source-available GDExtensions? Specially since, in some languages, it's common for these extensions source code to be outside of the Godot project (i.e.: in C++ it's usually in
../src
). This could be handled by the plugin itself, maybe with some custom project settings to add lookup paths, but we may also want to provide a discovery mechanism that all plugins can rely on instead of re-inventing the wheel.Some features like opening source code or adding signal callbacks can be problematic for languages that support defining classes among multiple files (e.g.: C++ supports splitting class definitions among multiple
.cpp
files, C# partial classes can be defined in multiple.cs
files). What file should the editor open in this case? Where should the signal callback be added? Some IDEs just pick the first file it finds (presumably sorting alphabetically so the pick is deterministic).Some languages may require creating multiple files to define classes (e.g.: C++). The
create_class_source
API only provides a directory path. Is this enough to implement the desired behavior? What if some languages prefer to use separate directories for the header and source files?If this enhancement will not be used often, can it be worked around with a few lines of script?
There is currently no way for GDExtension classes to participate in some editor tooling features.
Is there a reason why this should be core and not an add-on in the asset library?
This is about exposing editor functionality to GDExtension classes.