Skip to content

Test that internal classes work as expected #1818

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

Merged
merged 1 commit into from
Jul 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions gdextension/gdextension_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,8 @@ typedef struct {
void *class_userdata; // Per-class user data, later accessible in instance bindings.
} GDExtensionClassCreationInfo4;

typedef GDExtensionClassCreationInfo4 GDExtensionClassCreationInfo5;

typedef void *GDExtensionClassLibraryPtr;

/* Passed a pointer to a PackedStringArray that should be filled with the classes that may be used by the GDExtension. */
Expand Down Expand Up @@ -2943,6 +2945,7 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass3)(GDExtensionCl
/**
* @name classdb_register_extension_class4
* @since 4.4
* @deprecated in Godot 4.5. Use `classdb_register_extension_class5` instead.
*
* Registers an extension class in the ClassDB.
*
Expand All @@ -2955,6 +2958,21 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass3)(GDExtensionCl
*/
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass4)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs);

/**
* @name classdb_register_extension_class5
* @since 4.5
*
* Registers an extension class in the ClassDB.
*
* Provided struct can be safely freed once the function returns.
*
* @param p_library A pointer the library received by the GDExtension's entry point function.
* @param p_class_name A pointer to a StringName with the class name.
* @param p_parent_class_name A pointer to a StringName with the parent class name.
* @param p_extension_funcs A pointer to a GDExtensionClassCreationInfo2 struct.
*/
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass5)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo5 *p_extension_funcs);

