godotengine / godotengine/godot

ResourceSaver.save() writes null for sub-resources that refer to each other

Open
#89,961 12 comments 0 reactions 0 assignees View on GitHub
discussion documentation topic:core
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.