GDScript: Fix non-global class export
This commit is contained in:
parent
b75f0485ba
commit
24b6edcd38
7 changed files with 72 additions and 57 deletions
|
@ -314,7 +314,7 @@
|
|||
@export var image_array: Array[Image]
|
||||
@export var node_array: Array[Node]
|
||||
[/codeblock]
|
||||
[b]Note:[/b] Custom resources and nodes must be registered as global classes using [code]class_name[/code].
|
||||
[b]Note:[/b] Custom resources and nodes should be registered as global classes using [code]class_name[/code], since the Inspector currently only supports global classes. Otherwise, a less specific type will be exported instead.
|
||||
[b]Note:[/b] Node export is only supported in [Node]-derived classes and has a number of other limitations.
|
||||
</description>
|
||||
</annotation>
|
||||
|
|
|
@ -4160,6 +4160,64 @@ static String _get_annotation_error_string(const StringName &p_annotation_name,
|
|||
return vformat(R"("%s" annotation requires a variable of type %s, but type "%s" was given instead.)", p_annotation_name, string, p_provided_type.to_string());
|
||||
}
|
||||
|
||||
static StringName _find_narrowest_native_or_global_class(const GDScriptParser::DataType &p_type) {
|
||||
switch (p_type.kind) {
|
||||
case GDScriptParser::DataType::NATIVE: {
|
||||
if (p_type.is_meta_type) {
|
||||
return Object::get_class_static(); // `GDScriptNativeClass` is not an exposed class.
|
||||
}
|
||||
return p_type.native_type;
|
||||
} break;
|
||||
case GDScriptParser::DataType::SCRIPT: {
|
||||
Ref<Script> script;
|
||||
if (p_type.script_type.is_valid()) {
|
||||
script = p_type.script_type;
|
||||
} else {
|
||||
script = ResourceLoader::load(p_type.script_path, SNAME("Script"));
|
||||
}
|
||||
|
||||
if (p_type.is_meta_type) {
|
||||
return script.is_valid() ? script->get_class() : Script::get_class_static();
|
||||
}
|
||||
if (script.is_null()) {
|
||||
return p_type.native_type;
|
||||
}
|
||||
if (script->get_global_name() != StringName()) {
|
||||
return script->get_global_name();
|
||||
}
|
||||
|
||||
Ref<Script> base_script = script->get_base_script();
|
||||
if (base_script.is_null()) {
|
||||
return script->get_instance_base_type();
|
||||
}
|
||||
|
||||
GDScriptParser::DataType base_type;
|
||||
base_type.kind = GDScriptParser::DataType::SCRIPT;
|
||||
base_type.builtin_type = Variant::OBJECT;
|
||||
base_type.native_type = base_script->get_instance_base_type();
|
||||
base_type.script_type = base_script;
|
||||
base_type.script_path = base_script->get_path();
|
||||
|
||||
return _find_narrowest_native_or_global_class(base_type);
|
||||
} break;
|
||||
case GDScriptParser::DataType::CLASS: {
|
||||
if (p_type.is_meta_type) {
|
||||
return GDScript::get_class_static();
|
||||
}
|
||||
if (p_type.class_type == nullptr) {
|
||||
return p_type.native_type;
|
||||
}
|
||||
if (p_type.class_type->get_global_name() != StringName()) {
|
||||
return p_type.class_type->get_global_name();
|
||||
}
|
||||
return _find_narrowest_native_or_global_class(p_type.class_type->base_type);
|
||||
} break;
|
||||
default: {
|
||||
ERR_FAIL_V(StringName());
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
template <PropertyHint t_hint, Variant::Type t_type>
|
||||
bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
|
||||
ERR_FAIL_COND_V_MSG(p_target->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
|
||||
|
@ -4302,57 +4360,9 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
|
|||
variable->export_info.hint_string = String();
|
||||
break;
|
||||
case GDScriptParser::DataType::NATIVE:
|
||||
if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
|
||||
variable->export_info.type = Variant::OBJECT;
|
||||
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
variable->export_info.hint_string = export_type.native_type;
|
||||
} else if (ClassDB::is_parent_class(export_type.native_type, SNAME("Node"))) {
|
||||
variable->export_info.type = Variant::OBJECT;
|
||||
variable->export_info.hint = PROPERTY_HINT_NODE_TYPE;
|
||||
variable->export_info.hint_string = export_type.native_type;
|
||||
} else {
|
||||
push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", p_annotation);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case GDScriptParser::DataType::SCRIPT:
|
||||
case GDScriptParser::DataType::CLASS: {
|
||||
StringName class_name;
|
||||
if (export_type.class_type) {
|
||||
class_name = export_type.class_type->get_global_name();
|
||||
}
|
||||
if (class_name == StringName()) {
|
||||
push_error(R"(Script export type must be a global class.)", p_annotation);
|
||||
return false;
|
||||
}
|
||||
if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
|
||||
variable->export_info.type = Variant::OBJECT;
|
||||
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
variable->export_info.hint_string = class_name;
|
||||
} else if (ClassDB::is_parent_class(export_type.native_type, SNAME("Node"))) {
|
||||
variable->export_info.type = Variant::OBJECT;
|
||||
variable->export_info.hint = PROPERTY_HINT_NODE_TYPE;
|
||||
variable->export_info.hint_string = class_name;
|
||||
} else {
|
||||
push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", p_annotation);
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case GDScriptParser::DataType::SCRIPT: {
|
||||
StringName class_name;
|
||||
if (export_type.script_type.is_valid()) {
|
||||
class_name = export_type.script_type->get_global_name();
|
||||
}
|
||||
if (class_name == StringName()) {
|
||||
Ref<Script> script = ResourceLoader::load(export_type.script_path, SNAME("Script"));
|
||||
if (script.is_valid()) {
|
||||
class_name = script->get_global_name();
|
||||
}
|
||||
}
|
||||
if (class_name == StringName()) {
|
||||
push_error(R"(Script export type must be a global class.)", p_annotation);
|
||||
return false;
|
||||
}
|
||||
const StringName class_name = _find_narrowest_native_or_global_class(export_type);
|
||||
if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
|
||||
variable->export_info.type = Variant::OBJECT;
|
||||
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
|
|
|
@ -2,7 +2,8 @@ class_name ExportVariableTest
|
|||
extends Node
|
||||
|
||||
const Utils = preload("../../utils.notest.gd")
|
||||
const PreloadedScript = preload("./export_variable.notest.gd")
|
||||
const PreloadedGlobalClass = preload("./export_variable_global.notest.gd")
|
||||
const PreloadedUnnamedClass = preload("./export_variable_unnamed.notest.gd")
|
||||
|
||||
# Built-in types.
|
||||
@export var test_weak_int = 1
|
||||
|
@ -24,7 +25,8 @@ const PreloadedScript = preload("./export_variable.notest.gd")
|
|||
|
||||
# Global custom classes.
|
||||
@export var test_global_class: ExportVariableTest
|
||||
@export var test_preloaded_script: PreloadedScript
|
||||
@export var test_preloaded_global_class: PreloadedGlobalClass
|
||||
@export var test_preloaded_unnamed_class: PreloadedUnnamedClass # GH-93168
|
||||
|
||||
# Arrays.
|
||||
@export var test_array: Array
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
class_name ExportPreloadedClassTest
|
||||
extends Node
|
|
@ -25,8 +25,10 @@ var test_timer: Timer = null
|
|||
hint=NODE_TYPE hint_string="Timer" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"Timer"
|
||||
var test_global_class: ExportVariableTest = null
|
||||
hint=NODE_TYPE hint_string="ExportVariableTest" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"ExportVariableTest"
|
||||
var test_preloaded_script: ExportPreloadedClassTest = null
|
||||
hint=NODE_TYPE hint_string="ExportPreloadedClassTest" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"ExportPreloadedClassTest"
|
||||
var test_preloaded_global_class: ExportVariableTestGlobalClass = null
|
||||
hint=NODE_TYPE hint_string="ExportVariableTestGlobalClass" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"ExportVariableTestGlobalClass"
|
||||
var test_preloaded_unnamed_class: Node2D = null
|
||||
hint=NODE_TYPE hint_string="Node2D" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"Node2D"
|
||||
var test_array: Array = []
|
||||
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
|
||||
var test_array_bool: Array = Array[bool]([])
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
class_name ExportVariableTestGlobalClass
|
||||
extends Node2D
|
|
@ -0,0 +1 @@
|
|||
extends Node2D
|
Loading…
Reference in a new issue