Skip to content
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

Fix accessing UID before first scan #102513

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions core/io/resource_uid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,19 @@ String ResourceUID::get_id_path(ID p_id) const {
ERR_FAIL_COND_V_MSG(p_id == INVALID_ID, String(), "Invalid UID.");
MutexLock l(mutex);
const ResourceUID::Cache *cache = unique_ids.getptr(p_id);

#if TOOLS_ENABLED
// On startup, the scan_for_uid_on_startup callback should be set and will
// execute EditorFileSystem::scan_for_uid, which scans all project files
// to reload the UID cache before the first scan.
// Note: EditorFileSystem::scan_for_uid sets scan_for_uid_on_startup to nullptr
// once the first scan_for_uid is complete.
if (!cache && scan_for_uid_on_startup) {
scan_for_uid_on_startup();
cache = unique_ids.getptr(p_id);
}
#endif

ERR_FAIL_COND_V_MSG(!cache, String(), vformat("Unrecognized UID: \"%s\".", id_to_text(p_id)));
const CharString &cs = cache->cs;
return String::utf8(cs.ptr());
Expand Down
4 changes: 4 additions & 0 deletions core/io/resource_uid.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@

class FileAccess;

typedef void (*ResourceUIDScanForUIDOnStartup)();

class ResourceUID : public Object {
GDCLASS(ResourceUID, Object)
public:
Expand All @@ -63,6 +65,8 @@ class ResourceUID : public Object {
static void _bind_methods();

public:
inline static ResourceUIDScanForUIDOnStartup scan_for_uid_on_startup = nullptr;

String id_to_text(ID p_id) const;
ID text_to_id(const String &p_text) const;

Expand Down
68 changes: 64 additions & 4 deletions editor/editor_file_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
#include "scene/resources/packed_scene.h"

EditorFileSystem *EditorFileSystem::singleton = nullptr;
int EditorFileSystem::nb_files_total = 0;
EditorFileSystem::ScannedDirectory *EditorFileSystem::first_scan_root_dir = nullptr;

//the name is the version, to keep compatibility with different versions of Godot
#define CACHE_FILE_NAME "filesystem_cache10"

Expand Down Expand Up @@ -237,16 +240,72 @@ EditorFileSystem::ScannedDirectory::~ScannedDirectory() {
}
}

void EditorFileSystem::_first_scan_filesystem() {
EditorProgress ep = EditorProgress("first_scan_filesystem", TTR("Project initialization"), 5);
void EditorFileSystem::_load_first_scan_root_dir() {
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
first_scan_root_dir = memnew(ScannedDirectory);
first_scan_root_dir->full_path = "res://";

nb_files_total = _scan_new_dir(first_scan_root_dir, d);
}

void EditorFileSystem::scan_for_uid() {
// Load file structure into memory.
_load_first_scan_root_dir();

// Load extensions for which an .import should exists.
List<String> extensionsl;
HashSet<String> import_extensions;
ResourceFormatImporter::get_singleton()->get_recognized_extensions(&extensionsl);
for (const String &E : extensionsl) {
import_extensions.insert(E);
}

// Scan the file system to load uid.
_scan_for_uid_directory(first_scan_root_dir, import_extensions);

// It's done, resetting the callback method to prevent a second scan.
ResourceUID::scan_for_uid_on_startup = nullptr;
}

void EditorFileSystem::_scan_for_uid_directory(const ScannedDirectory *p_scan_dir, const HashSet<String> &p_import_extensions) {
for (ScannedDirectory *scan_sub_dir : p_scan_dir->subdirs) {
_scan_for_uid_directory(scan_sub_dir, p_import_extensions);
}

for (const String &scan_file : p_scan_dir->files) {
const String ext = scan_file.get_extension().to_lower();

if (ext == "uid" || ext == "import") {
continue;
}

const String path = p_scan_dir->full_path.path_join(scan_file);
ResourceUID::ID uid = ResourceUID::INVALID_ID;
if (p_import_extensions.has(ext)) {
if (FileAccess::exists(path + ".import")) {
uid = ResourceFormatImporter::get_singleton()->get_resource_uid(path);
}
} else {
uid = ResourceLoader::get_resource_uid(path);
}

if (uid != ResourceUID::INVALID_ID) {
if (!ResourceUID::get_singleton()->has_id(uid)) {
ResourceUID::get_singleton()->add_id(uid, path);
}
}
}
}

void EditorFileSystem::_first_scan_filesystem() {
EditorProgress ep = EditorProgress("first_scan_filesystem", TTR("Project initialization"), 5);
HashSet<String> existing_class_names;
HashSet<String> extensions;

ep.step(TTR("Scanning file structure..."), 0, true);
nb_files_total = _scan_new_dir(first_scan_root_dir, d);
if (!first_scan_root_dir) {
ep.step(TTR("Scanning file structure..."), 0, true);
_load_first_scan_root_dir();
}

// Preloading GDExtensions file extensions to prevent looping on all the resource loaders
// for each files in _first_scan_process_scripts.
Expand Down Expand Up @@ -440,6 +499,7 @@ void EditorFileSystem::_scan_filesystem() {
sd = first_scan_root_dir;
// Will be updated on scan.
ResourceUID::get_singleton()->clear();
ResourceUID::scan_for_uid_on_startup = nullptr;
processed_files = memnew(HashSet<String>());
} else {
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
Expand Down
12 changes: 9 additions & 3 deletions editor/editor_file_system.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class EditorFileSystem : public Node {
static void _thread_func(void *_userdata);

EditorFileSystemDirectory *new_filesystem = nullptr;
ScannedDirectory *first_scan_root_dir = nullptr;
static ScannedDirectory *first_scan_root_dir;

bool filesystem_changed_queued = false;
bool scanning = false;
Expand All @@ -192,13 +192,17 @@ class EditorFileSystem : public Node {
float scan_total;
String filesystem_settings_version_for_import;
bool revalidate_import_files = false;
int nb_files_total = 0;
static int nb_files_total;

void _notify_filesystem_changed();
void _scan_filesystem();
void _first_scan_filesystem();
void _first_scan_process_scripts(const ScannedDirectory *p_scan_dir, List<String> &p_gdextension_extensions, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions);

static void _scan_for_uid_directory(const ScannedDirectory *p_scan_dir, const HashSet<String> &p_import_extensions);

static void _load_first_scan_root_dir();

HashSet<String> late_update_files;

void _save_late_updated_files();
Expand Down Expand Up @@ -255,7 +259,7 @@ class EditorFileSystem : public Node {
HashSet<String> valid_extensions;
HashSet<String> import_extensions;

int _scan_new_dir(ScannedDirectory *p_dir, Ref<DirAccess> &da);
static int _scan_new_dir(ScannedDirectory *p_dir, Ref<DirAccess> &da);
void _process_file_system(const ScannedDirectory *p_scan_dir, EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, HashSet<String> *p_processed_files);

Thread thread_sources;
Expand Down Expand Up @@ -412,6 +416,8 @@ class EditorFileSystem : public Node {

static bool _should_skip_directory(const String &p_path);

static void scan_for_uid();

void add_import_format_support_query(Ref<EditorFileSystemImportFormatSupportQuery> p_query);
void remove_import_format_support_query(Ref<EditorFileSystemImportFormatSupportQuery> p_query);
EditorFileSystem();
Expand Down
27 changes: 25 additions & 2 deletions main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3444,6 +3444,17 @@ Error Main::setup2(bool p_show_boot_logo) {
// This loads global classes, so it must happen before custom loaders and savers are registered
ScriptServer::init_languages();

#if TOOLS_ENABLED

// Setting up the callback to execute a scan for UIDs on disk when a UID
// does not exist in the UID cache on startup. This prevents invalid UID errors
// when opening a project without a UID cache file or with an invalid cache.
if (editor) {
ResourceUID::scan_for_uid_on_startup = EditorFileSystem::scan_for_uid;
}

#endif

theme_db->initialize_theme();
audio_server->load_default_bus_layout();

Expand Down Expand Up @@ -3496,9 +3507,21 @@ void Main::setup_boot_logo() {

if (show_logo) { //boot logo!
const bool boot_logo_image = GLOBAL_DEF_BASIC("application/boot_splash/show_image", true);
const String boot_logo_path = ResourceUID::ensure_path(GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/boot_splash/image", PROPERTY_HINT_FILE, "*.png"), String())).strip_edges();
const bool boot_logo_scale = GLOBAL_DEF_BASIC("application/boot_splash/fullsize", true);
const bool boot_logo_filter = GLOBAL_DEF_BASIC("application/boot_splash/use_filter", true);
String boot_logo_path = GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/boot_splash/image", PROPERTY_HINT_FILE, "*.png"), String());

// If the UID cache is missing or invalid, it could be 'normal' for the UID to not exist in memory.
// It's too soon to scan the project files since the ResourceFormatImporter is not loaded yet,
// so to prevent printing errors, we will just skip the custom boot logo this time.
if (boot_logo_path.begins_with("uid://")) {
const ResourceUID::ID logo_id = ResourceUID::get_singleton()->text_to_id(boot_logo_path);
if (ResourceUID::get_singleton()->has_id(logo_id)) {
boot_logo_path = ResourceUID::get_singleton()->get_id_path(logo_id).strip_edges();
} else {
boot_logo_path = String();
}
}

Ref<Image> boot_logo;

Expand Down Expand Up @@ -4215,7 +4238,7 @@ int Main::start() {

#ifdef TOOLS_ENABLED
if (editor) {
if (!recovery_mode && (game_path != String(GLOBAL_GET("application/run/main_scene")) || !editor_node->has_scenes_in_session())) {
if (!recovery_mode && (game_path != ResourceUID::ensure_path(String(GLOBAL_GET("application/run/main_scene"))) || !editor_node->has_scenes_in_session())) {
Error serr = editor_node->load_scene(local_game_path);
if (serr != OK) {
ERR_PRINT("Failed to load scene");
Expand Down