-
-
Notifications
You must be signed in to change notification settings - Fork 21.1k
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
Allow exporting custom Resource variables from GDscript #41264
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -849,6 +849,9 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s | |
constant->value = scr; | ||
expr = constant; | ||
bfn = true; | ||
} else { | ||
_set_error("Cannot load " + identifier + " (" + ScriptServer::get_global_class_path(identifier) + "). Possible error or cyclic reference."); | ||
return NULL; | ||
} | ||
} | ||
|
||
|
@@ -4638,8 +4641,40 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { | |
current_export.class_name = native_class->get_name(); | ||
|
||
} else { | ||
current_export = PropertyInfo(); | ||
_set_error("The export hint isn't a resource type."); | ||
Ref<Script> script = Object::cast_to<Script>(constant); | ||
if (script.is_valid()) { | ||
bool is_script_class = false; | ||
for (int i = 0; i < ScriptServer::get_language_count() && !is_script_class; i++) { | ||
ScriptLanguage *lang = ScriptServer::get_language(i); | ||
String base; | ||
String class_name = lang->get_global_class_name(script->get_path(), &base); | ||
if (class_name.length()) { | ||
is_script_class = true; | ||
String to_test = base; | ||
if (ScriptServer::is_global_class(base)) { | ||
to_test = ScriptServer::get_global_class_native_base(base); | ||
} | ||
if (ClassDB::is_parent_class(to_test, "Resource")) { | ||
current_export.type = Variant::OBJECT; | ||
current_export.hint = PROPERTY_HINT_RESOURCE_TYPE; | ||
current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; | ||
|
||
current_export.hint_string = class_name; | ||
current_export.class_name = class_name; | ||
} else { | ||
current_export = PropertyInfo(); | ||
_set_error("The exported script class isn't a resource type."); | ||
} | ||
} | ||
} | ||
if (!is_script_class) { | ||
current_export = PropertyInfo(); | ||
_set_error("The exported script isn't a script class."); | ||
} | ||
} else { | ||
current_export = PropertyInfo(); | ||
_set_error("The export hint isn't a resource type."); | ||
} | ||
} | ||
} else if (constant.get_type() == Variant::DICTIONARY) { | ||
// Enumeration | ||
|
@@ -4929,10 +4964,42 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { | |
member._export.hint_string = member.data_type.native_type; | ||
member._export.class_name = member.data_type.native_type; | ||
} else { | ||
_set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line); | ||
_set_error("Invalid native export type. \"" + member.data_type.native_type + "\" is not a Resource type.", member.line); | ||
return; | ||
} | ||
} else if (member.data_type.kind == DataType::SCRIPT || member.data_type.kind == DataType::GDSCRIPT) { | ||
if (member.data_type.script_type.is_null()) { | ||
_set_error("The member's data type is a script that did not load successfully.", member.line); | ||
return; | ||
} | ||
StringName class_name = ScriptServer::get_global_class_name(member.data_type.script_type->get_path()); | ||
if (class_name == StringName()) { | ||
_set_error("Invalid script export type. \"" + class_name + "\" is not a script class.", member.line); | ||
return; | ||
} | ||
if (ClassDB::is_parent_class(member.data_type.native_type, "Resource")) { | ||
member._export.type = Variant::OBJECT; | ||
member._export.hint = PROPERTY_HINT_RESOURCE_TYPE; | ||
member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; | ||
member._export.hint_string = class_name; | ||
member._export.class_name = class_name; | ||
} else { | ||
_set_error("Invalid script class export type. \"" + class_name + "\" is not a Resource type.", member.line); | ||
return; | ||
} | ||
|
||
} else if (member.data_type.kind == DataType::UNRESOLVED && ScriptServer::is_global_class(member.data_type.native_type)) { | ||
String class_name = member.data_type.native_type; | ||
if (ScriptServer::get_global_class_native_base(class_name) == "Resource") { | ||
member._export.type = Variant::OBJECT; | ||
member._export.hint = PROPERTY_HINT_RESOURCE_TYPE; | ||
member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; | ||
member._export.hint_string = class_name; | ||
member._export.class_name = class_name; | ||
} else { | ||
_set_error("Invalid script class export type. \"" + class_name + "\" is not a Resource type.", member.line); | ||
return; | ||
} | ||
} else { | ||
_set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line); | ||
return; | ||
|
@@ -6255,7 +6322,9 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data | |
switch (p_expression.kind) { | ||
case DataType::NATIVE: { | ||
if (p_container.kind != DataType::NATIVE) { | ||
// Non-native type can't be a superclass of a native type | ||
if (p_container.kind == DataType::GDSCRIPT || p_container.kind == DataType::SCRIPT) { | ||
return ScriptServer::get_global_class_name(p_container.script_type->get_path()) == p_expression.native_type; | ||
} | ||
Comment on lines
+6325
to
+6327
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this code doesn't seem to be right... but the whole type checking in 3.2 GDscript is a big mess. The type checking goes through so many code paths it's hard to keep track of everything. I did some tests and my changes don't really affect any of the cases you mentioned (I still think my code is incorrect, but it doesn't really make things worse in these cases). Comparing This PR and 3.2.2 stable I get the exact same behavior in this test script: So... I'm not really sure what is the best course of action here. I hope with the cleanup of GDscript 2.0 we get a type checker that is much easier to maintain because the 3.2 one is a bit of a nightmare. |
||
return false; | ||
} | ||
if (p_expression.is_meta_type) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some confusion about these lines.
If
member.data_type.native_type
is a StringName containing the native class type, then why would it ever become a script class name? I'm not sure of what the intent here is. I was looking in the source for wherenative_type
gets assigned a value, but I couldn't seem to find anything ingdscript_parser.h/.cpp
, so I'm not sure how a script class name is supposed to end up there. I did see thatparse_class_name()
directly assigns the parsed identifier tocurrent_class->identifier
, so that is likely what you would want to pass to the ScriptServer, unless I'm mistaken.Based on the above point, I think you should gather the
class_name
variable's value from something different. As far as I can tell with how it is used,native_type
is supposed to be an actual C++ engine class.I don't think you can simply check if the native type of the class is "Resource". After all, someone might choose to extend a different class other than Resource and make a script class out of that type. So, you should first grab the native type, and then compare it to the ClassDB in full.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
native_type
to the identifier's string here:https://github.com/godotengine/godot/blob/3.2/modules/gdscript/gdscript_parser.cpp#L5723-L5731
I agree it doesn't make much sense semantically, but I just took the information I had available, I didn't change any of the type parsing code.