/**
* @name classdb_register_extension_class_method
* @since 4.1
Expand Down
4 changes: 2 additions & 2 deletions include/godot_cpp/core/class_db.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ void ClassDB::_register_class(bool p_virtual, bool p_exposed, bool p_runtime) {
class_register_order.push_back(cl.name);

// Register this class with Godot
GDExtensionClassCreationInfo4 class_info = {
GDExtensionClassCreationInfo5 class_info = {
p_virtual, // GDExtensionBool is_virtual;
is_abstract, // GDExtensionBool is_abstract;
p_exposed, // GDExtensionBool is_exposed;
Expand All @@ -278,7 +278,7 @@ void ClassDB::_register_class(bool p_virtual, bool p_exposed, bool p_runtime) {
(void *)&T::get_class_static(), // void *class_userdata;
};

internal::gdextension_interface_classdb_register_extension_class4(internal::library, cl.name._native_ptr(), cl.parent_name._native_ptr(), &class_info);
internal::gdextension_interface_classdb_register_extension_class5(internal::library, cl.name._native_ptr(), cl.parent_name._native_ptr(), &class_info);

// call bind_methods etc. to register all members of the class
T::initialize_class();
Expand Down
2 changes: 1 addition & 1 deletion include/godot_cpp/godot.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ extern "C" GDExtensionInterfaceObjectSetScriptInstance gdextension_interface_obj
extern "C" GDExtensionInterfaceClassdbConstructObject2 gdextension_interface_classdb_construct_object2;
extern "C" GDExtensionInterfaceClassdbGetMethodBind gdextension_interface_classdb_get_method_bind;
extern "C" GDExtensionInterfaceClassdbGetClassTag gdextension_interface_classdb_get_class_tag;
extern "C" GDExtensionInterfaceClassdbRegisterExtensionClass4 gdextension_interface_classdb_register_extension_class4;
extern "C" GDExtensionInterfaceClassdbRegisterExtensionClass5 gdextension_interface_classdb_register_extension_class5;
extern "C" GDExtensionInterfaceClassdbRegisterExtensionClassMethod gdextension_interface_classdb_register_extension_class_method;
extern "C" GDExtensionInterfaceClassdbRegisterExtensionClassVirtualMethod gdextension_interface_classdb_register_extension_class_virtual_method;
extern "C" GDExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant gdextension_interface_classdb_register_extension_class_integer_constant;
Expand Down
4 changes: 2 additions & 2 deletions src/godot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ GDExtensionInterfaceObjectSetScriptInstance gdextension_interface_object_set_scr
GDExtensionInterfaceClassdbConstructObject2 gdextension_interface_classdb_construct_object2 = nullptr;
GDExtensionInterfaceClassdbGetMethodBind gdextension_interface_classdb_get_method_bind = nullptr;
GDExtensionInterfaceClassdbGetClassTag gdextension_interface_classdb_get_class_tag = nullptr;
GDExtensionInterfaceClassdbRegisterExtensionClass4 gdextension_interface_classdb_register_extension_class4 = nullptr;
GDExtensionInterfaceClassdbRegisterExtensionClass5 gdextension_interface_classdb_register_extension_class5 = nullptr;
GDExtensionInterfaceClassdbRegisterExtensionClassMethod gdextension_interface_classdb_register_extension_class_method = nullptr;
GDExtensionInterfaceClassdbRegisterExtensionClassVirtualMethod gdextension_interface_classdb_register_extension_class_virtual_method = nullptr;
GDExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant gdextension_interface_classdb_register_extension_class_integer_constant = nullptr;
Expand Down Expand Up @@ -477,7 +477,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
LOAD_PROC_ADDRESS(classdb_construct_object2, GDExtensionInterfaceClassdbConstructObject2);
LOAD_PROC_ADDRESS(classdb_get_method_bind, GDExtensionInterfaceClassdbGetMethodBind);
LOAD_PROC_ADDRESS(classdb_get_class_tag, GDExtensionInterfaceClassdbGetClassTag);
LOAD_PROC_ADDRESS(classdb_register_extension_class4, GDExtensionInterfaceClassdbRegisterExtensionClass4);
LOAD_PROC_ADDRESS(classdb_register_extension_class5, GDExtensionInterfaceClassdbRegisterExtensionClass5);
LOAD_PROC_ADDRESS(classdb_register_extension_class_method, GDExtensionInterfaceClassdbRegisterExtensionClassMethod);
LOAD_PROC_ADDRESS(classdb_register_extension_class_virtual_method, GDExtensionInterfaceClassdbRegisterExtensionClassVirtualMethod);
LOAD_PROC_ADDRESS(classdb_register_extension_class_integer_constant, GDExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant);
Expand Down
7 changes: 7 additions & 0 deletions test/project/main.gd
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,13 @@ func _ready():
assert_equal(library_path, ProjectSettings.globalize_path(library_path))
assert_equal(FileAccess.file_exists(library_path), true)

# Test that internal classes work as expected (at least for Godot 4.5+).
assert_equal(ClassDB.can_instantiate("ExampleInternal"), false)
assert_equal(ClassDB.instantiate("ExampleInternal"), null)
var internal_class = example.test_get_internal_class()
assert_equal(internal_class.get_the_answer(), 42)
assert_equal(internal_class.get_class(), "ExampleInternal")

# Test a class with a unicode name.
var przykład = ExamplePrzykład.new()
assert_equal(przykład.get_the_word(), "słowo to przykład")
Expand Down
16 changes: 16 additions & 0 deletions test/src/example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ void Example::_bind_methods() {

ClassDB::bind_method(D_METHOD("test_use_engine_singleton"), &Example::test_use_engine_singleton);

ClassDB::bind_method(D_METHOD("test_get_internal_class"), &Example::test_get_internal_class);

ClassDB::bind_static_method("Example", D_METHOD("test_static", "a", "b"), &Example::test_static);
ClassDB::bind_static_method("Example", D_METHOD("test_static2"), &Example::test_static2);

Expand Down Expand Up @@ -744,6 +746,12 @@ String Example::test_library_path() {
return library_path;
}

Ref<RefCounted> Example::test_get_internal_class() const {
Ref<ExampleInternal> it;
it.instantiate();
return it;
}

int64_t Example::test_get_internal(const Variant &p_input) const {
if (p_input.get_type() != Variant::INT) {
return -1;
Expand Down Expand Up @@ -779,3 +787,11 @@ void ExamplePrzykład::_bind_methods() {
String ExamplePrzykład::get_the_word() const {
return U"słowo to przykład";
}

void ExampleInternal::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_the_answer"), &ExampleInternal::get_the_answer);
}

int ExampleInternal::get_the_answer() const {
return 42;
}
14 changes: 14 additions & 0 deletions test/src/example.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

using namespace godot;

class ExampleInternal;

class ExampleRef : public RefCounted {
GDCLASS(ExampleRef, RefCounted);

Expand Down Expand Up @@ -203,6 +205,8 @@ class Example : public Control {
String test_use_engine_singleton() const;

static String test_library_path();

Ref<RefCounted> test_get_internal_class() const;
};

VARIANT_ENUM_CAST(Example::Constants);
Expand Down Expand Up @@ -288,3 +292,13 @@ class ExamplePrzykład : public RefCounted {
public:
String get_the_word() const;
};

class ExampleInternal : public RefCounted {
GDCLASS(ExampleInternal, RefCounted);

protected:
static void _bind_methods();

public:
int get_the_answer() const;
};
1 change: 1 addition & 0 deletions test/src/register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ void initialize_example_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(ExampleChild);
GDREGISTER_RUNTIME_CLASS(ExampleRuntime);
GDREGISTER_CLASS(ExamplePrzykład);
GDREGISTER_INTERNAL_CLASS(ExampleInternal);
}

void uninitialize_example_module(ModuleInitializationLevel p_level) {
Expand Down
Loading