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

Ensure GDExtension class is the correct type for the Godot engine class #1050

Merged
merged 1 commit into from
May 25, 2023
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
38 changes: 38 additions & 0 deletions binding_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False):
if sources:
utility_functions_source_path = source_gen_folder / "variant" / "utility_functions.cpp"
files.append(str(utility_functions_source_path.as_posix()))
register_engine_classes_source_path = source_gen_folder / "register_engine_classes.cpp"
files.append(str(register_engine_classes_source_path.as_posix()))

return files

Expand Down Expand Up @@ -1157,6 +1159,10 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
generate_engine_class_source(class_api, used_classes, fully_used_classes, use_template_get_node)
)

register_engine_classes_filename = Path(output_dir) / "src" / "register_engine_classes.cpp"
with register_engine_classes_filename.open("w+") as source_file:
source_file.write(generate_register_engine_classes_source(api))

for native_struct in api["native_structures"]:
struct_name = native_struct["name"]
snake_struct_name = camel_to_snake(struct_name)
Expand Down Expand Up @@ -1551,6 +1557,38 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
return "\n".join(result)


def generate_register_engine_classes_source(api):
includes = []
registrations = []

for class_api in api["classes"]:
if class_api["name"] == "ClassDB":
continue

class_name = class_api["name"]
snake_class_name = camel_to_snake(class_name)

includes.append(f"#include <godot_cpp/classes/{snake_class_name}.hpp>")
registrations.append(f"\tClassDB::register_engine_class<{class_name}>();")

result = []
add_header(f"register_engine_classes.cpp", result)

result.append("#include <godot_cpp/godot.hpp>")
result.append("")
result = result + includes
result.append("")
result.append("namespace godot {")
result.append("")
result.append("void GDExtensionBinding::register_engine_classes() {")
result = result + registrations
result.append("}")
result.append("")
result.append("} // namespace godot ")

return "\n".join(result)


