godotengine / godotengine/godot
ResourceSaver.save() writes null for sub-resources that refer to each other
- Dominant language
- C++
- Stars
- 117k
- Forks
- 26.8k
- PR merge metrics
- PR metrics pending
Description
### Tested versions
Reproducible in v4.3.dev5.official [89f70e98d], v4.2.1-stable, v4.2-stable, v4.2-dev1
Does not exactly reproduce in v4.1.4-rc2, v4.1-stable, or v4.0-stable, but a different undesirable behavior is seen instead (resulting file is blank, no error message)
### System information
Godot v4.2.stable - Windows 10.0.19045 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3070 (NVIDIA; 31.0.15.3699) - Intel(R) Core(TM) i5-10600K CPU @ 4.10GHz (12 Threads)
### Issue description
One object of type Alpha contains references to objects of type Beta. Beta itself also references to Beta objects. The same Beta objects are referred to in both; that is, the Beta objects in the Alpha reference each other.
If you use ResourceSaver.save() to serialize an Alpha object to disc, a number of the references in the arrays in the Beta objects will instead be written as 'null'. For each such instance, the following error message will be produced:
```
E 0:00:00:0542 test.gd:32 @ _ready(): Resource was not pre cached for the resource section, bug?
Method/function failed. Returning: "null"
scene/resources/resource_format_text.cpp:1853 @ _write_resource()
test.gd:32 @ _ready()
```
I dug into the resulting .tres file and what seems to be happening is that as the ResourceSaver is writing out the references held in the Beta object, it only correctly references the other Beta objects _if it has already seen them in the Alpha object_. Otherwise, it can't find them, and writes null.
I tried saving as both .tres and .res, and with and without FLAG_BUNDLE_RESOURCES set; the same behavior was seen in all cases.
Here's an example .tres file produced by my minimum reproduction project (renamed so github would accept it): [file.txt](https://github.com/godotengine/godot/files/14782054/file.txt)
If you read it, you'll notice that the further down the file you go, the fewer null values you see. The first beta object's references are all null. Then for the next one, they're all null except for the first one that was written to the file. And for the third, all null except for the two we've already written. Etc. This is a simple example, but I've spent considerable time going over a much more complex real-world example (from when I first encountered this) and the same pattern holds.
It seems likely that this is caused by the circularity of the references, but I can't find any documentation on the ResourceSaver, ResourceFormatSaver, or Resource pages indicating that this is supposed to be invalid or a known issue.
I tested with versions going back to 4.0; prior to 4.2, the behavior was different, but also incorrect: it would silently fail, producing a blank file and no error at all.
### Steps to reproduce
Open the Minimum Reproduction Project I've provided, and run the Project. You'll see the error messages appear, and the broken file will be generated at "user://file.tres".
### Minimal reproduction project (MRP)
[Bug Minimum Reproduction.zip](https://github.com/godotengine/godot/files/14782105/Bug.Minimum.Reproduction.zip)
Contributor guide
Research direction
Start with the provided Minimum Reproduction Project and ResourceSaver.save(), reproducing the broken user://file.tres output. Inspect resource_format_text.cpp at _write_resource() around line 1853, where the reported pre-cache error occurs. Done means circular Beta references are serialized instead of becoming null, with the reproduction project no longer producing the reported errors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- game-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100