godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.45k stars 20.25k forks source link

Load scene in GDExtension into PackedScene at MODULE_INITIALIZATION_LEVEL_SCENE with attached GDScript lead to engine crash #85841

Open KirillGrigoriev opened 9 months ago

KirillGrigoriev commented 9 months ago

Tested versions

System information

Godot v4.2.stable - Windows 10.0.19045 - GLES3 (Compatibility) - NVIDIA GeForce GTX 650 (NVIDIA; 30.0.14.7464) - 13th Gen Intel(R) Core(TM) i5-13400 (16 Threads)

Issue description

Loading scene with (seems like) any typed node with attached GDScript use ResourceLoader::get_singleton()->load() at GDExtension init function (like initialize_example_module) at stage MODULE_INITIALIZATION_LEVEL_SCENE lead to engine crash. If node don't have attached GDScript everything works fine. If scene loaded later in, for example, any _ready function everything also works fine. So, perhaps GDScript loader rely on uninitialized at this stage code. GDExtension code:

#include <gdextension_interface.h>
#include <godot_cpp/classes/packed_scene.hpp>
#include <godot_cpp/classes/resource.hpp>
#include <godot_cpp/classes/resource_loader.hpp>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/godot.hpp>

namespace godot {
    Ref<PackedScene> packed_scene;

    void extension_init(ModuleInitializationLevel p_level) {
        if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { return; }
        packed_scene = ResourceLoader::get_singleton()->load("res://test_scene.tscn");
    }
    void extension_uninit(godot::ModuleInitializationLevel p_level) {
        if (p_level != godot::MODULE_INITIALIZATION_LEVEL_SCENE) { return; }
    }
    extern "C" GDExtensionBool GDE_EXPORT library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
        using namespace godot;

        godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);

        init_obj.register_initializer(extension_init);
        init_obj.register_terminator(extension_uninit);
        init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);

        return init_obj.init();
    }
};

GDScript and scene can be any. Seems like it's doesn't matter.

Steps to reproduce

  1. Setup working direction with "godot-cpp"
  2. Write and compile GDExtension.
  3. Setup simple project like MRP.

Minimal reproduction project (MRP)

project.zip

dsnopek commented 9 months ago

Thanks for the report!

While we probably shouldn't crash, I also don't think this should be expected to work. At this point in engine initialization (at MODULE_INITIALIZATION_LEVEL_SCENE), not everything is registered (ex the platform APIs, the server singletons, etc), but most importantly, scripting languages haven't been initialized yet. So, I wouldn't expect loading a GDScript to work.

In general, the initializer function (extension_init() in this example) should only be registering things (classes, servers, singletons, editor plugins, etc) but not doing any real work, because the engine isn't really initialized yet (at any of the initialization levels).

This isn't something specific to GDExtension - the same rule applies to modules, and I suspect if this example were converted to a module, that you'd get the same result.

KirillGrigoriev commented 9 months ago

I'm agree about initialization. Thanks for reply. I was think just loading a scene in PackedScene is not a real work. I mean, I don't instantiate it in init, and don't know scene's node and attached GDScript are active even if scene is not instantiated. I was think, it's some kind of caching. So, warning definitely need for people like me. )