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

"FIle not found" errors after deleting folder containing GDScript file with custom class_name #81867

Closed
tktiger opened this issue Sep 18, 2023 · 15 comments · Fixed by #90186
Closed

Comments

@tktiger
Copy link

tktiger commented Sep 18, 2023

Godot version

4.1.1

System information

Windows 11

Issue description

When a GDScript file with a custom class_name is deleted, the class is also removed from the editor. However, if the folder is deleted (which contained the same GDScript file) the class is seemingly not removed from the editor and when you search for nodes, it tries to open the missing file and reports errors.

Steps to reproduce

  1. Create a new folder in the resource tree (eg example).
  2. Inside this new folder, create a new GDScript file to Extend Node3D (although can be anything, I think)
  3. Inside the new GDScript file, add the following line under the Extends Node3D line:
    class_name someclass
  4. Save the GDScript file as myscript.gd.
  5. Create a new Scene, select Other Node as the Root Node and search for your new class (someclass, in this example)
  6. The option to extend your new scene from this class and GDScript file is available.
  7. Cancel the Create New Node dialog
  8. Delete the folder created in step 1
  9. Attempt step 5 again - you will get errors in the console as follows:

Attempt to open script 'res://example/myscript.gd' resulted in error 'File not found'.
Failed loading resource: res://example/myscript.gd. Make sure resources have been imported by opening the project in the editor at least once.
editor/create_dialog.cpp:241 - Condition "scr.is_null()" is true.

Closing and reopening Godot has no effect. The only way to fix this, is to create the same GDScript file again and delete the file rather that the folder it is in. Deleting the file itself in step 8 results in no errors being reported.

Minimal reproduction project

N/A

@dalexeev
Copy link
Member

dalexeev commented Sep 18, 2023

Probably related:

Closing and reopening Godot has no effect.

Clearing .godot/global_script_class_cache.cfg should help.

@tktiger
Copy link
Author

tktiger commented Sep 18, 2023

Clearing .godot/global_script_class_cache.cfg should help.

Well, yes, and no. The rogue entry in this file seems to be the cause. Deleting the folder does not delete the lines from this cache file, whilst deleting the file removed it properly. However, simply clearing this file out doesn't provide a clean answer as after I do that, other classes which previously did work, now do not - I get "could not find base class" in the editor for a second class I had created.

Therefore, it either needs a "refresh" where it traverses everything and builds up the cache again OR the delete folder function just needs to leverage the delete file function. I tried looking for it in the source code, but its way too big for my small brain - I have no idea where it is.

For reference, closing and reopening godot does not rebuild it fully, and just opening the .gd files in the editor does not rebuild it. You have to open the .gd script which contains the class_name, make a change, and save it, for the cache to be updated and all errors to go away.

@lamdevhs
Copy link

lamdevhs commented Sep 21, 2023

A simpler solution to fix the issue is to edit the cache file and remove the parts that mention the file(s) that were deleted.
Example:
}, {
"base": &"Node",
"class": &"WebSocketClient",
"icon": "",
"language": &"GDScript",
"path": "res://network/web-socket/web_socket_client.gd"
}, {
If you deleted that file, you then just have to delete the lines from "base" to "path", and also one of the "}, {" lines (not both, the structure has to remain intact). Out of paranoia, I do this while Godot is closed, even though it doesn't seem to matter (though you probably have to close and reopen Godot anyway after the edit).

This could easily be automated by a script which would remove any entry that references a file which does not exist, however right now I don't have time to write one. Searching and deleting is fast enough, although of course I'm still waiting for a global fix since this should not be required in the first place.

In particular: the bug is extremely incompatible with checking out older commits. At first, all seems to go well, but then you come back to the tip of your main branch, and suddenly Godot has lost its mind, thinks all the old classes it added while you were time-traveling are still there. This causes ridiculous issues, for example, suppose on commit X you moved a global class inside another script, making it an inner class. Then one day you checkout a commit before X: Godot updates the cache with the long-gone global class, and that update stays when you check back out commits after X. At which point, if you try to run your project, Godot tells you: 'Class "Foo" hides a global script class.' And then you're stuck, you can't do anything except update the cache file manually to erase Godot's memory again.

EDIT: I literally just tested this scenario to check I wasn't wrong, and therefore shot myself in the foot again, since I do have to edit that cache file manually, and that's very annoying. So I think tomorrow morning I'll write a short Python script to read that file, check which paths exist (in folders that don't contain .gdignore files) and which paths don't anymore (or are ignored and therefore invisible to Godot), and then overwrite the cache with the updated data. I'll post it here if anyone's interested.

@tktiger
Copy link
Author

tktiger commented Sep 21, 2023

OK, I think this could be a fix for it. Unfortunately, I haven't yet fully got a all the tools downloaded and installed and configured to compile this myself to test, but I think the logic and locations for edits could work:

In the _update_scan_actions() function of editor/file_system_editor.cpp, there is a CASE statement.
In case ItemAction::ACTION_FILE_REMOVE: there is an if statement which checks if the file being deleted is a SCRIPT file and, if it is, it adds it to a list with _queue_update_script_class and then deletes the file
In case ItemAction::ACTION_DIR_REMOVE: there is no such checks and the folder is just removed.

I'm not sure if it should be in the case statement or not, but if a loop can be added to traverse down the whole folder tree from the folder being deleted and check if it is a script file and add it to the list with _queue_update_script_class as in the other case, this should (I think) solve the problem.

Looking at the case for ItemAction::ACTION_FILE_ADD: there is a similar feature, so I do think this is where it should go.

