Merge pull request #1465 from dsnopek/4.2-cherrypicks-4

Cherry-picks for the godot-cpp 4.2 branch - 4th batch
pull/1494/head
David Snopek 2024-05-28 08:35:12 -05:00 committed by GitHub
commit 2b6eb6832e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 321 additions and 28 deletions

View File

@ -181,6 +181,10 @@ def generate_bindings(api_filepath, use_template_get_node, bits="64", precision=
generate_utility_functions(api, target_dir) generate_utility_functions(api, target_dir)
CLASS_ALIASES = {
"ClassDB": "ClassDBSingleton",
}
builtin_classes = [] builtin_classes = []
# Key is class name, value is boolean where True means the class is refcounted. # Key is class name, value is boolean where True means the class is refcounted.
@ -1127,9 +1131,9 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
# First create map of classes and singletons. # First create map of classes and singletons.
for class_api in api["classes"]: for class_api in api["classes"]:
# Generate code for the ClassDB singleton under a different name. # Generate code for the ClassDB singleton under a different name.
if class_api["name"] == "ClassDB": if class_api["name"] in CLASS_ALIASES:
class_api["name"] = "ClassDBSingleton" class_api["alias_for"] = class_api["name"]
class_api["alias_for"] = "ClassDB" class_api["name"] = CLASS_ALIASES[class_api["alias_for"]]
engine_classes[class_api["name"]] = class_api["is_refcounted"] engine_classes[class_api["name"]] = class_api["is_refcounted"]
for native_struct in api["native_structures"]: for native_struct in api["native_structures"]:
if native_struct["name"] == "ObjectID": if native_struct["name"] == "ObjectID":
@ -1139,9 +1143,9 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
for singleton in api["singletons"]: for singleton in api["singletons"]:
# Generate code for the ClassDB singleton under a different name. # Generate code for the ClassDB singleton under a different name.
if singleton["name"] == "ClassDB": if singleton["name"] in CLASS_ALIASES:
singleton["name"] = "ClassDBSingleton" singleton["alias_for"] = singleton["name"]
singleton["alias_for"] = "ClassDB" singleton["name"] = CLASS_ALIASES[singleton["name"]]
singletons.append(singleton["name"]) singletons.append(singleton["name"])
for class_api in api["classes"]: for class_api in api["classes"]:
@ -1346,6 +1350,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append("#include <type_traits>") result.append("#include <type_traits>")
result.append("") result.append("")
if class_name == "ClassDBSingleton":
result.append("#include <godot_cpp/core/binder_common.hpp>")
result.append("")
result.append("namespace godot {") result.append("namespace godot {")
result.append("") result.append("")
@ -1367,6 +1375,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append(f"\tGDEXTENSION_CLASS({class_name}, {inherits})") result.append(f"\tGDEXTENSION_CLASS({class_name}, {inherits})")
result.append("") result.append("")
if is_singleton:
result.append(f"\tstatic {class_name} *singleton;")
result.append("")
result.append("public:") result.append("public:")
result.append("") result.append("")
@ -1447,6 +1459,11 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append("\t}") result.append("\t}")
result.append("") result.append("")
if is_singleton:
result.append(f"\t~{class_name}();")
result.append("")
result.append("public:") result.append("public:")
# Special cases. # Special cases.
@ -1504,6 +1521,19 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
if class_name == "ClassDBSingleton": if class_name == "ClassDBSingleton":
result.append("#define CLASSDB_SINGLETON_FORWARD_METHODS \\") result.append("#define CLASSDB_SINGLETON_FORWARD_METHODS \\")
if "enums" in class_api:
for enum_api in class_api["enums"]:
if enum_api["is_bitfield"]:
result.append(f'\tenum {enum_api["name"]} : uint64_t {{ \\')
else:
result.append(f'\tenum {enum_api["name"]} {{ \\')
for value in enum_api["values"]:
result.append(f'\t\t{value["name"]} = {value["value"]}, \\')
result.append("\t}; \\")
result.append("\t \\")
for method in class_api["methods"]: for method in class_api["methods"]:
# ClassDBSingleton shouldn't have any static or vararg methods, but if some appear later, lets skip them. # ClassDBSingleton shouldn't have any static or vararg methods, but if some appear later, lets skip them.
if vararg: if vararg:
@ -1512,12 +1542,17 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
continue continue
method_signature = "\tstatic " method_signature = "\tstatic "
return_type = None
if "return_type" in method: if "return_type" in method:
method_signature += f'{correct_type(method["return_type"])} ' return_type = correct_type(method["return_type"].replace("ClassDBSingleton", "ClassDB"), None, False)
elif "return_value" in method: elif "return_value" in method:
method_signature += ( return_type = correct_type(
correct_type(method["return_value"]["type"], method["return_value"].get("meta", None)) + " " method["return_value"]["type"].replace("ClassDBSingleton", "ClassDB"),
method["return_value"].get("meta", None),
False,
) )
if return_type is not None:
method_signature += return_type + " "
else: else:
method_signature += "void " method_signature += "void "
@ -1536,8 +1571,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append(method_signature) result.append(method_signature)
method_body = "\t\t" method_body = "\t\t"
if "return_type" in method or "return_value" in method: if return_type is not None:
method_body += "return " method_body += "return "
if "alias_for" in class_api and return_type.startswith(class_api["alias_for"] + "::"):
method_body += f"({return_type})"
method_body += f'ClassDBSingleton::get_singleton()->{method["name"]}(' method_body += f'ClassDBSingleton::get_singleton()->{method["name"]}('
method_body += ", ".join(map(lambda x: escape_identifier(x["name"]), method_arguments)) method_body += ", ".join(map(lambda x: escape_identifier(x["name"]), method_arguments))
method_body += "); \\" method_body += "); \\"
@ -1547,6 +1584,18 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append("\t;") result.append("\t;")
result.append("") result.append("")
result.append("#define CLASSDB_SINGLETON_VARIANT_CAST \\")
if "enums" in class_api:
for enum_api in class_api["enums"]:
if enum_api["is_bitfield"]:
result.append(f'\tVARIANT_BITFIELD_CAST({class_api["alias_for"]}::{enum_api["name"]}); \\')
else:
result.append(f'\tVARIANT_ENUM_CAST({class_api["alias_for"]}::{enum_api["name"]}); \\')
result.append("\t;")
result.append("")
result.append(f"#endif // ! {header_guard}") result.append(f"#endif // ! {header_guard}")
return "\n".join(result) return "\n".join(result)
@ -1564,6 +1613,7 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
result.append(f"#include <godot_cpp/classes/{snake_class_name}.hpp>") result.append(f"#include <godot_cpp/classes/{snake_class_name}.hpp>")
result.append("") result.append("")
result.append("#include <godot_cpp/core/class_db.hpp>")
result.append("#include <godot_cpp/core/engine_ptrcall.hpp>") result.append("#include <godot_cpp/core/engine_ptrcall.hpp>")
result.append("#include <godot_cpp/core/error_macros.hpp>") result.append("#include <godot_cpp/core/error_macros.hpp>")
result.append("") result.append("")
@ -1578,9 +1628,10 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
result.append("") result.append("")
if is_singleton: if is_singleton:
result.append(f"{class_name} *{class_name}::singleton = nullptr;")
result.append("")
result.append(f"{class_name} *{class_name}::get_singleton() {{") result.append(f"{class_name} *{class_name}::get_singleton() {{")
# We assume multi-threaded access is OK because each assignment will assign the same value every time # We assume multi-threaded access is OK because each assignment will assign the same value every time
result.append(f"\tstatic {class_name} *singleton = nullptr;")
result.append("\tif (unlikely(singleton == nullptr)) {") result.append("\tif (unlikely(singleton == nullptr)) {")
result.append( result.append(
f"\t\tGDExtensionObjectPtr singleton_obj = internal::gdextension_interface_global_get_singleton({class_name}::get_class_static()._native_ptr());" f"\t\tGDExtensionObjectPtr singleton_obj = internal::gdextension_interface_global_get_singleton({class_name}::get_class_static()._native_ptr());"
@ -1594,11 +1645,22 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
result.append("#ifdef DEBUG_ENABLED") result.append("#ifdef DEBUG_ENABLED")
result.append("\t\tERR_FAIL_NULL_V(singleton, nullptr);") result.append("\t\tERR_FAIL_NULL_V(singleton, nullptr);")
result.append("#endif // DEBUG_ENABLED") result.append("#endif // DEBUG_ENABLED")
result.append("\t\tif (likely(singleton)) {")
result.append(f"\t\t\tClassDB::_register_engine_singleton({class_name}::get_class_static(), singleton);")
result.append("\t\t}")
result.append("\t}") result.append("\t}")
result.append("\treturn singleton;") result.append("\treturn singleton;")
result.append("}") result.append("}")
result.append("") result.append("")
result.append(f"{class_name}::~{class_name}() {{")
result.append("\tif (singleton == this) {")
result.append(f"\t\tClassDB::_unregister_engine_singleton({class_name}::get_class_static());")
result.append("\t\tsingleton = nullptr;")
result.append("\t}")
result.append("}")
result.append("")
if "methods" in class_api: if "methods" in class_api:
for method in class_api["methods"]: for method in class_api["methods"]:
if method["is_virtual"]: if method["is_virtual"]:
@ -2384,7 +2446,7 @@ def correct_typed_array(type_name):
return type_name return type_name
def correct_type(type_name, meta=None): def correct_type(type_name, meta=None, use_alias=True):
type_conversion = {"float": "double", "int": "int64_t", "Nil": "Variant"} type_conversion = {"float": "double", "int": "int64_t", "Nil": "Variant"}
if meta != None: if meta != None:
if "int" in meta: if "int" in meta:
@ -2400,11 +2462,15 @@ def correct_type(type_name, meta=None):
if is_enum(type_name): if is_enum(type_name):
if is_bitfield(type_name): if is_bitfield(type_name):
base_class = get_enum_class(type_name) base_class = get_enum_class(type_name)
if use_alias and base_class in CLASS_ALIASES:
base_class = CLASS_ALIASES[base_class]
if base_class == "GlobalConstants": if base_class == "GlobalConstants":
return f"BitField<{get_enum_name(type_name)}>" return f"BitField<{get_enum_name(type_name)}>"
return f"BitField<{base_class}::{get_enum_name(type_name)}>" return f"BitField<{base_class}::{get_enum_name(type_name)}>"
else: else:
base_class = get_enum_class(type_name) base_class = get_enum_class(type_name)
if use_alias and base_class in CLASS_ALIASES:
base_class = CLASS_ALIASES[base_class]
if base_class == "GlobalConstants": if base_class == "GlobalConstants":
return f"{get_enum_name(type_name)}" return f"{get_enum_name(type_name)}"
return f"{base_class}::{get_enum_name(type_name)}" return f"{base_class}::{get_enum_name(type_name)}"

View File

@ -88,14 +88,15 @@ protected:
::godot::List<::godot::PropertyInfo> plist_owned; ::godot::List<::godot::PropertyInfo> plist_owned;
void _postinitialize(); void _postinitialize();
virtual void _notificationv(int32_t p_what, bool p_reversed = false) {}
Wrapped(const StringName p_godot_class); Wrapped(const StringName p_godot_class);
Wrapped(GodotObject *p_godot_object); Wrapped(GodotObject *p_godot_object);
virtual ~Wrapped() {} virtual ~Wrapped() {}
public: public:
static StringName &get_class_static() { static const StringName &get_class_static() {
static StringName string_name = StringName("Wrapped"); static const StringName string_name = StringName("Wrapped");
return string_name; return string_name;
} }
@ -193,6 +194,7 @@ protected:
\ \
public: \ public: \
typedef m_class self_type; \ typedef m_class self_type; \
typedef m_inherits parent_type; \
\ \
static void initialize_class() { \ static void initialize_class() { \
static bool initialized = false; \ static bool initialized = false; \
@ -207,12 +209,12 @@ public:
initialized = true; \ initialized = true; \
} \ } \
\ \
static ::godot::StringName &get_class_static() { \ static const ::godot::StringName &get_class_static() { \
static ::godot::StringName string_name = ::godot::StringName(#m_class); \ static const ::godot::StringName string_name = ::godot::StringName(#m_class); \
return string_name; \ return string_name; \
} \ } \
\ \
static ::godot::StringName &get_parent_class_static() { \ static const ::godot::StringName &get_parent_class_static() { \
return m_inherits::get_class_static(); \ return m_inherits::get_class_static(); \
} \ } \
\ \
@ -352,6 +354,11 @@ public:
_gde_binding_reference_callback, \ _gde_binding_reference_callback, \
}; \ }; \
\ \
protected: \
virtual void _notificationv(int32_t p_what, bool p_reversed = false) override { \
m_class::notification_bind(this, p_what, p_reversed); \
} \
\
private: private:
// Don't use this for your classes, use GDCLASS() instead. // Don't use this for your classes, use GDCLASS() instead.
@ -359,6 +366,7 @@ private:
private: \ private: \
inline static ::godot::internal::EngineClassRegistration<m_class> _gde_engine_class_registration_helper; \ inline static ::godot::internal::EngineClassRegistration<m_class> _gde_engine_class_registration_helper; \
void operator=(const m_class &p_rval) {} \ void operator=(const m_class &p_rval) {} \
friend class ::godot::ClassDB; \
\ \
protected: \ protected: \
virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override { \ virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override { \
@ -368,6 +376,8 @@ protected:
m_class(const char *p_godot_class) : m_inherits(p_godot_class) {} \ m_class(const char *p_godot_class) : m_inherits(p_godot_class) {} \
m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {} \ m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {} \
\ \
static void _bind_methods() {} \
\
static void (*_get_bind_methods())() { \ static void (*_get_bind_methods())() { \
return nullptr; \ return nullptr; \
} \ } \
@ -410,15 +420,16 @@ protected:
\ \
public: \ public: \
typedef m_class self_type; \ typedef m_class self_type; \
typedef m_inherits parent_type; \
\ \
static void initialize_class() {} \ static void initialize_class() {} \
\ \
static ::godot::StringName &get_class_static() { \ static const ::godot::StringName &get_class_static() { \
static ::godot::StringName string_name = ::godot::StringName(#m_alias_for); \ static const ::godot::StringName string_name = ::godot::StringName(#m_alias_for); \
return string_name; \ return string_name; \
} \ } \
\ \
static ::godot::StringName &get_parent_class_static() { \ static const ::godot::StringName &get_parent_class_static() { \
return m_inherits::get_class_static(); \ return m_inherits::get_class_static(); \
} \ } \
\ \

View File

@ -45,6 +45,7 @@
#include <godot_cpp/variant/callable_method_pointer.hpp> #include <godot_cpp/variant/callable_method_pointer.hpp>
#include <list> #include <list>
#include <mutex>
#include <set> #include <set>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@ -104,6 +105,8 @@ private:
static std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *> instance_binding_callbacks; static std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *> instance_binding_callbacks;
// Used to remember the custom class registration order. // Used to remember the custom class registration order.
static std::vector<StringName> class_register_order; static std::vector<StringName> class_register_order;
static std::unordered_map<StringName, Object *> engine_singletons;
static std::mutex engine_singletons_mutex;
static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const void **p_defs, int p_defcount); 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); static void initialize_class(const ClassInfo &cl);
@ -151,6 +154,21 @@ public:
instance_binding_callbacks[p_name] = p_callbacks; instance_binding_callbacks[p_name] = p_callbacks;
} }
static void _register_engine_singleton(const StringName &p_class_name, Object *p_singleton) {
std::lock_guard<std::mutex> lock(engine_singletons_mutex);
std::unordered_map<StringName, Object *>::const_iterator i = engine_singletons.find(p_class_name);
if (i != engine_singletons.end()) {
ERR_FAIL_COND((*i).second != p_singleton);
return;
}
engine_singletons[p_class_name] = p_singleton;
}
static void _unregister_engine_singleton(const StringName &p_class_name) {
std::lock_guard<std::mutex> lock(engine_singletons_mutex);
engine_singletons.erase(p_class_name);
}
template <typename N, typename M, typename... VarArgs> template <typename N, typename M, typename... VarArgs>
static MethodBind *bind_method(N p_method_name, M p_method, VarArgs... p_args); static MethodBind *bind_method(N p_method_name, M p_method, VarArgs... p_args);
@ -198,6 +216,8 @@ public:
template <typename T, bool is_abstract> template <typename T, bool is_abstract>
void ClassDB::_register_class(bool p_virtual, bool p_exposed) { void ClassDB::_register_class(bool p_virtual, bool p_exposed) {
static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS."); static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
static_assert(!FunctionsAreSame<T::self_type::_bind_methods, T::parent_type::_bind_methods>::value, "Class must declare 'static void _bind_methods'.");
static_assert(!std::is_abstract_v<T> || is_abstract, "Class is abstract, please use GDREGISTER_ABSTRACT_CLASS.");
instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks; instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks;
// Register this class within our plugin // Register this class within our plugin
@ -325,4 +345,6 @@ MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, StringName p_name, M p
} // namespace godot } // namespace godot
CLASSDB_SINGLETON_VARIANT_CAST;
#endif // GODOT_CLASS_DB_HPP #endif // GODOT_CLASS_DB_HPP

View File

@ -613,6 +613,14 @@ inline bool is_inf(double p_val) {
return std::isinf(p_val); return std::isinf(p_val);
} }
inline bool is_finite(float p_val) {
return std::isfinite(p_val);
}
inline bool is_finite(double p_val) {
return std::isfinite(p_val);
}
inline bool is_equal_approx(float a, float b) { inline bool is_equal_approx(float a, float b) {
// Check for exact equality first, required to handle "infinity" values. // Check for exact equality first, required to handle "infinity" values.
if (a == b) { if (a == b) {

View File

@ -170,11 +170,11 @@ template <typename T>
struct PtrToArg<T *> { struct PtrToArg<T *> {
static_assert(std::is_base_of<Object, T>::value, "Cannot encode non-Object value as an Object"); static_assert(std::is_base_of<Object, T>::value, "Cannot encode non-Object value as an Object");
_FORCE_INLINE_ static T *convert(const void *p_ptr) { _FORCE_INLINE_ static T *convert(const void *p_ptr) {
return reinterpret_cast<T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr)))); return likely(p_ptr) ? reinterpret_cast<T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr)))) : nullptr;
} }
typedef Object *EncodeT; typedef Object *EncodeT;
_FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) { _FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) {
*reinterpret_cast<const void **>(p_ptr) = p_var ? p_var->_owner : nullptr; *reinterpret_cast<const void **>(p_ptr) = likely(p_var) ? p_var->_owner : nullptr;
} }
}; };
@ -182,11 +182,11 @@ template <typename T>
struct PtrToArg<const T *> { struct PtrToArg<const T *> {
static_assert(std::is_base_of<Object, T>::value, "Cannot encode non-Object value as an Object"); static_assert(std::is_base_of<Object, T>::value, "Cannot encode non-Object value as an Object");
_FORCE_INLINE_ static const T *convert(const void *p_ptr) { _FORCE_INLINE_ static const T *convert(const void *p_ptr) {
return reinterpret_cast<const T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr)))); return likely(p_ptr) ? reinterpret_cast<const T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr)))) : nullptr;
} }
typedef const Object *EncodeT; typedef const Object *EncodeT;
_FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) { _FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) {
*reinterpret_cast<const void **>(p_ptr) = p_var ? p_var->_owner : nullptr; *reinterpret_cast<const void **>(p_ptr) = likely(p_var) ? p_var->_owner : nullptr;
} }
}; };