def generate_global_constants(api, output_dir):
include_gen_folder = Path(output_dir) / "include" / "godot_cpp" / "classes"
source_gen_folder = Path(output_dir) / "src" / "classes"
Expand Down
13 changes: 13 additions & 0 deletions gdextension/gdextension_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -1879,6 +1879,19 @@ typedef void (*GDExtensionInterfaceObjectSetInstanceBinding)(GDExtensionObjectPt
*/
typedef void (*GDExtensionInterfaceObjectSetInstance)(GDExtensionObjectPtr p_o, GDExtensionConstStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */

/**
* @name object_get_class_name
*
* Gets the class name of an Object.
*
* @param p_object A pointer to the Object.
* @param p_library A pointer the library received by the GDExtension's entry point function.
* @param r_class_name A pointer to a String to receive the class name.
*
* @return true if successful in getting the class name; otherwise false.
*/
typedef GDExtensionBool (*GDExtensionInterfaceObjectGetClassName)(GDExtensionConstObjectPtr p_object, GDExtensionClassLibraryPtr p_library, GDExtensionStringNamePtr r_class_name);

/**
* @name object_cast_to
*
Expand Down
8 changes: 2 additions & 6 deletions include/godot_cpp/classes/ref.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,7 @@ struct PtrToArg<Ref<T>> {
_FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) {
// Important: p_ptr is T*, not Ref<T>*, since Object* is what engine gives to ptrcall.
ERR_FAIL_NULL_V(p_ptr, Ref<T>());
return Ref<T>(reinterpret_cast<T *>(godot::internal::gdextension_interface_object_get_instance_binding(
reinterpret_cast<GDExtensionObjectPtr>(const_cast<void *>(p_ptr)),
godot::internal::token, &T::___binding_callbacks)));
return Ref<T>(reinterpret_cast<T *>(godot::internal::get_object_instance_binding(reinterpret_cast<GDExtensionObjectPtr>(const_cast<void *>(p_ptr)))));
}

typedef Ref<T> EncodeT;
Expand All @@ -267,9 +265,7 @@ struct PtrToArg<const Ref<T> &> {

_FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) {
ERR_FAIL_NULL_V(p_ptr, Ref<T>());
return Ref<T>(reinterpret_cast<T *>(godot::internal::gdextension_interface_object_get_instance_binding(
reinterpret_cast<GDExtensionObjectPtr>(const_cast<void *>(p_ptr)),
godot::internal::token, &T::___binding_callbacks)));
return Ref<T>(reinterpret_cast<T *>(godot::internal::get_object_instance_binding(reinterpret_cast<GDExtensionObjectPtr>(const_cast<void *>(p_ptr)))));
}
};

Expand Down
11 changes: 11 additions & 0 deletions include/godot_cpp/core/class_db.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class ClassDB {
private:
// This may only contain custom classes, not Godot classes
static std::unordered_map<StringName, ClassInfo> classes;
static std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *> instance_binding_callbacks;

static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const void **p_defs, int p_defcount);
static void initialize_class(const ClassInfo &cl);
Expand All @@ -117,6 +118,8 @@ class ClassDB {
static void register_class(bool p_virtual = false);
template <class T>
static void register_abstract_class();
template <class T>
static void register_engine_class();

template <class N, class M, typename... VarArgs>
static MethodBind *bind_method(N p_method_name, M p_method, VarArgs... p_args);
Expand All @@ -137,6 +140,7 @@ class ClassDB {
static MethodBind *get_method(const StringName &p_class, const StringName &p_method);

static GDExtensionClassCallVirtual get_virtual_func(void *p_userdata, GDExtensionConstStringNamePtr p_name);
static const GDExtensionInstanceBindingCallbacks *get_instance_binding_callbacks(const StringName &p_class);

static void initialize(GDExtensionInitializationLevel p_level);
static void deinitialize(GDExtensionInitializationLevel p_level);
Expand All @@ -161,6 +165,8 @@ class ClassDB {

template <class T, bool is_abstract>
void ClassDB::_register_class(bool p_virtual) {
instance_binding_callbacks[T::get_class_static()] = &T::___binding_callbacks;

// Register this class within our plugin
ClassInfo cl;
cl.name = T::get_class_static();
Expand Down Expand Up @@ -213,6 +219,11 @@ void ClassDB::register_abstract_class() {
ClassDB::_register_class<T, true>();
}

template <class T>
void ClassDB::register_engine_class() {
instance_binding_callbacks[T::get_class_static()] = &T::___binding_callbacks;
}

template <class N, class M, typename... VarArgs>
MethodBind *ClassDB::bind_method(N p_method_name, M p_method, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
Expand Down
4 changes: 2 additions & 2 deletions include/godot_cpp/core/engine_ptrcall.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ O *_call_native_mb_ret_obj(const GDExtensionMethodBindPtr mb, void *instance, co
if (ret == nullptr) {
return nullptr;
}
return reinterpret_cast<O *>(internal::gdextension_interface_object_get_instance_binding(ret, internal::token, &O::___binding_callbacks));
return reinterpret_cast<O *>(internal::get_object_instance_binding(ret));
}

template <class R, class... Args>
Expand Down Expand Up @@ -81,7 +81,7 @@ Object *_call_utility_ret_obj(const GDExtensionPtrUtilityFunction func, void *in
GodotObject *ret = nullptr;
std::array<GDExtensionConstTypePtr, sizeof...(Args)> mb_args = { { (GDExtensionConstTypePtr)args... } };
func(&ret, mb_args.data(), mb_args.size());
return (Object *)internal::gdextension_interface_object_get_instance_binding(ret, internal::token, &Object::___binding_callbacks);
return (Object *)internal::get_object_instance_binding(ret);
}

template <class... Args>
Expand Down
9 changes: 3 additions & 6 deletions include/godot_cpp/core/method_ptrcall.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

#include <godot_cpp/core/defs.hpp>

#include <godot_cpp/core/object.hpp>
#include <godot_cpp/godot.hpp>
#include <godot_cpp/variant/variant.hpp>

Expand Down Expand Up @@ -168,9 +169,7 @@ MAKE_PTRARG_BY_REFERENCE(Variant);
template <class T>
struct PtrToArg<T *> {
_FORCE_INLINE_ static T *convert(const void *p_ptr) {
return reinterpret_cast<T *>(godot::internal::gdextension_interface_object_get_instance_binding(
reinterpret_cast<GDExtensionObjectPtr>(const_cast<void *>(p_ptr)),
godot::internal::token, &T::___binding_callbacks));
return reinterpret_cast<T *>(godot::internal::get_object_instance_binding(reinterpret_cast<GDExtensionObjectPtr>(const_cast<void *>(p_ptr))));
}
typedef Object *EncodeT;
_FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) {
Expand All @@ -181,9 +180,7 @@ struct PtrToArg<T *> {
template <class T>
struct PtrToArg<const T *> {
_FORCE_INLINE_ static const T *convert(const void *p_ptr) {
return reinterpret_cast<const T *>(godot::internal::gdextension_interface_object_get_instance_binding(
reinterpret_cast<GDExtensionObjectPtr>(const_cast<void *>(p_ptr)),
godot::internal::token, &T::___binding_callbacks));
return reinterpret_cast<const T *>(godot::internal::get_object_instance_binding(reinterpret_cast<GDExtensionObjectPtr>(const_cast<void *>(p_ptr))));
}
typedef const Object *EncodeT;
_FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) {
Expand Down
12 changes: 9 additions & 3 deletions include/godot_cpp/core/object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@

namespace godot {

namespace internal {

Object *get_object_instance_binding(GodotObject *);

} // namespace internal

struct MethodInfo {
StringName name;
PropertyInfo return_val;
Expand Down Expand Up @@ -128,7 +134,7 @@ class ObjectDB {
if (obj == nullptr) {
return nullptr;
}
return reinterpret_cast<Object *>(internal::gdextension_interface_object_get_instance_binding(obj, internal::token, &Object::___binding_callbacks));
return internal::get_object_instance_binding(obj);
}
};

Expand All @@ -142,7 +148,7 @@ T *Object::cast_to(Object *p_object) {
if (casted == nullptr) {
return nullptr;
}
return reinterpret_cast<T *>(internal::gdextension_interface_object_get_instance_binding(casted, internal::token, &T::___binding_callbacks));
return dynamic_cast<T *>(internal::get_object_instance_binding(casted));
}

template <class T>
Expand All @@ -155,7 +161,7 @@ const T *Object::cast_to(const Object *p_object) {
if (casted == nullptr) {
return nullptr;
}
return reinterpret_cast<const T *>(internal::gdextension_interface_object_get_instance_binding(casted, internal::token, &T::___binding_callbacks));
return dynamic_cast<const T *>(internal::get_object_instance_binding(casted));
}

} // namespace godot
Expand Down
4 changes: 4 additions & 0 deletions include/godot_cpp/godot.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ extern "C" GDExtensionInterfaceGlobalGetSingleton gdextension_interface_global_g
extern "C" GDExtensionInterfaceObjectGetInstanceBinding gdextension_interface_object_get_instance_binding;
extern "C" GDExtensionInterfaceObjectSetInstanceBinding gdextension_interface_object_set_instance_binding;
extern "C" GDExtensionInterfaceObjectSetInstance gdextension_interface_object_set_instance;
extern "C" GDExtensionInterfaceObjectGetClassName gdextension_interface_object_get_class_name;
extern "C" GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to;
extern "C" GDExtensionInterfaceObjectGetInstanceFromId gdextension_interface_object_get_instance_from_id;
extern "C" GDExtensionInterfaceObjectGetInstanceId gdextension_interface_object_get_instance_id;
Expand Down Expand Up @@ -188,6 +189,9 @@ enum ModuleInitializationLevel {
};

class GDExtensionBinding {
private:
static void register_engine_classes();

public:
using Callback = void (*)(ModuleInitializationLevel p_level);

Expand Down
7 changes: 7 additions & 0 deletions src/core/class_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
namespace godot {

std::unordered_map<StringName, ClassDB::ClassInfo> ClassDB::classes;
std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *> ClassDB::instance_binding_callbacks;
GDExtensionInitializationLevel ClassDB::current_level = GDEXTENSION_INITIALIZATION_CORE;

MethodDefinition D_METHOD(StringName p_name) {
Expand Down Expand Up @@ -314,6 +315,12 @@ GDExtensionClassCallVirtual ClassDB::get_virtual_func(void *p_userdata, GDExtens
return nullptr;
}

const GDExtensionInstanceBindingCallbacks *ClassDB::get_instance_binding_callbacks(const StringName &p_class) {
std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *>::iterator callbacks_it = instance_binding_callbacks.find(p_class);
ERR_FAIL_COND_V_MSG(callbacks_it == instance_binding_callbacks.end(), nullptr, String("Cannot find instance binding callbacks for class '{0}'.").format(Array::make(p_class)));
return callbacks_it->second;
}

void ClassDB::bind_virtual_method(const StringName &p_class, const StringName &p_method, GDExtensionClassCallVirtual p_call) {
std::unordered_map<StringName, ClassInfo>::iterator type_it = classes.find(p_class);
ERR_FAIL_COND_MSG(type_it == classes.end(), String("Class '{0}' doesn't exist.").format(Array::make(p_class)));
Expand Down
30 changes: 30 additions & 0 deletions src/core/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,38 @@

#include <godot_cpp/core/object.hpp>

#include <godot_cpp/core/class_db.hpp>

namespace godot {

namespace internal {

Object *get_object_instance_binding(GodotObject *p_engine_object) {
if (p_engine_object == nullptr) {
return nullptr;
}

// Get existing instance binding, if one already exists.
GDExtensionObjectPtr instance = gdextension_interface_object_get_instance_binding(p_engine_object, token, nullptr);
if (instance != nullptr) {
return reinterpret_cast<Object *>(instance);
}

// Otherwise, try to look up the correct binding callbacks.
const GDExtensionInstanceBindingCallbacks *binding_callbacks = nullptr;
StringName class_name;
if (gdextension_interface_object_get_class_name(p_engine_object, library, reinterpret_cast<GDExtensionStringNamePtr>(class_name._native_ptr()))) {
binding_callbacks = ClassDB::get_instance_binding_callbacks(class_name);
}
if (binding_callbacks == nullptr) {
binding_callbacks = &Object::___binding_callbacks;
}

return reinterpret_cast<Object *>(gdextension_interface_object_get_instance_binding(p_engine_object, token, binding_callbacks));
}

} // namespace internal

MethodInfo::MethodInfo() :
flags(GDEXTENSION_METHOD_FLAG_NORMAL) {}

Expand Down
3 changes: 3 additions & 0 deletions src/godot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ GDExtensionInterfaceGlobalGetSingleton gdextension_interface_global_get_singleto
GDExtensionInterfaceObjectGetInstanceBinding gdextension_interface_object_get_instance_binding = nullptr;
GDExtensionInterfaceObjectSetInstanceBinding gdextension_interface_object_set_instance_binding = nullptr;
GDExtensionInterfaceObjectSetInstance gdextension_interface_object_set_instance = nullptr;
GDExtensionInterfaceObjectGetClassName gdextension_interface_object_get_class_name = nullptr;
GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to = nullptr;
GDExtensionInterfaceObjectGetInstanceFromId gdextension_interface_object_get_instance_from_id = nullptr;
GDExtensionInterfaceObjectGetInstanceId gdextension_interface_object_get_instance_id = nullptr;
Expand Down Expand Up @@ -342,6 +343,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
LOAD_PROC_ADDRESS(object_get_instance_binding, GDExtensionInterfaceObjectGetInstanceBinding);
LOAD_PROC_ADDRESS(object_set_instance_binding, GDExtensionInterfaceObjectSetInstanceBinding);
LOAD_PROC_ADDRESS(object_set_instance, GDExtensionInterfaceObjectSetInstance);
LOAD_PROC_ADDRESS(object_get_class_name, GDExtensionInterfaceObjectGetClassName);
LOAD_PROC_ADDRESS(object_cast_to, GDExtensionInterfaceObjectCastTo);
LOAD_PROC_ADDRESS(object_get_instance_from_id, GDExtensionInterfaceObjectGetInstanceFromId);
LOAD_PROC_ADDRESS(object_get_instance_id, GDExtensionInterfaceObjectGetInstanceId);
Expand Down Expand Up @@ -371,6 +373,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
ERR_FAIL_COND_V_MSG(init_callback == nullptr, false, "Initialization callback must be defined.");

Variant::init_bindings();
register_engine_classes();

return true;
}
Expand Down
3 changes: 2 additions & 1 deletion src/variant/variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <godot_cpp/godot.hpp>

#include <godot_cpp/core/binder_common.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/defs.hpp>

#include <utility>
Expand Down Expand Up @@ -411,7 +412,7 @@ Variant::operator Object *() const {
if (obj == nullptr) {
return nullptr;
}
return reinterpret_cast<Object *>(internal::gdextension_interface_object_get_instance_binding(obj, internal::token, &Object::___binding_callbacks));
return internal::get_object_instance_binding(obj);
}

Variant::operator ObjectID() const {
Expand Down