godot: Resource.duplicate(true) doesn't duplicate subresources stored in Array or Dictionary properties

Godot version

4.0.stable

System information

Manjaro Linux

Issue description

This behavior is poorly documented, and it breaks expectations. It is mentioned in the docs only under Array.duplicate(), but not Dictionary class, or Resource.duplicate().

This affects also “Make Unique” and “Make Unique (Recursive)” for custom resources.

Here’s the problematic code: https://github.com/godotengine/godot/blob/fc7adaab7b3856a7831d402ea2bbb27efe7b7d8a/core/variant/variant_setget.cpp#L1889-L1900

Steps to reproduce

For simple code see reproduction project.

To reproduce on your own:

  1. Have a resource with a property of type Array[SomeOtherResource]
  2. One of the following: 2a) “Make Unique” on parent resource in inspector. 2b) Or duplicate(true) from code.
  3. Check if SomeOtherResource items inside array are unique.

Minimal reproduction project

res_bug_reproduction.zip

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 49
  • Comments: 18 (1 by maintainers)

Commits related to this issue

Most upvoted comments

It’s troublesome that this isn’t fixed yet, my game relies a lot on duplicating resources and arrays and I was really miffed when I just found out deep copying isn’t functioning properly. This should be tracked and added to a milestone.

I confim that my_resource = my_resource.duplicate(true) doesn’t make unique sub-resources as the documentation says. Godot 4.1.

Can this at least be updated in the documentation ASAP even if it won’t be fixed any time soon? The Resource.duplicate docs confidently state:

If subresources is true, a deep copy is returned; nested subresources will be duplicated and are not shared.

Which is clearly not true and causing headaches for many users in this thread. It’s now been over 1 year with no comments from the team.

Oh my god, I was confused why all my enemy mobs die at the same time like they have shared HP. Had to make multiple loops to manually duplicate subresources in my ready function to work around this. This is rough for stat heavy games out there.

I can’t believe this hasn’t been fixed, my project relies heavily on resources that have arrays or dictionaries in them, for localToScene to not work with them is really bad. I’m now having to write duplication methods for more than 10 different classes.

This seems to have been fixed. Using Resource.duplicate(true) duplicates the subresources as well in v4.2.1

I’ve tested this recently in 4.2.1 and no, it wasn’t fixed when using Array at least. If you think it’s fixed, please post a simple example of it working, as it might help solve the issue.

it’s not fix now. has any way to fix this problem?

I made a gdscript class, it’s extend Resource to implement a function call _duplicate to fix this problem.

But please fix this in the engine as soon as possible.

the class code is :

class_name BaseResource

Edit: So, I kinda figured it out where my (and many others) issues come from regarding duplicating resources and saving and loading them.

The initial problem is that saving and loading subresources just does not work if you simply save and load using ResourceSaver and RexourceLoader IF any of your subresources are actually instanced. ResourceSaver will just save a reference to the project file instance.

Now, the "FLAG_BUNDLE_RESOURCES " should fix it, but it is actually not working right now (#65393)

If you want to save your instanced subresources you need to deep duplicate them using “BaseResource”.

It’s also important du make deep duplications when you have a resource “template” saved that you later want to add to an array List multiple times.


I am trying to use this, together with: https://github.com/godotengine/godot/issues/65393#issuecomment-1847601360 to save and load resources that contain an Array with other Resources.

It does not work unfortunately:

E 0:00:09:0688   SaveManager.gd:17 @ loadAll(): Attempted to assign an object into a TypedArray, that does not inherit from 'GDScript'.
  <C++ Error>    Condition "!other_script->inherits_script(script)" is true. Returning: false
  <C++ Source>   core/variant/container_type_validate.h:140 @ validate_object()
  <Stack Trace>  SaveManager.gd:17 @ loadAll()
                 SaveManager.gd:9 @ _init()
		var loadfile = ResourceLoader.load(SAVEDIR,"GameStats", 0)

E 0:00:09:0688   SaveManager.gd:17 @ loadAll(): unsupported format character
  <C++ Error>    Condition "error" is true. Returning: String()
  <C++ Source>   ./core/variant/variant.h:834 @ vformat()
  <Stack Trace>  SaveManager.gd:17 @ loadAll()
                 SaveManager.gd:9 @ _init()

E 0:00:09:0688   SaveManager.gd:17 @ loadAll(): Method/function failed.
  <C++ Source>   core/variant/array.cpp:222 @ assign()
  <Stack Trace>  SaveManager.gd:17 @ loadAll()
                 SaveManager.gd:9 @ _init()

E 0:00:09:0695   SaveManager.gd:17 @ loadAll(): Attempted to assign an object into a TypedArray, that does not inherit from 'GDScript'.
  <C++ Error>    Condition "!other_script->inherits_script(script)" is true. Returning: false
  <C++ Source>   core/variant/container_type_validate.h:140 @ validate_object()
  <Stack Trace>  SaveManager.gd:17 @ loadAll()
                 SaveManager.gd:9 @ _init()

E 0:00:09:0695   SaveManager.gd:17 @ loadAll(): unsupported format character
  <C++ Error>    Condition "error" is true. Returning: String()
  <C++ Source>   ./core/variant/variant.h:834 @ vformat()
  <Stack Trace>  SaveManager.gd:17 @ loadAll()
                 SaveManager.gd:9 @ _init()

E 0:00:09:0695   SaveManager.gd:17 @ loadAll(): Method/function failed.
  <C++ Source>   core/variant/array.cpp:222 @ assign()
  <Stack Trace>  SaveManager.gd:17 @ loadAll()
                 SaveManager.gd:9 @ _init()

Has anyone managed to save and load resources that contain an Array of another resources?