View File

@ -72,6 +72,40 @@ struct PropertyInfo {
PropertyInfo(const GDExtensionPropertyInfo *p_info) : PropertyInfo(const GDExtensionPropertyInfo *p_info) :
PropertyInfo(p_info->type, *reinterpret_cast<StringName *>(p_info->name), (PropertyHint)p_info->hint, *reinterpret_cast<String *>(p_info->hint_string), p_info->usage, *reinterpret_cast<StringName *>(p_info->class_name)) {} PropertyInfo(p_info->type, *reinterpret_cast<StringName *>(p_info->name), (PropertyHint)p_info->hint, *reinterpret_cast<String *>(p_info->hint_string), p_info->usage, *reinterpret_cast<StringName *>(p_info->class_name)) {}
operator Dictionary() const {
Dictionary dict;
dict["name"] = name;
dict["class_name"] = class_name;
dict["type"] = type;
dict["hint"] = hint;
dict["hint_string"] = hint_string;
dict["usage"] = usage;
return dict;
}
static PropertyInfo from_dict(const Dictionary &p_dict) {
PropertyInfo pi;
if (p_dict.has("type")) {
pi.type = Variant::Type(int(p_dict["type"]));
}
if (p_dict.has("name")) {
pi.name = p_dict["name"];
}
if (p_dict.has("class_name")) {
pi.class_name = p_dict["class_name"];
}
if (p_dict.has("hint")) {
pi.hint = PropertyHint(int(p_dict["hint"]));
}
if (p_dict.has("hint_string")) {
pi.hint_string = p_dict["hint_string"];
}
if (p_dict.has("usage")) {
pi.usage = p_dict["usage"];
}
return pi;
}
void _update(GDExtensionPropertyInfo *p_info) { void _update(GDExtensionPropertyInfo *p_info) {
p_info->type = (GDExtensionVariantType)type; p_info->type = (GDExtensionVariantType)type;
*(reinterpret_cast<StringName *>(p_info->name)) = name; *(reinterpret_cast<StringName *>(p_info->name)) = name;

View File

@ -58,6 +58,16 @@ struct TypesAreSame<A, A> {
static bool const value = true; static bool const value = true;
}; };
template <auto A, auto B>
struct FunctionsAreSame {
static bool const value = false;
};
template <auto A>
struct FunctionsAreSame<A, A> {
static bool const value = true;
};
template <typename B, typename D> template <typename B, typename D>
struct TypeInherits { struct TypeInherits {
static D *get_d(); static D *get_d();

View File

@ -160,6 +160,7 @@ extern "C" GDExtensionInterfaceObjectDestroy gdextension_interface_object_destro
extern "C" GDExtensionInterfaceGlobalGetSingleton gdextension_interface_global_get_singleton; extern "C" GDExtensionInterfaceGlobalGetSingleton gdextension_interface_global_get_singleton;
extern "C" GDExtensionInterfaceObjectGetInstanceBinding gdextension_interface_object_get_instance_binding; extern "C" GDExtensionInterfaceObjectGetInstanceBinding gdextension_interface_object_get_instance_binding;
extern "C" GDExtensionInterfaceObjectSetInstanceBinding gdextension_interface_object_set_instance_binding; extern "C" GDExtensionInterfaceObjectSetInstanceBinding gdextension_interface_object_set_instance_binding;
extern "C" GDExtensionInterfaceObjectFreeInstanceBinding gdextension_interface_object_free_instance_binding;
extern "C" GDExtensionInterfaceObjectSetInstance gdextension_interface_object_set_instance; extern "C" GDExtensionInterfaceObjectSetInstance gdextension_interface_object_set_instance;
extern "C" GDExtensionInterfaceObjectGetClassName gdextension_interface_object_get_class_name; extern "C" GDExtensionInterfaceObjectGetClassName gdextension_interface_object_get_class_name;
extern "C" GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to; extern "C" GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to;

View File

@ -65,6 +65,7 @@ struct _NO_DISCARD_ AABB {
bool operator!=(const AABB &p_rval) const; bool operator!=(const AABB &p_rval) const;
bool is_equal_approx(const AABB &p_aabb) const; bool is_equal_approx(const AABB &p_aabb) const;
bool is_finite() const;
_FORCE_INLINE_ bool intersects(const AABB &p_aabb) const; /// Both AABBs overlap _FORCE_INLINE_ bool intersects(const AABB &p_aabb) const; /// Both AABBs overlap
_FORCE_INLINE_ bool intersects_inclusive(const AABB &p_aabb) const; /// Both AABBs (or their faces) overlap _FORCE_INLINE_ bool intersects_inclusive(const AABB &p_aabb) const; /// Both AABBs (or their faces) overlap
_FORCE_INLINE_ bool encloses(const AABB &p_aabb) const; /// p_aabb is completely inside this _FORCE_INLINE_ bool encloses(const AABB &p_aabb) const; /// p_aabb is completely inside this

View File

@ -128,6 +128,7 @@ struct _NO_DISCARD_ Basis {
} }
bool is_equal_approx(const Basis &p_basis) const; bool is_equal_approx(const Basis &p_basis) const;
bool is_finite() const;
bool operator==(const Basis &p_matrix) const; bool operator==(const Basis &p_matrix) const;
bool operator!=(const Basis &p_matrix) const; bool operator!=(const Basis &p_matrix) const;

View File

@ -77,6 +77,7 @@ struct _NO_DISCARD_ Plane {
Plane operator-() const { return Plane(-normal, -d); } Plane operator-() const { return Plane(-normal, -d); }
bool is_equal_approx(const Plane &p_plane) const; bool is_equal_approx(const Plane &p_plane) const;
bool is_equal_approx_any_side(const Plane &p_plane) const; bool is_equal_approx_any_side(const Plane &p_plane) const;
bool is_finite() const;
_FORCE_INLINE_ bool operator==(const Plane &p_plane) const; _FORCE_INLINE_ bool operator==(const Plane &p_plane) const;
_FORCE_INLINE_ bool operator!=(const Plane &p_plane) const; _FORCE_INLINE_ bool operator!=(const Plane &p_plane) const;

View File

@ -55,6 +55,7 @@ struct _NO_DISCARD_ Quaternion {
} }
_FORCE_INLINE_ real_t length_squared() const; _FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Quaternion &p_quaternion) const; bool is_equal_approx(const Quaternion &p_quaternion) const;
bool is_finite() const;
real_t length() const; real_t length() const;
void normalize(); void normalize();
Quaternion normalized() const; Quaternion normalized() const;

View File

@ -209,6 +209,7 @@ struct _NO_DISCARD_ Rect2 {
} }
bool is_equal_approx(const Rect2 &p_rect) const; bool is_equal_approx(const Rect2 &p_rect) const;
bool is_finite() const;
bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; } bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; }
bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; } bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; }

