Skip to content

Commit

Permalink
Support script global resource name in EditorFileSystem
Browse files Browse the repository at this point in the history
* Works for binary and text files.
* Makes EditorQuickOpen work with custom resources again.
* Information is cached and easily accessible.

Properly fixes godotengine#66179. Supersedes godotengine#66215 and supersedes godotengine#62417

**WARNING**: This required breaking backwards binary compatibility (.res and .scn files). Files saved after this PR is merged will no longer open in any earlier versions of Godot.
  • Loading branch information
reduz committed Jan 21, 2023
1 parent 14fdd28 commit dddd8d4
Show file tree
Hide file tree
Showing 17 changed files with 250 additions and 18 deletions.
93 changes: 92 additions & 1 deletion core/io/resource_format_binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ enum {
// Version 2: added 64 bits support for float and int.
// Version 3: changed nodepath encoding.
// Version 4: new string ID for ext/subresources, breaks forward compat.
FORMAT_VERSION = 4,
// Version 5: Ability to store script class in the header.
FORMAT_VERSION = 5,
FORMAT_VERSION_CAN_RENAME_DEPS = 1,
FORMAT_VERSION_NO_NODEPATH_PROPERTY = 3,
};
Expand Down Expand Up @@ -1013,6 +1014,10 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p
uid = ResourceUID::INVALID_ID;
}

if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_HAS_SCRIPT_CLASS) {
script_class = get_unicode_string();
}

for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
f->get_32(); //skip a few reserved fields
}
Expand Down Expand Up @@ -1117,6 +1122,57 @@ String ResourceLoaderBinary::recognize(Ref<FileAccess> p_f) {
return get_unicode_string();
}

String ResourceLoaderBinary::recognize_script_class(Ref<FileAccess> p_f) {
error = OK;

f = p_f;
uint8_t header[4];
f->get_buffer(header, 4);
if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') {
// Compressed.
Ref<FileAccessCompressed> fac;
fac.instantiate();
error = fac->open_after_magic(f);
if (error != OK) {
f.unref();
return "";
}
f = fac;

} else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') {
// Not normal.
error = ERR_FILE_UNRECOGNIZED;
f.unref();
return "";
}

bool big_endian = f->get_32();
f->get_32(); // use_real64

f->set_big_endian(big_endian != 0); //read big endian if saved as big endian

uint32_t ver_major = f->get_32();
f->get_32(); // ver_minor
uint32_t ver_fmt = f->get_32();

if (ver_fmt > FORMAT_VERSION || ver_major > VERSION_MAJOR) {
f.unref();
return "";
}

get_unicode_string(); // type

f->get_64(); // Metadata offset
uint32_t flags = f->get_32();
f->get_64(); // UID

if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_HAS_SCRIPT_CLASS) {
return get_unicode_string();
} else {
return String();
}
}

Ref<Resource> ResourceFormatLoaderBinary::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
Expand Down Expand Up @@ -1299,6 +1355,9 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons

fw->store_32(flags);
fw->store_64(uid_data);
if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_HAS_SCRIPT_CLASS) {
save_ustring(fw, get_ustring(f));
}

for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
fw->store_32(0); // reserved
Expand Down Expand Up @@ -1420,6 +1479,18 @@ String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const
return ClassDB::get_compatibility_remapped_class(r);
}

String ResourceFormatLoaderBinary::get_resource_script_class(const String &p_path) const {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
if (f.is_null()) {
return ""; //could not read
}

ResourceLoaderBinary loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
return loader.recognize_script_class(f);
}

