|
@@ -76,6 +76,21 @@ class PlaceholderExtensionInstance {
|
|
StringName class_name;
|
|
StringName class_name;
|
|
HashMap<StringName, Variant> properties;
|
|
HashMap<StringName, Variant> properties;
|
|
|
|
|
|
|
|
+ // Checks if a property is from a runtime class, and not a non-runtime base class.
|
|
|
|
+ bool is_runtime_property(const StringName &p_property_name) {
|
|
|
|
+ StringName current_class_name = class_name;
|
|
|
|
+
|
|
|
|
+ while (ClassDB::is_class_runtime(current_class_name)) {
|
|
|
|
+ if (ClassDB::has_property(current_class_name, p_property_name, true)) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ current_class_name = ClassDB::get_parent_class(current_class_name);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
public:
|
|
public:
|
|
PlaceholderExtensionInstance(const StringName &p_class_name) {
|
|
PlaceholderExtensionInstance(const StringName &p_class_name) {
|
|
class_name = p_class_name;
|
|
class_name = p_class_name;
|
|
@@ -83,27 +98,24 @@ public:
|
|
|
|
|
|
~PlaceholderExtensionInstance() {}
|
|
~PlaceholderExtensionInstance() {}
|
|
|
|
|
|
- void set(const StringName &p_name, const Variant &p_value) {
|
|
|
|
- bool is_default_valid = false;
|
|
|
|
- Variant default_value = ClassDB::class_get_default_property_value(class_name, p_name, &is_default_valid);
|
|
|
|
-
|
|
|
|
- // If there's a default value, then we know it's a valid property.
|
|
|
|
- if (is_default_valid) {
|
|
|
|
|
|
+ void set(const StringName &p_name, const Variant &p_value, bool &r_valid) {
|
|
|
|
+ r_valid = is_runtime_property(p_name);
|
|
|
|
+ if (r_valid) {
|
|
properties[p_name] = p_value;
|
|
properties[p_name] = p_value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- Variant get(const StringName &p_name) {
|
|
|
|
|
|
+ Variant get(const StringName &p_name, bool &r_valid) {
|
|
const Variant *value = properties.getptr(p_name);
|
|
const Variant *value = properties.getptr(p_name);
|
|
Variant ret;
|
|
Variant ret;
|
|
|
|
|
|
if (value) {
|
|
if (value) {
|
|
ret = *value;
|
|
ret = *value;
|
|
|
|
+ r_valid = true;
|
|
} else {
|
|
} else {
|
|
- bool is_default_valid = false;
|
|
|
|
- Variant default_value = ClassDB::class_get_default_property_value(class_name, p_name, &is_default_valid);
|
|
|
|
- if (is_default_valid) {
|
|
|
|
- ret = default_value;
|
|
|
|
|
|
+ r_valid = is_runtime_property(p_name);
|
|
|
|
+ if (r_valid) {
|
|
|
|
+ ret = ClassDB::class_get_default_property_value(class_name, p_name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -115,10 +127,10 @@ public:
|
|
const StringName &name = *(StringName *)p_name;
|
|
const StringName &name = *(StringName *)p_name;
|
|
const Variant &value = *(const Variant *)p_value;
|
|
const Variant &value = *(const Variant *)p_value;
|
|
|
|
|
|
- self->set(name, value);
|
|
|
|
|
|
+ bool valid = false;
|
|
|
|
+ self->set(name, value, valid);
|
|
|
|
|
|
- // We have to return true so Godot doesn't try to call the real setter function.
|
|
|
|
- return true;
|
|
|
|
|
|
+ return valid;
|
|
}
|
|
}
|
|
|
|
|
|
static GDExtensionBool placeholder_instance_get(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret) {
|
|
static GDExtensionBool placeholder_instance_get(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret) {
|
|
@@ -126,10 +138,10 @@ public:
|
|
const StringName &name = *(StringName *)p_name;
|
|
const StringName &name = *(StringName *)p_name;
|
|
Variant *value = (Variant *)r_ret;
|
|
Variant *value = (Variant *)r_ret;
|
|
|
|
|
|
- *value = self->get(name);
|
|
|
|
|
|
+ bool valid = false;
|
|
|
|
+ *value = self->get(name, valid);
|
|
|
|
|
|
- // We have to return true so Godot doesn't try to call the real getter function.
|
|
|
|
- return true;
|
|
|
|
|
|
+ return valid;
|
|
}
|
|
}
|
|
|
|
|
|
static const GDExtensionPropertyInfo *placeholder_instance_get_property_list(GDExtensionClassInstancePtr p_instance, uint32_t *r_count) {
|
|
static const GDExtensionPropertyInfo *placeholder_instance_get_property_list(GDExtensionClassInstancePtr p_instance, uint32_t *r_count) {
|
|
@@ -172,9 +184,9 @@ public:
|
|
static GDExtensionObjectPtr placeholder_class_create_instance(void *p_class_userdata) {
|
|
static GDExtensionObjectPtr placeholder_class_create_instance(void *p_class_userdata) {
|
|
ClassDB::ClassInfo *ti = (ClassDB::ClassInfo *)p_class_userdata;
|
|
ClassDB::ClassInfo *ti = (ClassDB::ClassInfo *)p_class_userdata;
|
|
|
|
|
|
- // Find the closest native parent.
|
|
|
|
|
|
+ // Find the closest native parent, that isn't a runtime class.
|
|
ClassDB::ClassInfo *native_parent = ti->inherits_ptr;
|
|
ClassDB::ClassInfo *native_parent = ti->inherits_ptr;
|
|
- while (native_parent->gdextension) {
|
|
|
|
|
|
+ while (native_parent->gdextension || native_parent->is_runtime) {
|
|
native_parent = native_parent->inherits_ptr;
|
|
native_parent = native_parent->inherits_ptr;
|
|
}
|
|
}
|
|
ERR_FAIL_NULL_V(native_parent->creation_func, nullptr);
|
|
ERR_FAIL_NULL_V(native_parent->creation_func, nullptr);
|
|
@@ -1952,6 +1964,14 @@ bool ClassDB::is_class_reloadable(const StringName &p_class) {
|
|
return ti->reloadable;
|
|
return ti->reloadable;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+bool ClassDB::is_class_runtime(const StringName &p_class) {
|
|
|
|
+ OBJTYPE_RLOCK;
|
|
|
|
+
|
|
|
|
+ ClassInfo *ti = classes.getptr(p_class);
|
|
|
|
+ ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'.");
|
|
|
|
+ return ti->is_runtime;
|
|
|
|
+}
|
|
|
|
+
|
|
void ClassDB::add_resource_base_extension(const StringName &p_extension, const StringName &p_class) {
|
|
void ClassDB::add_resource_base_extension(const StringName &p_extension, const StringName &p_class) {
|
|
if (resource_base_extensions.has(p_extension)) {
|
|
if (resource_base_extensions.has(p_extension)) {
|
|
return;
|
|
return;
|
|
@@ -2063,6 +2083,11 @@ void ClassDB::register_extension_class(ObjectGDExtension *p_extension) {
|
|
|
|
|
|
ClassInfo *parent = classes.getptr(p_extension->parent_class_name);
|
|
ClassInfo *parent = classes.getptr(p_extension->parent_class_name);
|
|
|
|
|
|
|
|
+#ifdef TOOLS_ENABLED
|
|
|
|
+ // @todo This is a limitation of the current implementation, but it should be possible to remove.
|
|
|
|
+ ERR_FAIL_COND_MSG(p_extension->is_runtime && parent->gdextension && !parent->is_runtime, "Extension runtime class " + String(p_extension->class_name) + " cannot descend from " + parent->name + " which isn't also a runtime class");
|
|
|
|
+#endif
|
|
|
|
+
|
|
ClassInfo c;
|
|
ClassInfo c;
|
|
c.api = p_extension->editor_class ? API_EDITOR_EXTENSION : API_EXTENSION;
|
|
c.api = p_extension->editor_class ? API_EDITOR_EXTENSION : API_EXTENSION;
|
|
c.gdextension = p_extension;
|
|
c.gdextension = p_extension;
|