View File

@ -99,6 +99,7 @@ struct _NO_DISCARD_ Transform2D {
void orthonormalize(); void orthonormalize();
Transform2D orthonormalized() const; Transform2D orthonormalized() const;
bool is_equal_approx(const Transform2D &p_transform) const; bool is_equal_approx(const Transform2D &p_transform) const;
bool is_finite() const;
Transform2D looking_at(const Vector2 &p_target) const; Transform2D looking_at(const Vector2 &p_target) const;

View File

@ -78,6 +78,7 @@ struct _NO_DISCARD_ Transform3D {
void orthogonalize(); void orthogonalize();
Transform3D orthogonalized() const; Transform3D orthogonalized() const;
bool is_equal_approx(const Transform3D &p_transform) const; bool is_equal_approx(const Transform3D &p_transform) const;
bool is_finite() const;
bool operator==(const Transform3D &p_transform) const; bool operator==(const Transform3D &p_transform) const;
bool operator!=(const Transform3D &p_transform) const; bool operator!=(const Transform3D &p_transform) const;

View File

@ -123,6 +123,7 @@ struct _NO_DISCARD_ Vector2 {
bool is_equal_approx(const Vector2 &p_v) const; bool is_equal_approx(const Vector2 &p_v) const;
bool is_zero_approx() const; bool is_zero_approx() const;
bool is_finite() const;
Vector2 operator+(const Vector2 &p_v) const; Vector2 operator+(const Vector2 &p_v) const;
void operator+=(const Vector2 &p_v); void operator+=(const Vector2 &p_v);

View File

@ -146,6 +146,7 @@ struct _NO_DISCARD_ Vector3 {
bool is_equal_approx(const Vector3 &p_v) const; bool is_equal_approx(const Vector3 &p_v) const;
bool is_zero_approx() const; bool is_zero_approx() const;
bool is_finite() const;
/* Operators */ /* Operators */

View File

@ -81,6 +81,7 @@ struct _NO_DISCARD_ Vector4 {
_FORCE_INLINE_ real_t length_squared() const; _FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Vector4 &p_vec4) const; bool is_equal_approx(const Vector4 &p_vec4) const;
bool is_zero_approx() const; bool is_zero_approx() const;
bool is_finite() const;
real_t length() const; real_t length() const;
void normalize(); void normalize();
Vector4 normalized() const; Vector4 normalized() const;

View File

@ -51,10 +51,7 @@ void Wrapped::_postinitialize() {
} }
godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _get_bindings_callbacks()); godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _get_bindings_callbacks());
if (extension_class) { if (extension_class) {
Object *obj = dynamic_cast<Object *>(this); _notificationv(Object::NOTIFICATION_POSTINITIALIZE);
if (obj) {
obj->notification(Object::NOTIFICATION_POSTINITIALIZE);
}
} }
} }

View File

@ -42,6 +42,8 @@ namespace godot {
std::unordered_map<StringName, ClassDB::ClassInfo> ClassDB::classes; std::unordered_map<StringName, ClassDB::ClassInfo> ClassDB::classes;
std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *> ClassDB::instance_binding_callbacks; std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *> ClassDB::instance_binding_callbacks;
std::vector<StringName> ClassDB::class_register_order; std::vector<StringName> ClassDB::class_register_order;
std::unordered_map<StringName, Object *> ClassDB::engine_singletons;
std::mutex ClassDB::engine_singletons_mutex;
GDExtensionInitializationLevel ClassDB::current_level = GDEXTENSION_INITIALIZATION_CORE; GDExtensionInitializationLevel ClassDB::current_level = GDEXTENSION_INITIALIZATION_CORE;
MethodDefinition D_METHOD(StringName p_name) { MethodDefinition D_METHOD(StringName p_name) {
@ -378,6 +380,22 @@ void ClassDB::deinitialize(GDExtensionInitializationLevel p_level) {
}); });
class_register_order.erase(it, class_register_order.end()); class_register_order.erase(it, class_register_order.end());
} }
if (p_level == GDEXTENSION_INITIALIZATION_CORE) {
// Make a new list of the singleton objects, since freeing the instance bindings will lead to
// elements getting removed from engine_singletons.
std::vector<Object *> singleton_objects;
{
std::lock_guard<std::mutex> lock(engine_singletons_mutex);
singleton_objects.reserve(engine_singletons.size());
for (const std::pair<StringName, Object *> &pair : engine_singletons) {
singleton_objects.push_back(pair.second);
}
}
for (std::vector<Object *>::iterator i = singleton_objects.begin(); i != singleton_objects.end(); i++) {
internal::gdextension_interface_object_free_instance_binding((*i)->_owner, internal::token);
}
}
} }
} // namespace godot } // namespace godot

View File

@ -60,8 +60,66 @@ Object *get_object_instance_binding(GodotObject *p_engine_object) {
return reinterpret_cast<Object *>(gdextension_interface_object_get_instance_binding(p_engine_object, token, binding_callbacks)); return reinterpret_cast<Object *>(gdextension_interface_object_get_instance_binding(p_engine_object, token, binding_callbacks));
} }
TypedArray<Dictionary> convert_property_list(const std::vector<PropertyInfo> &p_list) {
TypedArray<Dictionary> va;
for (const PropertyInfo &pi : p_list) {
va.push_back(Dictionary(pi));
}
return va;
}
} // namespace internal } // namespace internal
MethodInfo::operator Dictionary() const {
Dictionary dict;
dict["name"] = name;
dict["args"] = internal::convert_property_list(arguments);
Array da;
for (int i = 0; i < default_arguments.size(); i++) {
da.push_back(default_arguments[i]);
}
dict["default_args"] = da;
dict["flags"] = flags;
dict["id"] = id;
Dictionary r = return_val;
dict["return"] = r;
return dict;
}
MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) {
MethodInfo mi;
if (p_dict.has("name")) {
mi.name = p_dict["name"];
}
Array args;
if (p_dict.has("args")) {
args = p_dict["args"];
}
for (int i = 0; i < args.size(); i++) {
Dictionary d = args[i];
mi.arguments.push_back(PropertyInfo::from_dict(d));
}
Array defargs;
if (p_dict.has("default_args")) {
defargs = p_dict["default_args"];
}
for (int i = 0; i < defargs.size(); i++) {
mi.default_arguments.push_back(defargs[i]);
}
if (p_dict.has("return")) {
mi.return_val = PropertyInfo::from_dict(p_dict["return"]);
}
if (p_dict.has("flags")) {
mi.flags = p_dict["flags"];
}
return mi;
}
MethodInfo::MethodInfo() : MethodInfo::MethodInfo() :
flags(GDEXTENSION_METHOD_FLAG_NORMAL) {} flags(GDEXTENSION_METHOD_FLAG_NORMAL) {}

View File

@ -166,6 +166,7 @@ GDExtensionInterfaceObjectDestroy gdextension_interface_object_destroy = nullptr
GDExtensionInterfaceGlobalGetSingleton gdextension_interface_global_get_singleton = nullptr; GDExtensionInterfaceGlobalGetSingleton gdextension_interface_global_get_singleton = nullptr;
GDExtensionInterfaceObjectGetInstanceBinding gdextension_interface_object_get_instance_binding = nullptr; GDExtensionInterfaceObjectGetInstanceBinding gdextension_interface_object_get_instance_binding = nullptr;
GDExtensionInterfaceObjectSetInstanceBinding gdextension_interface_object_set_instance_binding = nullptr; GDExtensionInterfaceObjectSetInstanceBinding gdextension_interface_object_set_instance_binding = nullptr;
GDExtensionInterfaceObjectFreeInstanceBinding gdextension_interface_object_free_instance_binding = nullptr;
GDExtensionInterfaceObjectSetInstance gdextension_interface_object_set_instance = nullptr; GDExtensionInterfaceObjectSetInstance gdextension_interface_object_set_instance = nullptr;
GDExtensionInterfaceObjectGetClassName gdextension_interface_object_get_class_name = nullptr; GDExtensionInterfaceObjectGetClassName gdextension_interface_object_get_class_name = nullptr;
GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to = nullptr; GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to = nullptr;
@ -403,6 +404,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
LOAD_PROC_ADDRESS(global_get_singleton, GDExtensionInterfaceGlobalGetSingleton); LOAD_PROC_ADDRESS(global_get_singleton, GDExtensionInterfaceGlobalGetSingleton);
LOAD_PROC_ADDRESS(object_get_instance_binding, GDExtensionInterfaceObjectGetInstanceBinding); LOAD_PROC_ADDRESS(object_get_instance_binding, GDExtensionInterfaceObjectGetInstanceBinding);
LOAD_PROC_ADDRESS(object_set_instance_binding, GDExtensionInterfaceObjectSetInstanceBinding); LOAD_PROC_ADDRESS(object_set_instance_binding, GDExtensionInterfaceObjectSetInstanceBinding);
LOAD_PROC_ADDRESS(object_free_instance_binding, GDExtensionInterfaceObjectFreeInstanceBinding);
LOAD_PROC_ADDRESS(object_set_instance, GDExtensionInterfaceObjectSetInstance); LOAD_PROC_ADDRESS(object_set_instance, GDExtensionInterfaceObjectSetInstance);
LOAD_PROC_ADDRESS(object_get_class_name, GDExtensionInterfaceObjectGetClassName); LOAD_PROC_ADDRESS(object_get_class_name, GDExtensionInterfaceObjectGetClassName);
LOAD_PROC_ADDRESS(object_cast_to, GDExtensionInterfaceObjectCastTo); LOAD_PROC_ADDRESS(object_cast_to, GDExtensionInterfaceObjectCastTo);