ResourceUID::ID ResourceFormatLoaderBinary::get_resource_uid(const String &p_path) const {
String ext = p_path.get_extension().to_lower();
if (!ClassDB::is_resource_extension(ext)) {
Expand Down Expand Up @@ -2037,15 +2108,31 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re

save_unicode_string(f, _resource_get_class(p_resource));
f->store_64(0); //offset to import metadata

String script_class;
{
uint32_t format_flags = FORMAT_FLAG_NAMED_SCENE_IDS | FORMAT_FLAG_UIDS;
#ifdef REAL_T_IS_DOUBLE
format_flags |= FORMAT_FLAG_REAL_T_IS_DOUBLE;
#endif
if (!p_resource->is_class("PackedScene")) {
Ref<Script> s = p_resource->get_script();
if (s.is_valid()) {
script_class = s->get_global_name();
if (!script_class.is_empty()) {
format_flags |= ResourceFormatSaverBinaryInstance::FORMAT_FLAG_HAS_SCRIPT_CLASS;
}
}
}

f->store_32(format_flags);
}
ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(p_path, true);
f->store_64(uid);
if (!script_class.is_empty()) {
save_unicode_string(f, script_class);
}

for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
f->store_32(0); // reserved
}
Expand Down Expand Up @@ -2298,6 +2385,10 @@ Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceU
fw->store_32(flags);
fw->store_64(p_uid);

if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_HAS_SCRIPT_CLASS) {
save_ustring(fw, get_ustring(f));
}

