Merge pull request #60597 from reduz/missing-node-resource-placeholders

This commit is contained in:
Rémi Verschelde 2022-05-05 15:57:50 +02:00 committed by GitHub
commit 71e41eb395
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 630 additions and 58 deletions

View file

@ -0,0 +1,90 @@
/*************************************************************************/
/* missing_resource.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "missing_resource.h"
bool MissingResource::_set(const StringName &p_name, const Variant &p_value) {
if (is_recording_properties()) {
properties.insert(p_name, p_value);
return true; //always valid to set (add)
} else {
if (!properties.has(p_name)) {
return false;
}
properties[p_name] = p_value;
return true;
}
}
bool MissingResource::_get(const StringName &p_name, Variant &r_ret) const {
if (!properties.has(p_name)) {
return false;
}
r_ret = properties[p_name];
return true;
}
void MissingResource::_get_property_list(List<PropertyInfo> *p_list) const {
for (OrderedHashMap<StringName, Variant>::ConstElement E = properties.front(); E; E = E.next()) {
p_list->push_back(PropertyInfo(E.value().get_type(), E.key()));
}
}
void MissingResource::set_original_class(const String &p_class) {
original_class = p_class;
}
String MissingResource::get_original_class() const {
return original_class;
}
void MissingResource::set_recording_properties(bool p_enable) {
recording_properties = p_enable;
}
bool MissingResource::is_recording_properties() const {
return recording_properties;
}
void MissingResource::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_original_class", "name"), &MissingResource::set_original_class);
ClassDB::bind_method(D_METHOD("get_original_class"), &MissingResource::get_original_class);
ClassDB::bind_method(D_METHOD("set_recording_properties", "enable"), &MissingResource::set_recording_properties);
ClassDB::bind_method(D_METHOD("is_recording_properties"), &MissingResource::is_recording_properties);
// 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::BOOL, "recording_properties", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_recording_properties", "is_recording_properties");
}
MissingResource::MissingResource() {
}

View file

@ -0,0 +1,63 @@
/*************************************************************************/
/* missing_resource.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef MISSING_RESOURCE_H
#define MISSING_RESOURCE_H
#include "core/io/resource.h"
#define META_PROPERTY_MISSING_RESOURCES "metadata/_missing_resources"
#define META_MISSING_RESOURCES "_missing_resources"
class MissingResource : public Resource {
GDCLASS(MissingResource, Resource)
OrderedHashMap<StringName, Variant> properties;
String original_class;
bool recording_properties = false;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
void set_original_class(const String &p_class);
String get_original_class() const;
void set_recording_properties(bool p_enable);
bool is_recording_properties() const;
MissingResource();
};
#endif // MISSING_RESOURCE_H

View file

@ -35,6 +35,7 @@
#include "core/io/file_access_compressed.h"
#include "core/io/image.h"
#include "core/io/marshalls.h"
#include "core/io/missing_resource.h"
#include "core/version.h"
//#define print_bl(m_what) print_line(m_what)
@ -728,13 +729,23 @@ Error ResourceLoaderBinary::load() {
}
}
MissingResource *missing_resource = nullptr;
if (res.is_null()) {
//did not replace
Object *obj = ClassDB::instantiate(t);
if (!obj) {
error = ERR_FILE_CORRUPT;
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource of unrecognized type in file: " + t + ".");
if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) {
//create a missing resource
missing_resource = memnew(MissingResource);
missing_resource->set_original_class(t);
missing_resource->set_recording_properties(true);
obj = missing_resource;
} else {
error = ERR_FILE_CORRUPT;
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource of unrecognized type in file: " + t + ".");
}
}
Resource *r = Object::cast_to<Resource>(obj);
@ -760,6 +771,8 @@ Error ResourceLoaderBinary::load() {
//set properties
Dictionary missing_resource_properties;
for (int j = 0; j < pc; j++) {
StringName name = _get_string();
@ -775,8 +788,32 @@ Error ResourceLoaderBinary::load() {
return error;
}
res->set(name, value);
bool set_valid = true;
if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) {
// If the property being set is a missing resource (and the parent is not),
// then setting it will most likely not work.
// Instead, save it as metadata.
Ref<MissingResource> mr = value;
if (mr.is_valid()) {
missing_resource_properties[name] = mr;
set_valid = false;
}
}
if (set_valid) {
res->set(name, value);
}
}
if (missing_resource) {
missing_resource->set_recording_properties(false);
}
if (!missing_resource_properties.is_empty()) {
res->set_meta(META_MISSING_RESOURCES, missing_resource_properties);
}
#ifdef TOOLS_ENABLED
res->set_edited(false);
#endif
@ -1833,6 +1870,15 @@ int ResourceFormatSaverBinaryInstance::get_string_index(const String &p_string)
return strings.size() - 1;
}
static String _resource_get_class(Ref<Resource> p_resource) {
Ref<MissingResource> missing_resource = p_resource;
if (missing_resource.is_valid()) {
return missing_resource->get_original_class();
} else {
return p_resource->get_class();
}
}
Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) {
Error err;
Ref<FileAccess> f;
@ -1885,7 +1931,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
return ERR_CANT_CREATE;
}
save_unicode_string(f, p_resource->get_class());
save_unicode_string(f, _resource_get_class(p_resource));
f->store_64(0); //offset to import metadata
{
uint32_t format_flags = FORMAT_FLAG_NAMED_SCENE_IDS | FORMAT_FLAG_UIDS;
@ -1902,10 +1948,12 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
List<ResourceData> resources;
Dictionary missing_resource_properties = p_resource->get_meta(META_MISSING_RESOURCES, Dictionary());
{
for (const Ref<Resource> &E : saved_resources) {
ResourceData &rd = resources.push_back(ResourceData())->get();
rd.type = E->get_class();
rd.type = _resource_get_class(E);
List<PropertyInfo> property_list;
E->get_property_list(&property_list);
@ -1914,6 +1962,10 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
if (skip_editor && F.name.begins_with("__editor")) {
continue;
}
if (F.name == META_PROPERTY_MISSING_RESOURCES) {
continue;
}
if ((F.usage & PROPERTY_USAGE_STORAGE)) {
Property p;
p.name_idx = get_string_index(F.name);
@ -1929,6 +1981,14 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
p.value = E->get(F.name);
}
if (p.pi.type == Variant::OBJECT && missing_resource_properties.has(F.name)) {
// Was this missing resource overriden? If so do not save the old value.
Ref<Resource> res = p.value;
if (res.is_null()) {
p.value = missing_resource_properties[F.name];
}
}
Variant default_value = ClassDB::class_get_default_property_value(E->get_class(), F.name);
if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, p.value, default_value))) {
@ -1990,7 +2050,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
String new_id;
while (true) {
new_id = r->get_class() + "_" + Resource::generate_scene_unique_id();
new_id = _resource_get_class(r) + "_" + Resource::generate_scene_unique_id();
if (!used_unique_ids.has(new_id)) {
break;
}

View file

@ -979,6 +979,10 @@ void ResourceLoader::remove_custom_resource_format_loader(String script_path) {
}
}
void ResourceLoader::set_create_missing_resources_if_class_unavailable(bool p_enable) {
create_missing_resources_if_class_unavailable = p_enable;
}
void ResourceLoader::add_custom_loaders() {
// Custom loaders registration exploits global class names
@ -1030,6 +1034,7 @@ void *ResourceLoader::err_notify_ud = nullptr;
DependencyErrorNotify ResourceLoader::dep_err_notify = nullptr;
void *ResourceLoader::dep_err_notify_ud = nullptr;
bool ResourceLoader::create_missing_resources_if_class_unavailable = false;
bool ResourceLoader::abort_on_missing_resource = true;
bool ResourceLoader::timestamp_on_load = false;

View file

@ -110,6 +110,7 @@ private:
static void *dep_err_notify_ud;
static DependencyErrorNotify dep_err_notify;
static bool abort_on_missing_resource;
static bool create_missing_resources_if_class_unavailable;
static HashMap<String, Vector<String>> translation_remaps;
static HashMap<String, String> path_remaps;
@ -222,6 +223,9 @@ public:
static void add_custom_loaders();
static void remove_custom_loaders();
static void set_create_missing_resources_if_class_unavailable(bool p_enable);
_FORCE_INLINE_ static bool is_creating_missing_resources_if_class_unavailable_enabled() { return create_missing_resources_if_class_unavailable; }
static void initialize();
static void finalize();
};

View file

@ -434,15 +434,6 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid
}
}
// Something inside the object... :|
bool success = _setv(p_name, p_value);
if (success) {
if (r_valid) {
*r_valid = true;
}
return;
}
#ifdef TOOLS_ENABLED
if (script_instance) {
bool valid;
@ -456,6 +447,15 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid
}
#endif
// Something inside the object... :|
bool success = _setv(p_name, p_value);
if (success) {
if (r_valid) {
*r_valid = true;
}
return;
}
if (r_valid) {
*r_valid = false;
}
@ -518,15 +518,6 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const {
return ret;
} else {
// Something inside the object... :|
bool success = _getv(p_name, ret);
if (success) {
if (r_valid) {
*r_valid = true;
}
return ret;
}
#ifdef TOOLS_ENABLED
if (script_instance) {
bool valid;
@ -539,6 +530,14 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const {
}
}
#endif
// Something inside the object... :|
bool success = _getv(p_name, ret);
if (success) {
if (r_valid) {
*r_valid = true;
}
return ret;
}
if (r_valid) {
*r_valid = false;

View file

@ -49,6 +49,7 @@
#include "core/io/image_loader.h"
#include "core/io/json.h"
#include "core/io/marshalls.h"
#include "core/io/missing_resource.h"
#include "core/io/packed_data_container.h"
#include "core/io/packet_peer.h"
#include "core/io/packet_peer_dtls.h"
@ -151,6 +152,7 @@ void register_core_types() {
GDREGISTER_CLASS(RefCounted);
GDREGISTER_CLASS(WeakRef);
GDREGISTER_CLASS(Resource);
GDREGISTER_VIRTUAL_CLASS(MissingResource);
GDREGISTER_CLASS(Image);
GDREGISTER_CLASS(Shortcut);