View File

@ -78,6 +78,10 @@ bool AABB::is_equal_approx(const AABB &p_aabb) const {
return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size); return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size);
} }
bool AABB::is_finite() const {
return position.is_finite() && size.is_finite();
}
AABB AABB::intersection(const AABB &p_aabb) const { AABB AABB::intersection(const AABB &p_aabb) const {
#ifdef MATH_CHECKS #ifdef MATH_CHECKS
if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) { if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) {

View File

@ -692,6 +692,10 @@ bool Basis::is_equal_approx(const Basis &p_basis) const {
return rows[0].is_equal_approx(p_basis.rows[0]) && rows[1].is_equal_approx(p_basis.rows[1]) && rows[2].is_equal_approx(p_basis.rows[2]); return rows[0].is_equal_approx(p_basis.rows[0]) && rows[1].is_equal_approx(p_basis.rows[1]) && rows[2].is_equal_approx(p_basis.rows[2]);
} }
bool Basis::is_finite() const {
return rows[0].is_finite() && rows[1].is_finite() && rows[2].is_finite();
}
bool Basis::operator==(const Basis &p_matrix) const { bool Basis::operator==(const Basis &p_matrix) const {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) { for (int j = 0; j < 3; j++) {

View File

@ -178,6 +178,10 @@ bool Plane::is_equal_approx(const Plane &p_plane) const {
return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d); return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d);
} }
bool Plane::is_finite() const {
return normal.is_finite() && Math::is_finite(d);
}
Plane::operator String() const { Plane::operator String() const {
return "[N: " + normal.operator String() + ", D: " + String::num_real(d, false) + "]"; return "[N: " + normal.operator String() + ", D: " + String::num_real(d, false) + "]";
} }

View File

@ -81,6 +81,10 @@ bool Quaternion::is_equal_approx(const Quaternion &p_quaternion) const {
return Math::is_equal_approx(x, p_quaternion.x) && Math::is_equal_approx(y, p_quaternion.y) && Math::is_equal_approx(z, p_quaternion.z) && Math::is_equal_approx(w, p_quaternion.w); return Math::is_equal_approx(x, p_quaternion.x) && Math::is_equal_approx(y, p_quaternion.y) && Math::is_equal_approx(z, p_quaternion.z) && Math::is_equal_approx(w, p_quaternion.w);
} }
bool Quaternion::is_finite() const {
return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z) && Math::is_finite(w);
}
real_t Quaternion::length() const { real_t Quaternion::length() const {
return Math::sqrt(length_squared()); return Math::sqrt(length_squared());
} }

View File

@ -40,6 +40,10 @@ bool Rect2::is_equal_approx(const Rect2 &p_rect) const {
return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size); return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size);
} }
bool Rect2::is_finite() const {
return position.is_finite() && size.is_finite();
}
bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const { bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const {
#ifdef MATH_CHECKS #ifdef MATH_CHECKS
if (unlikely(size.x < 0 || size.y < 0)) { if (unlikely(size.x < 0 || size.y < 0)) {

View File

@ -170,6 +170,10 @@ bool Transform2D::is_equal_approx(const Transform2D &p_transform) const {
return columns[0].is_equal_approx(p_transform.columns[0]) && columns[1].is_equal_approx(p_transform.columns[1]) && columns[2].is_equal_approx(p_transform.columns[2]); return columns[0].is_equal_approx(p_transform.columns[0]) && columns[1].is_equal_approx(p_transform.columns[1]) && columns[2].is_equal_approx(p_transform.columns[2]);
} }
bool Transform2D::is_finite() const {
return columns[0].is_finite() && columns[1].is_finite() && columns[2].is_finite();
}
Transform2D Transform2D::looking_at(const Vector2 &p_target) const { Transform2D Transform2D::looking_at(const Vector2 &p_target) const {
Transform2D return_trans = Transform2D(get_rotation(), get_origin()); Transform2D return_trans = Transform2D(get_rotation(), get_origin());
Vector2 target_position = affine_inverse().xform(p_target); Vector2 target_position = affine_inverse().xform(p_target);

View File

@ -175,6 +175,10 @@ bool Transform3D::is_equal_approx(const Transform3D &p_transform) const {
return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin); return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin);
} }
bool Transform3D::is_finite() const {
return basis.is_finite() && origin.is_finite();
}
bool Transform3D::operator==(const Transform3D &p_transform) const { bool Transform3D::operator==(const Transform3D &p_transform) const {
return (basis == p_transform.basis && origin == p_transform.origin); return (basis == p_transform.basis && origin == p_transform.origin);
} }

View File

@ -188,6 +188,10 @@ bool Vector2::is_zero_approx() const {
return Math::is_zero_approx(x) && Math::is_zero_approx(y); return Math::is_zero_approx(x) && Math::is_zero_approx(y);
} }
bool Vector2::is_finite() const {
return Math::is_finite(x) && Math::is_finite(y);
}
Vector2::operator String() const { Vector2::operator String() const {
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")"; return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")";
} }

View File

@ -141,6 +141,10 @@ bool Vector3::is_zero_approx() const {
return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z); return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z);
} }
bool Vector3::is_finite() const {
return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z);
}
Vector3::operator String() const { Vector3::operator String() const {
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")"; return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")";
} }

View File

@ -67,6 +67,10 @@ bool Vector4::is_zero_approx() const {
return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z) && Math::is_zero_approx(w); return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z) && Math::is_zero_approx(w);
} }
bool Vector4::is_finite() const {
return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z) && Math::is_finite(w);
}
real_t Vector4::length() const { real_t Vector4::length() const {
return Math::sqrt(length_squared()); return Math::sqrt(length_squared());
} }

View File

@ -185,6 +185,10 @@ func _ready():
control.queue_free() control.queue_free()
sprite.queue_free() sprite.queue_free()
# Test that passing null for objects works as expected too.
var example_null : Example = null
assert_equal(example.test_object_cast_to_node(example_null), false)
# Test conversions to and from Variant. # Test conversions to and from Variant.
assert_equal(example.test_variant_vector2i_conversion(Vector2i(1, 1)), Vector2i(1, 1)) assert_equal(example.test_variant_vector2i_conversion(Vector2i(1, 1)), Vector2i(1, 1))
assert_equal(example.test_variant_vector2i_conversion(Vector2(1.0, 1.0)), Vector2i(1, 1)) assert_equal(example.test_variant_vector2i_conversion(Vector2(1.0, 1.0)), Vector2i(1, 1))
@ -242,6 +246,9 @@ func _ready():
assert_equal(new_example_ref.was_post_initialized(), true) assert_equal(new_example_ref.was_post_initialized(), true)
assert_equal(example.test_post_initialize(), true) assert_equal(example.test_post_initialize(), true)
# Test that we can access an engine singleton.
assert_equal(example.test_use_engine_singleton(), OS.get_name())
# Test that notifications happen on both parent and child classes. # Test that notifications happen on both parent and child classes.
var example_child = $ExampleChild var example_child = $ExampleChild
assert_equal(example_child.get_value1(), 11) assert_equal(example_child.get_value1(), 11)

View File

@ -11,6 +11,7 @@
#include <godot_cpp/classes/label.hpp> #include <godot_cpp/classes/label.hpp>
#include <godot_cpp/classes/multiplayer_api.hpp> #include <godot_cpp/classes/multiplayer_api.hpp>
#include <godot_cpp/classes/multiplayer_peer.hpp> #include <godot_cpp/classes/multiplayer_peer.hpp>
#include <godot_cpp/classes/os.hpp>
#include <godot_cpp/variant/utility_functions.hpp> #include <godot_cpp/variant/utility_functions.hpp>
using namespace godot; using namespace godot;
@ -231,6 +232,8 @@ void Example::_bind_methods() {
ClassDB::bind_method(D_METHOD("callable_bind"), &Example::callable_bind); ClassDB::bind_method(D_METHOD("callable_bind"), &Example::callable_bind);
ClassDB::bind_method(D_METHOD("test_post_initialize"), &Example::test_post_initialize); ClassDB::bind_method(D_METHOD("test_post_initialize"), &Example::test_post_initialize);
ClassDB::bind_method(D_METHOD("test_use_engine_singleton"), &Example::test_use_engine_singleton);
ClassDB::bind_static_method("Example", D_METHOD("test_static", "a", "b"), &Example::test_static); 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); ClassDB::bind_static_method("Example", D_METHOD("test_static2"), &Example::test_static2);
@ -654,3 +657,7 @@ void ExampleChild::_notification(int p_what) {
value2 = 33; value2 = 33;
} }
} }
String Example::test_use_engine_singleton() const {
return OS::get_singleton()->get_name();
}

View File

@ -182,6 +182,8 @@ public:
// Virtual function override (no need to bind manually). // Virtual function override (no need to bind manually).
virtual bool _has_point(const Vector2 &point) const override; virtual bool _has_point(const Vector2 &point) const override;
virtual void _input(const Ref<InputEvent> &event) override; virtual void _input(const Ref<InputEvent> &event) override;
String test_use_engine_singleton() const;
}; };
VARIANT_ENUM_CAST(Example::Constants); VARIANT_ENUM_CAST(Example::Constants);