//rest of file
uint8_t b = f->get_8();
while (!f->eof_reached()) {
Expand Down
4 changes: 4 additions & 0 deletions core/io/resource_format_binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class ResourceLoaderBinary {

bool using_named_scene_ids = false;
bool using_uids = false;
String script_class;
bool use_sub_threads = false;
float *progress = nullptr;
Vector<ExtResource> external_resources;
Expand Down Expand Up @@ -100,6 +101,7 @@ class ResourceLoaderBinary {
void set_remaps(const HashMap<String, String> &p_remaps) { remaps = p_remaps; }
void open(Ref<FileAccess> p_f, bool p_no_resources = false, bool p_keep_uuid_paths = false);
String recognize(Ref<FileAccess> p_f);
String recognize_script_class(Ref<FileAccess> p_f);
void get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types);
void get_classes_used(Ref<FileAccess> p_f, HashSet<StringName> *p_classes);

Expand All @@ -113,6 +115,7 @@ class ResourceFormatLoaderBinary : public ResourceFormatLoader {
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
virtual String get_resource_script_class(const String &p_path) const;
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
Expand Down Expand Up @@ -165,6 +168,7 @@ class ResourceFormatSaverBinaryInstance {
FORMAT_FLAG_NAMED_SCENE_IDS = 1,
FORMAT_FLAG_UIDS = 2,
FORMAT_FLAG_REAL_T_IS_DOUBLE = 4,
FORMAT_FLAG_HAS_SCRIPT_CLASS = 8,

// Amount of reserved 32-bit fields in resource header
RESERVED_FIELDS = 11
Expand Down
20 changes: 20 additions & 0 deletions core/io/resource_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ String ResourceFormatLoader::get_resource_type(const String &p_path) const {
return ret;
}

String ResourceFormatLoader::get_resource_script_class(const String &p_path) const {
String ret;
GDVIRTUAL_CALL(_get_resource_script_class, p_path, ret);
return ret;
}

ResourceUID::ID ResourceFormatLoader::get_resource_uid(const String &p_path) const {
int64_t uid = ResourceUID::INVALID_ID;
GDVIRTUAL_CALL(_get_resource_uid, p_path, uid);
Expand Down Expand Up @@ -184,6 +190,7 @@ void ResourceFormatLoader::_bind_methods() {
GDVIRTUAL_BIND(_recognize_path, "path", "type");
GDVIRTUAL_BIND(_handles_type, "type");
GDVIRTUAL_BIND(_get_resource_type, "path");
GDVIRTUAL_BIND(_get_resource_script_class, "path");
GDVIRTUAL_BIND(_get_resource_uid, "path");
GDVIRTUAL_BIND(_get_dependencies, "path", "add_types");
GDVIRTUAL_BIND(_rename_dependencies, "path", "renames");
Expand Down Expand Up @@ -764,6 +771,19 @@ String ResourceLoader::get_resource_type(const String &p_path) {
return "";
}

String ResourceLoader::get_resource_script_class(const String &p_path) {
String local_path = _validate_local_path(p_path);

for (int i = 0; i < loader_count; i++) {
String result = loader[i]->get_resource_script_class(local_path);
if (!result.is_empty()) {
return result;
}
}

return "";
}

ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) {
String local_path = _validate_local_path(p_path);

Expand Down
3 changes: 3 additions & 0 deletions core/io/resource_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class ResourceFormatLoader : public RefCounted {
GDVIRTUAL2RC(bool, _recognize_path, String, StringName)
GDVIRTUAL1RC(bool, _handles_type, StringName)
GDVIRTUAL1RC(String, _get_resource_type, String)
GDVIRTUAL1RC(String, _get_resource_script_class, String)
GDVIRTUAL1RC(ResourceUID::ID, _get_resource_uid, String)
GDVIRTUAL2RC(Vector<String>, _get_dependencies, String, bool)
GDVIRTUAL1RC(Vector<String>, _get_classes_used, String)
Expand All @@ -71,6 +72,7 @@ class ResourceFormatLoader : public RefCounted {
virtual bool handles_type(const String &p_type) const;
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
virtual String get_resource_type(const String &p_path) const;
virtual String get_resource_script_class(const String &p_path) const;
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map);
Expand Down Expand Up @@ -175,6 +177,7 @@ class ResourceLoader {
static void remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader);
static void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
static String get_resource_type(const String &p_path);
static String get_resource_script_class(const String &p_path);
static ResourceUID::ID get_resource_uid(const String &p_path);
static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
static Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map);
Expand Down
2 changes: 1 addition & 1 deletion core/object/script_language.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class Script : public Resource {
virtual bool can_instantiate() const = 0;

virtual Ref<Script> get_base_script() const = 0; //for script inheritance

virtual StringName get_global_name() const = 0;
virtual bool inherits_script(const Ref<Script> &p_script) const = 0;

virtual StringName get_instance_base_type() const = 0; // this may not work in all scripts, will return empty if so
Expand Down
1 change: 1 addition & 0 deletions core/object/script_language_extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class ScriptExtension : public Script {
public:
EXBIND0RC(bool, can_instantiate)
EXBIND0RC(Ref<Script>, get_base_script)
EXBIND0RC(StringName, get_global_name)
EXBIND1RC(bool, inherits_script, const Ref<Script> &)
EXBIND0RC(StringName, get_instance_base_type)

Expand Down
7 changes: 7 additions & 0 deletions doc/classes/ResourceFormatLoader.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@
Gets the list of extensions for files this loader is able to read.
</description>
</method>
<method name="_get_resource_script_class" qualifiers="virtual const">
<return type="String" />
<param index="0" name="path" type="String" />
<description>
Returns the script class name associated with the [Resource] under the given [param path]. If the resource has no script or the script isn't a named class, it should return [code]""[/code].
</description>
</method>
<method name="_get_resource_type" qualifiers="virtual const">
<return type="String" />
<param index="0" name="path" type="String" />
Expand Down
26 changes: 24 additions & 2 deletions editor/editor_file_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

EditorFileSystem *EditorFileSystem::singleton = nullptr;
//the name is the version, to keep compatibility with different versions of Godot
#define CACHE_FILE_NAME "filesystem_cache7"
#define CACHE_FILE_NAME "filesystem_cache8"

void EditorFileSystemDirectory::sort_files() {
files.sort_custom<FileInfoSort>();
Expand Down Expand Up @@ -169,6 +169,11 @@ StringName EditorFileSystemDirectory::get_file_type(int p_idx) const {
return files[p_idx]->type;
}

StringName EditorFileSystemDirectory::get_file_resource_script_class(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, files.size(), "");
return files[p_idx]->resource_script_class;
}

String EditorFileSystemDirectory::get_name() {
return name;
}
Expand Down Expand Up @@ -266,6 +271,10 @@ void EditorFileSystem::_scan_filesystem() {

FileCache fc;
fc.type = split[1];
if (fc.type.find("/") != -1) {
fc.type = fc.type.get_slice("/", 0);
fc.resource_script_class = fc.type.get_slice("/", 1);
}
fc.uid = split[2].to_int();
fc.modification_time = split[3].to_int();
fc.import_modification_time = split[4].to_int();
Expand Down Expand Up @@ -854,6 +863,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAc

if (fc && fc->modification_time == mt && fc->import_modification_time == import_mt && !_test_for_reimport(path, true)) {
fi->type = fc->type;
fi->resource_script_class = fc->resource_script_class;
fi->uid = fc->uid;
fi->deps = fc->deps;
fi->modified_time = fc->modification_time;
Expand All @@ -875,6 +885,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAc

if (fc->type.is_empty()) {
fi->type = ResourceLoader::get_resource_type(path);
fi->resource_script_class = ResourceLoader::get_resource_script_class(path);
fi->import_group_file = ResourceLoader::get_import_group_file(path);
//there is also the chance that file type changed due to reimport, must probably check this somehow here (or kind of note it for next time in another file?)
//note: I think this should not happen any longer..
Expand Down Expand Up @@ -904,6 +915,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAc
if (fc && fc->modification_time == mt) {
//not imported, so just update type if changed
fi->type = fc->type;
fi->resource_script_class = fc->resource_script_class;
fi->uid = fc->uid;
fi->modified_time = fc->modification_time;
fi->deps = fc->deps;
Expand All @@ -915,6 +927,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAc
} else {
//new or modified time
fi->type = ResourceLoader::get_resource_type(path);
fi->resource_script_class = ResourceLoader::get_resource_script_class(path);
if (fi->type == "" && textfile_extensions.has(ext)) {
fi->type = "TextFile";
}
Expand Down Expand Up @@ -1029,6 +1042,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const
fi->modified_time = FileAccess::get_modified_time(path);
fi->import_modified_time = 0;
fi->type = ResourceLoader::get_resource_type(path);
fi->resource_script_class = ResourceLoader::get_resource_script_class(path);
if (fi->type == "" && textfile_extensions.has(ext)) {
fi->type = "TextFile";
}
Expand Down Expand Up @@ -1285,7 +1299,12 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir,
if (!p_dir->files[i]->import_group_file.is_empty()) {
group_file_cache.insert(p_dir->files[i]->import_group_file);
}
String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->uid) + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->import_group_file + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends + "<>" + p_dir->files[i]->script_class_icon_path;

String type = p_dir->files[i]->type;
if (p_dir->files[i]->resource_script_class) {
type += "/" + String(p_dir->files[i]->resource_script_class);
}
String s = p_dir->files[i]->file + "::" + type + "::" + itos(p_dir->files[i]->uid) + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->import_group_file + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends + "<>" + p_dir->files[i]->script_class_icon_path;
s += "::";
for (int j = 0; j < p_dir->files[i]->deps.size(); j++) {
if (j > 0) {
Expand Down Expand Up @@ -1612,6 +1631,8 @@ void EditorFileSystem::update_file(const String &p_file) {
if (type.is_empty() && textfile_extensions.has(p_file.get_extension())) {
type = "TextFile";
}
String script_class = ResourceLoader::get_resource_script_class(p_file);

ResourceUID::ID uid = ResourceLoader::get_resource_uid(p_file);

if (cpos == -1) {
Expand Down Expand Up @@ -1645,6 +1666,7 @@ void EditorFileSystem::update_file(const String &p_file) {
}

fs->files[cpos]->type = type;
fs->files[cpos]->resource_script_class = script_class;
fs->files[cpos]->uid = uid;
fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path);
fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(p_file);
Expand Down
Loading

0 comments on commit dddd8d4

Please sign in to comment.