Make the EditorFileSystem cache more reliable#113035
Open
Rindbee wants to merge 9 commits intogodotengine:masterfrom
Open
Make the EditorFileSystem cache more reliable#113035Rindbee wants to merge 9 commits intogodotengine:masterfrom
Rindbee wants to merge 9 commits intogodotengine:masterfrom
Conversation
29fe287 to
29af0e3
Compare
0dad9d9 to
d89a0be
Compare
5525d9e to
c37acd6
Compare
07bc25d to
deecdc8
Compare
5b22e9d to
a9b8e9f
Compare
9452287 to
2d2ec65
Compare
b8a0720 to
8fb128b
Compare
6c26a83 to
ec6d4f9
Compare
Unify `global_script_class_cache.cfg` saving logic This is the first step to making `global_script_class_cache.cfg` reliable. Use uid instead of path in the global script class cache
- extension_loaded - extension_unloading These signals are best disconnected in `GDScriptLanguage::finish()` to prevent spam in unit tests.
Add `ResourceFormatSaver::set_script_class()` to allow updating the `script_class` field. Supported file types: - tres (`ResourceFormatSaverText`) - res (`ResourceFormatSaverBinary`)
…perty. Ensure that the `script` is marked as `1` in `_find_resources()`. This ensures that the first script resource in `deps` is `script` when only parsing is performed and not loaded. TODO: Properly clean up `ResourceCache::resource_path_cache` to prevent misjudgments when changing scripts.
- `EditorFileInfo` for `EditorFileSystemDirectory::FileInfo` - `ScriptClassInfo` for `EditorFileSystemDirectory::FileInfo::ScriptClassInfo`
It is not allowed to modify the `text` of a JSON file with the `json` extension by modifying the `data`. When saving a JSON object to a file, if the `text` field is empty, it is allowed to stringify the `data` field into `text` and update the `text` field accordingly.
…nd translations" This reverts commit b91bacb.
204c16d to
e03c5c5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes godotengine/godot-proposals#13196
Known limitations
Due to the limited precision of file modification timestamps obtained by Godot, file movements are primarily tracked using UIDs. However, not all files are assigned a UID.
Details
The file timestamps are not precise enough
File change detection relies on the file's modification timestamp. However, the timestamp precision of the current file's modification time is 1 second.
And when processing files in batches, the modification timestamps of many files may all fall within the same second. This is especially noticeable when updating files using version control software such as Git.
This is unlikely to be a problem in most cases. However, some unexpected issues may arise in cases of file overwriting. For example:
res://a/x/andres://b/x/.gitand committed them.res://a/andres://b/.git restore .to restore these directories might be a good idea.res://a/outside the editor and rename the originalres://b/tores://a/.res://a/x/is likely not updated correctly.Solutions
git(identify viares://.git/).Some types of file move actions cannot be tracked because no UID was assigned
The order in which file extensions are processed is as follows:
importeris eitherskiporkeep);json,po,mo,crt,pub,key, Core: Do not generate*.uidfiles for JSON, certificates, and translations #99540);txt,md,cfg,ini,log,json,yml,yaml,toml,xml, defined indocks/filesystem/textfile_extensions);ico,icns, defined indocks/filesystem/other_file_extensions);Point 2 and Point 3 are somewhat repetitive. If we process 3 and 4 first, point 2 might be able to be merged into point 3.
TODO
However, from the perspective of using UID to track file movements, it might be better to assign UIDs to files with the above extensions.
The directory move actions could not be tracked because no UID was assigned
This may affect the updating of directory colors, favorites.
TODO
Perhaps the UID assigned to the directory could be recorded in a
.uidfile.The
@iconpath for the scriptUsing the
uid://path allows for effective tracking; otherwise, it might be difficult to analyze the movement of@iconfiles. Furthermore, there is no method to update the@iconpath in the script files.Main processing flow
Main flowchart
Flowchart
flowchart TD S0(["Launch editor (first scan)"]) --> P00["Update global script classes"] P00 --> PA00["Scan"] PA00 --> C00{Already build tree?} C00 --> |No| PB03[[Build filesystem tree]] C00 --> |Yes| PB02[[Update filesystem tree]] PB02 --> PE00["Scan dirs"] PB03 --> PM01[Update uid actions] S1(["Application focus in"]) --> PB01["Scan full changes"] S2([Extensions changed]) --> PB01 PB01 --> C01{Is scanning?} C01 --> |No|PB12["Mark fs dirty"] C01 --> |Yes|PB11["Full scan pending"] PB11 --> |Delayed to the end of the scan|PB01 PB12 --> PD00["Scan dirty dirs"] S3([Dir operations in editor]) --> PB13["Mark dir dirty"] PB13 --> |Delayed to the end of the frame| PD00 PD00 -->PE00 PE00 -->PM01 S4([File operations in editor]) --> PC00["Update file(s)"] PC00 --> |Delayed to the end of the frame|PM01 PM01 --> C02{"Is first scan?"} C02 --> |No| PM02[Update global script classes] PM02 --> PM03[[Update scan actions]] C02 --> |Yes| PM03 PM03 --> Z([End])Trigger a scan/update
Several cases that trigger a scan/update:
Details
In a sense, Case 1 is a special case of Case 2. One difference is that the file information is stored in the file in case 1, while it is stored in memory in case 2. Another difference is that in case 1, the editor is being launched, so it may read the file information first, rather than reading the file information in the cache file to ensure that certain modules are available.
In case 3, the fact that the directory timestamp has not changed should not be an obstacle to detecting new files. The reasons for the changes in the list of supported file extensions are likely as follows:
docks/filesystem/textfile_extensionsand/ordocks/filesystem/other_file_extensions.ResourceFormatLoader1;Cases 4 and 5 are usually caused by user interaction in the editor. Some file caches (such as uids and global script classes) are project-wide, but some processing methods are implemented on a file-by-file basis (depending on
update_file()). Additionally, the current file timestamp precision is 1 second, making frequent calls to some methods unsuitable (#110048). These are the reasons for delaying processing until the end of the frame. It should be noted that if these file/dir operations affect the scripts mentioned in case 3, a new full scan may be triggered when the processing is completed.Case 6 is probably the most unpredictable. Here are just some suggestions:
Update file info
During the scanning process, most of the information in
FileInfois updated immediately. This information is generally unrelated to file movement. However, information that might be affected by file movement is updated with a delay.Details
The
FileInfo.statusrecords the file status at the corresponding path:The
FileInfo.class_name.icon_pathis used not only for scripts but also for other types of resources.FileInfo.resource_script_classis used for any resource that has script with a global class name. These two might become invalid when the script changes. However, the script will definitely be inFileInfo.deps. ModifyObject::get_property_list()to ensure that the firstdepfound in_find_resources()ofResourceFormatSaverBinaryInstance/ResourceFormatSaverTextInstanceis thescript.Update UID and track paths
File changes can be tracked by monitoring changes in the UID. Since we obtain a snapshot of the file system during the scan, we need to analyze the changes in UIDs in a specific order to infer the changes to the files.
UID(f)is cached inResourceUID;UID(f);UID(f)already exists inResourceUID, try addingUID(m); if it still exists, try creating and adding a new UID.Details
The above order is derived by working backward from the file changes. We may store the UID in both the
FileInfoand the file itself. Read the new UID from the file to update the UID in memory.Compound operations can be viewed as deleting first, and the UID is unique. Adding a UID before removing the existing one ensures the uniqueness of the newly added UID.
flowchart LR A001(["Check uid change"]) --> C001{"Can have uid now?"} C001 --> |Yes|A003["Check file status"] C001 --> |No|A002["Remove UID(m) if valid"] A002 --> E003([3]) A003 --> C002{"File removed?"} C002 --> |Yes|A002 C002 --> |No|C003{"File added?"} C003 --> |No|C005{"UID(m) valid?"} C005 --> |Yes|C006{"UID(m) == UID(f)?"} C006 --> |No|A008["Remove UID(m)"] C006 --> |Yes|A007["Try to add UID(f) during first scan"] A007 --> E001([1]) A008 --> E003 A008 --> C007{"UID(f) valid?"} C005 --> |No|C007 C007 --> |Yes|A009["Try to add UID(f), UID(m)"] A009 --> E004([4]) C007 --> |No|A010["Try to add UID(m)"] A010 --> E004 C003 --> |Yes|C004{"UID(f) valid?"} C004 --> |No|A006["Create a new UID"] A006 --> E002([2]) C004 --> |Yes|A005["Try to add UID(f)"] A005 --> E004Analyze file changes based on path changes in UID
During each scan, we record the basic File operations in
FileInfo.statusand use UID to track path changes.Based on the above UID tracking records, the file operations are inferred according to rules.
Details
File operation types include (
Ufor UID,Pfor path):PaUa;PrUr;Pu;Ua;Ur;P0U0r + P1U0a;P0U0r + P1U1r + P1U0a + ...;P0U0r + P0U0a + P1U0a + ....Update the caches after confirming the file has been moved
ResourceCache;Jobs
Multi-dot extensions
Improve support for multi-dot extensions.
Tasks
String::validate_extension()method to validate whether the path's extension is in the provided list of extensions.File status
Use
statusto record information such as the file category, type, and status.Tasks
EditorFileSystem::_category_validate().EditorFileSystem::_category_validate().type, tagged inEditorFileSystem::_type_analysis().EditorFileSystem::_file_info_remove().EditorFileSystem::_file_info_add().EditorFileSystem::_file_info_update().EditorFileSystem::_type_analysis().EditorFileSystem::_update_scan_actions().EditorFileSystem::_update_scan_actions().resources_reloadsignal, handled inEditorNode::_resources_changed().File movement
Track changes to UIDs to analyze complex operations such as file movement.
Tasks
EditorFileSystem::_create_actions_from_uid_change().ResourceUIDcache in order, handled inEditorFileSystem::_update_scan_uid_actions().EditorFileSystem::_update_scan_uid_actions()..tmp~temporary suffix to avoid path swapping cases.depsin the owner's file info..uidfile to track?FileSystemDock::_update_folder_colors_after_move().ResourceCache::resource_path_cache.Script class info
Update class information in the script.
Tasks
EditorFileSystem::_script_class_info_update().class_info.icon_path3.class_info.icon_path3.EditorFileSystem::_global_script_class_info_remove().EditorFileSystem::_global_script_class_info_add().EditorFileSystem::_script_class_info_update().EditorFileSystem::_update_global_script_class_activation().ScriptServer.EditorData.EditorFileSystem::_global_script_class_info_remove().ScriptServer.EditorData.ResourceFormatLoader.ResourceFormatSaver.Scenes
Update global scene groups and built-in scripts.
Tasks
Importable files
Tasks
EditorFileSystem::_import_validate().Known issues
An error occurs when updating the tscn file that uses ViewportTexture
ERROR: Path to node is invalid: 'SubViewportContainer/SubViewport'. at: _setup_local_to_scene (scene/main/viewport.cpp:200)EditorFileSystem::_update_script_documentation()will load and use thePackedScene. Resources that are not configured with a local scene in the editor will return the root of the currently edited scene whenResource::get_local_scene()is called.Footnotes
These scripts must be global script classes (
class_name) and must be tool (@tool) scripts. ↩Custom importer scripts must inherit from
EditorImportPluginand be tool (@tool) scripts, and useadd_import_plugin()/remove_import_plugin()to add/remove them. ↩The icon displayed as the file icon conflicts with the icon specified in the script's icon field. ↩ ↩2