Skip to content

Commit

Permalink
Fix accessing UID before first scan
Browse files Browse the repository at this point in the history
  • Loading branch information
Hilderin committed Feb 7, 2025
1 parent 06acfcc commit 281cbdd
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 8 deletions.
15 changes: 15 additions & 0 deletions core/io/resource_uid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ static constexpr uint8_t uuid_characters[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g'
static constexpr uint32_t uuid_characters_element_count = (sizeof(uuid_characters) / sizeof(*uuid_characters));
static constexpr uint8_t max_uuid_number_length = 13; // Max 0x7FFFFFFFFFFFFFFF (uid://d4n4ub6itg400) size is 13 characters.

ResourceUIDScanForUIDOnStartup ResourceUID::scan_for_uid_on_startup = nullptr;

String ResourceUID::id_to_text(ID p_id) const {
if (p_id < 0) {
return "uid://<invalid>";
Expand Down Expand Up @@ -154,6 +156,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:
static ResourceUIDScanForUIDOnStartup scan_for_uid_on_startup;

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
13 changes: 12 additions & 1 deletion 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 @@ -4215,7 +4226,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

0 comments on commit 281cbdd

Please sign in to comment.