Hopefully, that helps someone find and fix it. Unless I'm completely wrong of course, which is entirely possible.

@lamdevhs
Copy link

lamdevhs commented Sep 22, 2023

While we wait for a definitive fix to this issue, here's my little Python script to clear the cache file of dead entries:
https://gitlab.com/neatbackyard/godot-cache-cleaner

@mellonedain
Copy link

mellonedain commented Sep 26, 2023

Hi, I am not sure that it the right topic for my issue, but sounds similarly:

What I did:

  1. I had res://main_menu scene with all assets, script and scene definition
  2. I created res://ui forter to do some refactor
  3. I cannot move the res://main_menu to the res://ui
  4. I created res://ui/main_menu folder and move all from res://main_menu manually one by one (assets, scene and script)
  5. I deleted the empty res://main_menu folder
  6. I renamed res://ui/main_menu to the res://ui/menu_main

Error:

Cannot open file 'res://ui/main_menu/main_menu.tscn'. Failed loading resource: res://ui/main_menu/main_menu.tscn. Make sure resources have been imported by opening the project in the editor at least once. scene/resources/resource_format_text.cpp:283 - res://main.tscn:9 - Parse Error: Failed loading resource: res://main.tscn. Make sure resources have been imported by opening the project in the editor at least once.

Verification

  1. Close Godot
  2. Edit in external text editor the files res://main.tscn and res://ui/main_menu/main_menu.tscn
  3. All paths are properly changed
  4. I found the problem:

The main_menu UID has been changed only in one file (res://ui/menu_main/main_menu.tscn) from "uid://b7qjqs6a1budn" to "uid://c4w0clxm2ed2d".

Solution

I made change in the res://main.tscn file to proper UID:

From:

[ext_resource type="PackedScene" uid="uid://b7qjqs6a1budn" path="res://ui/main_menu/main_menu.tscn" id="2_lep4t"]

To:

[ext_resource type="PackedScene" uid="uid://c4w0clxm2ed2d" path="res://ui/main_menu/main_menu.tscn" id="2_lep4t"]

After all I close project and reload it with new definitions. Everything start working again properly.

In delete case probably You need to remove line similar to:

[ext_resource type="PackedScene" uid="uid://b7qjqs6a1budn" path="res://ui/main_menu/main_menu.tscn" id="2_lep4t"]

I hope it help You...

@donn-xx
Copy link

donn-xx commented Oct 8, 2023

Not sure if this is related/relevant...
I get a lot of the same errors, like:
ERROR: Attempt to open script 'res://addons/dabber/dab3dnode/_old/single_scene.gd' resulted in error 'File not found'.

What would be great is if the error message could give some clue where it was when the error happens. i.e. What resource or file or whatever was it parsing when the missing reference happened.

It would really speed the ability to solve the problem. Right now it's mostly a mystery.

@mellonedain
Copy link

mellonedain commented Oct 9, 2023

Did You check the source files in any other text editor? Maybe it is more general problem with UIDs?

@donn-xx
Copy link

donn-xx commented Oct 9, 2023

I have searched for refs to that file without success. The next step is a text-search across all project files.

@mellonedain
Copy link

mellonedain commented Oct 10, 2023

Try to check the main project file project.godot. It is text file too....

@lamdevhs
Copy link

Assuming you use git you can do git grep <regex> to search through all your project files painlessly. If it isn't there it probably is an old entry in the cache file. You can edit it in the .godot/ folder, or use my cache cleaner script if it works on your machine (cf my previous message).

@donn-xx
Copy link

donn-xx commented Oct 10, 2023

Try to check the main project file project.godot. It is text file too....

Excellent suggestion!

Assuming you use git you can do git grep <regex> to search through all your project files painlessly. If it isn't there it probably is an old entry in the cache file. You can edit it in the .godot/ folder, or use my cache cleaner script if it works on your machine (cf my previous message).

Ooh. I was going to break out find and grep, but if git can help that's cool.

Godot Devs: See those two ideas just quoted? Can't the IDE do that for us?

@Rindbee
Copy link
Contributor

Rindbee commented Oct 13, 2023

FileSystemDock is responsible for operating files/folders in the editor. EditorFileSystem manages the file system cache and some cache files (in .godot). And file/folder moving (renaming) is treated as remove&new in EditorFileSystem.

There are scan() and scan_changes() in EditorFileSystem. I'm not particularly clear on the design purpose of these two methods.

scan() does not handle some subsequent (clear) operations after the folder is deleted. It does not modify the cache file for the file's removal. It seems to be suitable for handling new additions of files/folders, or the first scan after opening a project.

void FileSystemDock::_rescan() {
_set_scanning_mode();
EditorFileSystem::get_singleton()->scan();
}

scan_changes() is good at handling the addition/removal/modification of files/folders after the first scan. It seems to be designed for external editing/modification and is not used in FileSystemDock.

That is, processing file operations in FileSystemDock may need to manage cache files (such as .godot/global_script_class_cache.cfg).

@mellonedain
Copy link

@Rindbee it is solution for both problems? If yes maybe the topic should be change to more general? Could you change it?

@Rindbee
Copy link
Contributor

Rindbee commented Oct 13, 2023

@Rindbee it is solution for both problems? If yes maybe the topic should be change to more general? Could you change it?

Sadly it is not, changing the scanning method may require more work. The logic in FileSystemDock seems to be customized for scan().

Another thing worth noting is that the change of dependencies between files is done by FileSystemDock. EditorFileSystem does not manage dependency changes between files when files are removed and moved/renamed. This is probably to avoid completely removing previous dependencies when moving (renaming) files externally. Currently the dependency is just broken (#82647).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants