godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.16k stars 97 forks source link

Implement registering autoloads in GDExtension #10928

Open dementive opened 1 month ago

dementive commented 1 month ago

Describe the project you are working on

Making a SoundManager class to manager the interface sounds for my game.

Describe the problem or limitation you are having in your project

When using GDextension there doesn't seem to be any way to properly add autoloads. An autoload is just a node that is at the tree root and is always persistent.

There are a few solutions I found: https://www.reddit.com/r/godot/comments/17lexmx/how_to_register_a_native_gdextension_class_as/ This one proposes to just create a single line gdscript file that extends my native class. This works and is what im doing now but it feels very jank/hacky and isn't really a great solution imo. When I need to access the autoloaded node from GDscript I can use the autoload name but if I want to access it from C++ I must use get_tree()->get_root()->get_node("Sound");. Ideally this is accessible via Engine::get_singleton in C++ and via the class name in gdscript and I also shouldn't have to make a single line gdscript file to register it as an autoload, ideally there should be some macro in register_types.cpp that registers the autoload for me. It isn't even possible to extend engine singletons in gdscript either so for this to work I have to make my native node that should be a singleton a regular class in order to make it a autoload.

This is another potential solution: https://forum.godotengine.org/t/how-to-automatically-set-a-script-as-singleton-autoload-in-gdextension/63671/2

The problem with this is the node has to be a singleton and if it is a singleton there is no way to add an instance of it to the scene tree...and my node needs to be in the scene tree to function so this will not work for me since it needs to call play() on AudioStreamPlayer nodes and when they are not in the tree you get an error that says "playback can only happen when a node is inside the scene tree".

imo a proper native solution would be much better. Having to make a gdscript script file with 1 line in it for a system that has nothing to do with gdscript at all does not seem like a good solution and that seems to be the only way to do what Im trying to do.

I would just use a standard engine singleton for this but my node is written in a way to where it must be part of the scene.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Add GDExtension singleton's registered with the register_singleton function as autoload nodes that have the same behavior as gdscript autoloads. Ideally a new macro will be added that can be called from register_types.cpp that will autoload C++ nodes when the scene tree starts up.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

The macro would look something like this: GDREGISTER_AUTOLOAD(CustomClass);

This would automatically register the class as an autoload that gets inserted at the scene tree root and is always persistent between scenes, the same way gdscript autoloads work.

If this enhancement will not be used often, can it be worked around with a few lines of script?

Yes there is a workaround but it is jank and hacky imo. This would allow for a more consistent API between C++ and gdscript when doing autoloads in C++ and also remove the requirement of making a gdscript file and then assigning it as an autoload in the editor.

Is there a reason why this should be core and not an add-on in the asset library?

It's not possible to implement outside of core/godot-cpp it seems.

dsnopek commented 1 month ago

What I personally do to register an autoload from a class defined in GDExtension is:

dementive commented 1 month ago

That seems much better than the other solutions I found, idk why but I didn't think auto loading a scene was doable but I see now it's clearly there in the documentation. That doesn't feel hacky or jank to me either although I will still have to deal with the api differences between Cpp and gdscript but that doesn't matter much.

My suggestion probably isn't necessary then, although some gdextension documentation for this might be nice but that is hard to come by these days as is haha. Thanks for the help 🙏

FireCatMagic commented 1 month ago

I actually was trying to do this earlier with the following code: Inside void initialize_bopimo_module(ModuleInitializationLevel p_level)

((SceneTree*)(Engine::get_singleton()->get_main_loop()))->get_root()->add_child(memnew(BopimoGame))

But even at the scene level of initilization, a call to get_root() crashes, so this doesn't work. What I did instead was turn my main singleton from an autoload node into an extension of SceneTree and made it the mainloop type. From there, in _initialize(), I can add my autoloads to root

But I agree, there should be better solutions than these

dsnopek commented 1 month ago

@FireCatMagic: The Engine singleton isn't available that early, but your code should work later in the startup process.

See these issues for more information about the long running "singletons not available early enough" issue: