mirror of
https://github.com/godotengine/godot.git
synced 2025-02-16 07:40:36 +00:00
Allow opening scenes with missing scene dependency
This commit is contained in:
parent
dfe226b933
commit
be4cbee873
@ -13,8 +13,11 @@
|
|||||||
<member name="original_class" type="String" setter="set_original_class" getter="get_original_class">
|
<member name="original_class" type="String" setter="set_original_class" getter="get_original_class">
|
||||||
The name of the class this node was supposed to be (see [method Object.get_class]).
|
The name of the class this node was supposed to be (see [method Object.get_class]).
|
||||||
</member>
|
</member>
|
||||||
|
<member name="original_scene" type="String" setter="set_original_scene" getter="get_original_scene">
|
||||||
|
Returns the path of the scene this node was instance of originally.
|
||||||
|
</member>
|
||||||
<member name="recording_properties" type="bool" setter="set_recording_properties" getter="is_recording_properties">
|
<member name="recording_properties" type="bool" setter="set_recording_properties" getter="is_recording_properties">
|
||||||
If set to [code]true[/code], allows new properties to be added on top of the existing ones with [method Object.set].
|
If [code]true[/code], allows new properties to be set along with existing ones. If [code]false[/code], only existing properties' values can be set, and new properties cannot be added.
|
||||||
</member>
|
</member>
|
||||||
</members>
|
</members>
|
||||||
</class>
|
</class>
|
||||||
|
@ -66,6 +66,14 @@ String MissingNode::get_original_class() const {
|
|||||||
return original_class;
|
return original_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MissingNode::set_original_scene(const String &p_scene) {
|
||||||
|
original_scene = p_scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
String MissingNode::get_original_scene() const {
|
||||||
|
return original_scene;
|
||||||
|
}
|
||||||
|
|
||||||
void MissingNode::set_recording_properties(bool p_enable) {
|
void MissingNode::set_recording_properties(bool p_enable) {
|
||||||
recording_properties = p_enable;
|
recording_properties = p_enable;
|
||||||
}
|
}
|
||||||
@ -77,8 +85,15 @@ bool MissingNode::is_recording_properties() const {
|
|||||||
Array MissingNode::get_configuration_warnings() const {
|
Array MissingNode::get_configuration_warnings() const {
|
||||||
// The mere existence of this node is warning.
|
// The mere existence of this node is warning.
|
||||||
Array ret;
|
Array ret;
|
||||||
ret.push_back(vformat(RTR("This node was saved as class type '%s', which was no longer available when this scene was loaded."), original_class));
|
if (!original_scene.is_empty()) {
|
||||||
ret.push_back(RTR("Data from the original node is kept as a placeholder until this type of node is available again. It can hence be safely re-saved without risk of data loss."));
|
ret.push_back(vformat(RTR("This node was an instance of scene '%s', which was no longer available when this scene was loaded."), original_scene));
|
||||||
|
ret.push_back(vformat(RTR("Saving current scene will discard instance and all its properties, including editable children edits (if existing).")));
|
||||||
|
} else if (!original_class.is_empty()) {
|
||||||
|
ret.push_back(vformat(RTR("This node was saved as class type '%s', which was no longer available when this scene was loaded."), original_class));
|
||||||
|
ret.push_back(RTR("Data from the original node is kept as a placeholder until this type of node is available again. It can hence be safely re-saved without risk of data loss."));
|
||||||
|
} else {
|
||||||
|
ret.push_back(RTR("Unrecognized missing node. Check scene dependency errors for details."));
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,11 +101,15 @@ void MissingNode::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("set_original_class", "name"), &MissingNode::set_original_class);
|
ClassDB::bind_method(D_METHOD("set_original_class", "name"), &MissingNode::set_original_class);
|
||||||
ClassDB::bind_method(D_METHOD("get_original_class"), &MissingNode::get_original_class);
|
ClassDB::bind_method(D_METHOD("get_original_class"), &MissingNode::get_original_class);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_original_scene", "name"), &MissingNode::set_original_class);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_original_scene"), &MissingNode::get_original_class);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_recording_properties", "enable"), &MissingNode::set_recording_properties);
|
ClassDB::bind_method(D_METHOD("set_recording_properties", "enable"), &MissingNode::set_recording_properties);
|
||||||
ClassDB::bind_method(D_METHOD("is_recording_properties"), &MissingNode::is_recording_properties);
|
ClassDB::bind_method(D_METHOD("is_recording_properties"), &MissingNode::is_recording_properties);
|
||||||
|
|
||||||
// Expose, but not save.
|
// Expose, but not save.
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "original_class", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_original_class", "get_original_class");
|
ADD_PROPERTY(PropertyInfo(Variant::STRING, "original_class", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_original_class", "get_original_class");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::STRING, "original_scene", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_original_scene", "get_original_scene");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "recording_properties", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_recording_properties", "is_recording_properties");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "recording_properties", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_recording_properties", "is_recording_properties");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ class MissingNode : public Node {
|
|||||||
HashMap<StringName, Variant> properties;
|
HashMap<StringName, Variant> properties;
|
||||||
|
|
||||||
String original_class;
|
String original_class;
|
||||||
|
String original_scene;
|
||||||
bool recording_properties = false;
|
bool recording_properties = false;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -52,6 +53,9 @@ public:
|
|||||||
void set_original_class(const String &p_class);
|
void set_original_class(const String &p_class);
|
||||||
String get_original_class() const;
|
String get_original_class() const;
|
||||||
|
|
||||||
|
void set_original_scene(const String &p_scene);
|
||||||
|
String get_original_scene() const;
|
||||||
|
|
||||||
void set_recording_properties(bool p_enable);
|
void set_recording_properties(bool p_enable);
|
||||||
bool is_recording_properties() const;
|
bool is_recording_properties() const;
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
|
|||||||
MissingNode *missing_node = nullptr;
|
MissingNode *missing_node = nullptr;
|
||||||
|
|
||||||
if (i == 0 && base_scene_idx >= 0) {
|
if (i == 0 && base_scene_idx >= 0) {
|
||||||
//scene inheritance on root node
|
// Scene inheritance on root node.
|
||||||
Ref<PackedScene> sdata = props[base_scene_idx];
|
Ref<PackedScene> sdata = props[base_scene_idx];
|
||||||
ERR_FAIL_COND_V(!sdata.is_valid(), nullptr);
|
ERR_FAIL_COND_V(!sdata.is_valid(), nullptr);
|
||||||
node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); //only main gets main edit state
|
node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); //only main gets main edit state
|
||||||
@ -201,14 +201,22 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (n.instance >= 0) {
|
} else if (n.instance >= 0) {
|
||||||
//instance a scene into this node
|
// Instance a scene into this node.
|
||||||
if (n.instance & FLAG_INSTANCE_IS_PLACEHOLDER) {
|
if (n.instance & FLAG_INSTANCE_IS_PLACEHOLDER) {
|
||||||
String scene_path = props[n.instance & FLAG_MASK];
|
const String scene_path = props[n.instance & FLAG_MASK];
|
||||||
if (disable_placeholders) {
|
if (disable_placeholders) {
|
||||||
Ref<PackedScene> sdata = ResourceLoader::load(scene_path, "PackedScene");
|
Ref<PackedScene> sdata = ResourceLoader::load(scene_path, "PackedScene");
|
||||||
ERR_FAIL_COND_V(!sdata.is_valid(), nullptr);
|
if (sdata.is_valid()) {
|
||||||
node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE);
|
node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE);
|
||||||
ERR_FAIL_NULL_V(node, nullptr);
|
ERR_FAIL_NULL_V(node, nullptr);
|
||||||
|
} else if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) {
|
||||||
|
missing_node = memnew(MissingNode);
|
||||||
|
missing_node->set_original_scene(scene_path);
|
||||||
|
missing_node->set_recording_properties(true);
|
||||||
|
node = missing_node;
|
||||||
|
} else {
|
||||||
|
ERR_FAIL_V_MSG(nullptr, "Placeholder scene is missing.");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
InstancePlaceholder *ip = memnew(InstancePlaceholder);
|
InstancePlaceholder *ip = memnew(InstancePlaceholder);
|
||||||
ip->set_instance_path(scene_path);
|
ip->set_instance_path(scene_path);
|
||||||
@ -216,14 +224,27 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
|
|||||||
}
|
}
|
||||||
node->set_scene_instance_load_placeholder(true);
|
node->set_scene_instance_load_placeholder(true);
|
||||||
} else {
|
} else {
|
||||||
Ref<PackedScene> sdata = props[n.instance & FLAG_MASK];
|
Ref<Resource> res = props[n.instance & FLAG_MASK];
|
||||||
ERR_FAIL_COND_V(!sdata.is_valid(), nullptr);
|
Ref<PackedScene> sdata = res;
|
||||||
node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE);
|
if (sdata.is_valid()) {
|
||||||
ERR_FAIL_NULL_V_MSG(node, nullptr, vformat("Failed to load scene dependency: \"%s\". Make sure the required scene is valid.", sdata->get_path()));
|
node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE);
|
||||||
|
ERR_FAIL_NULL_V_MSG(node, nullptr, vformat("Failed to load scene dependency: \"%s\". Make sure the required scene is valid.", sdata->get_path()));
|
||||||
|
} else if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) {
|
||||||
|
missing_node = memnew(MissingNode);
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
if (res.is_valid()) {
|
||||||
|
missing_node->set_original_scene(res->get_meta("__load_path__", ""));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
missing_node->set_recording_properties(true);
|
||||||
|
node = missing_node;
|
||||||
|
} else {
|
||||||
|
ERR_FAIL_V_MSG(nullptr, "Scene instance is missing.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (n.type == TYPE_INSTANTIATED) {
|
} else if (n.type == TYPE_INSTANTIATED) {
|
||||||
//get the node from somewhere, it likely already exists from another instance
|
// Get the node from somewhere, it likely already exists from another instance.
|
||||||
if (parent) {
|
if (parent) {
|
||||||
node = parent->_get_child_by_name(snames[n.name]);
|
node = parent->_get_child_by_name(snames[n.name]);
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
@ -233,7 +254,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//node belongs to this scene and must be created
|
// Node belongs to this scene and must be created.
|
||||||
Object *obj = ClassDB::instantiate(snames[n.type]);
|
Object *obj = ClassDB::instantiate(snames[n.type]);
|
||||||
|
|
||||||
node = Object::cast_to<Node>(obj);
|
node = Object::cast_to<Node>(obj);
|
||||||
|
@ -176,6 +176,13 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R
|
|||||||
} else {
|
} else {
|
||||||
r_res = Ref<Resource>();
|
r_res = Ref<Resource>();
|
||||||
}
|
}
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
if (r_res.is_null()) {
|
||||||
|
// Hack to allow checking original path.
|
||||||
|
r_res.instantiate();
|
||||||
|
r_res->set_meta("__load_path__", ext_resources[id].path);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
VariantParser::get_token(p_stream, token, line, r_err_str);
|
VariantParser::get_token(p_stream, token, line, r_err_str);
|
||||||
|
Loading…
Reference in New Issue
Block a user