Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extension class instances constructed in rust cannot be reloaded #543

Closed
TitanNano opened this issue Dec 21, 2023 · 4 comments · Fixed by #689
Closed

Extension class instances constructed in rust cannot be reloaded #543

TitanNano opened this issue Dec 21, 2023 · 4 comments · Fixed by #689
Labels
bug c: ffi Low-level components and interaction with GDExtension API status: upstream Depending on upstream fix (typically Godot)

Comments

@TitanNano
Copy link
Contributor

Since Godot 4.2 the engine has extension (hot) reloading. During reloading, the engine creates a backup of the class properties that have been exposed to godot and once the extension has been reloaded it recreates the extension parts of all the classes that have been defined by the extension.

Important here is that the engine tries to track all the instances which have been created for an extension class so that they can be reloaded later on. This happens in ClassDB::instantiate.

Unfortunately when gdext is creating classes from inside rust, it never goes through ClassDB::instantiate. It rather creates a new instance of the base class via ClassDB::instantiate and then assigns the rust instance to that base object via object_set_instance. The engine does not track these assignments as new instances of an extension class.

When the extension is then reloaded, none of the class instances are reloaded as the instance list of the extension_class is empty.


Trying to follow the code paths of Godot and gdext it appears that Gd::default_instance() should not directly call crate::callbacks::create but rather call ClassDB::instantiate.

Direct access to crate::callbacks::create_custom via Gd::from_init_fn also seems currently problematic, as it bypasses the instance tracking as well.

On the other hand, the engine could also move the instance tracking to ClassDB::set_object_extension_instance.

@Bromeon Bromeon added bug c: core Core components labels Dec 22, 2023
@Bromeon
Copy link
Member

Bromeon commented Feb 20, 2024

ClassDB::instantiate() cannot be generally used, as it takes no arguments. It thus can't create Rust classes that don't have an init function, and requiring the latter is too limiting. For example, Gd::from_init_fn() allows the user to pass in arbitrary context.

Otherwise, we'd need to have an intermediate zombie state (something like Option<UserInstance>) which is then properly initialized after ClassDB::instantiate(). The big issue with that approach is, how do we ensure that the post-init method is called from GDScript? This is not possible, and in fact a problem I had in the early days of gdext in 2022.


On the other hand, the engine could also move the instance tracking to ClassDB::set_object_extension_instance.

This sounds like the better idea. I can talk to the Godot devs about the feasibility of this.


Extension class instances constructed in rust cannot be reloaded

This doesn't seem to be true in general. For example, if you add reloadable = true to the dodge-the-creeps extension, and then add #[export] to the Main class' fields, those are reloaded.

Could you describe more clearly what exactly doesn't work? Ideally with a short example.

@TitanNano
Copy link
Contributor Author

ClassDB::instantiate() cannot be generally used, as it takes no arguments. It thus can't create Rust classes that don't have an init function, and requiring the latter is too limiting. For example, Gd::from_init_fn() allows the user to pass in arbitrary context.

Godot hot reloading requires a init method as it uses recreate_instance_func to recreate the extension part of a class instance.

This doesn't seem to be true in general. For example, if you add reloadable = true to the dodge-the-creeps extension, and then add #[export] to the Main class' fields, those are reloaded.

Could you describe more clearly what exactly doesn't work? Ideally with a short example.

The dodge-the-creeps example project does not contain any classes that are instantiated in rust. The Main class is a node subclass that is part of the scene and instanciated by the egnine when the scene tree is loaded. What I was refering to are GodotClasses which have been defined in rust and instanciated in rust code via a function on Gd.

@Bromeon
Copy link
Member

Bromeon commented Apr 22, 2024

This should have been fixed upstream with godotengine/godot#90447.

@TitanNano Any chance you can test this with a recent nightly version of Godot? You can download precompiled binaries from this CI or build yourself. If you don't get around it, also fine! 🙂

@obvious5211
Copy link

obvious5211 commented Apr 27, 2024

Hi!

I ran into the same issues as @TitanNano. I confirm that using Godot's latest nighty build( v4.3.dev.custom_build [6118592c6] for my case), the hot reload work even with Gd instantiated classes.

@Bromeon Bromeon added c: ffi Low-level components and interaction with GDExtension API and removed c: core Core components labels May 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug c: ffi Low-level components and interaction with GDExtension API status: upstream Depending on upstream fix (typically Godot)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants