Saving nested resources without errors

Godot Version

4.5

Question

Hello! I’ve been trying to implement a saving system in my game using custom resources, but whichever way I do it either doesn’t work or throws errors.

So I have a custom persistent data resource, which holds an array of defeated enemies and a Player resource. The player resource holds all player stats, equipped items, and inventory. Every item (equipped and in inventory) has a NarrationLine resource attached to it.

So I’m using a global DataManager script to save and load the game, which looks like this:

var persistent_data : PersistentDataResource

var save_path := "user://data.tres"

func _ready():
	persistent_data = load("res://Resources/persistent_data.tres")
	
	load_game()

func save_game():
	ResourceSaver.save(persistent_data, save_path, ResourceSaver.FLAG_BUNDLE_RESOURCES)
	
func load_game():
	if FileAccess.file_exists(save_path):
		persistent_data = ResourceLoader.load(save_path)
	

When I tried using this script without the ResourceSaver.FLAG_BUNDLE_RESOURCES, the defeated enemy data within persistent data saved fine (as it is just an array of Strings), but the Player data did not save at all. But now that I added ResourceSaver.FLAG_BUNDLE_RESOURCES, on starting the game it throws an error “Class “NarrationLine” hides a global script class.

I’m aware there are similar issues brought up before, but I was wondering if Godot 4.5 fixed it or if there is a way to save nested resources without workarounds.

Would be grateful for any help!

Check for circular dependencies.

2 Likes

Hello! Thank you for your reply!

I honestly don’t think there are any circular dependencies in here? To be more clear, the Persistent Data resource that I am saving hold two arrays of Strings and the Player Resource. The Player Resource hold variables (float, int, String), a Weapon Resource and an Armor Resource (which inherit from the Item Resource), and an Inventory Resource. The Inventory Resource holds an array of Item Resources. Every Item Resource holds several NarrationFull resources, which holds an array of NarrationLines. Narration Line holds a String and several variables.

I don’t see any circular dependacies in here, but perhaps I am mistaken?

Any more fixes for this? I’m at the end of my rope here :frowning:

Can you make a minimal reproduction project?

Hello again! So I made a project showcasing my issue, which can be found here: GitHub - biphasicSleeper/save-test: saving test for godot 4.5 (hopefully I uploaded it right, I’ve never done it before).

When you try to save without using the bundle resources flag, the inventory does not save. But when you use the flag, it throws a “Class “Item” hides a global script class.” error.

No more updates on this? Can someone tell me how to save and load nested resources properly? I’ve watched and read a lot of tutorials, but for some reason none of them helped with this issue.

TL;DR

What you currently have:

  • PersistentData, PlayerInfo, Inventory and Items are all standalone resources.

What you probably want:

  • PersistentData is a standalone resource that embeds PlayerInfo and Inventory (because these should be saved together)
  • Items are standalone resources (because they are provided by the game)

To embed the PlayerInfo and the Inventory in the PersistentData, open the PersistentData in the editor, click on PlayerInfo and select “Make Unique”. The go into the PlayerInfo, click on Inventory and also select “Make Unique”


More thorough explanation

There are two ways to save nested resources:

  • they can be embedded and saved as part of a parent (either a scene or another resource)
  • they can be standalone and saved as separate resource files

ResourceSaver.save() without ResourceSaver.FLAG_BUNDLE_RESOURCES saves a resource and its embedded resources and stores references to external (standalone) resources.

ResourceSaver.save() with ResourceSaver.FLAG_BUNDLE_RESOURCES saves a resource and embedds all resources, including the referenced scripts.

When loading a saved resource with ResourceLoader.load() it will load the saved resource.

  • If the saved resource contains references to standalone resources it will re-add those resource (this is where your first attempt fails: PersistentData contains a reference to the standalone PlayerInfo and after loading you will see the original PlayerInfo as stored in the game)
  • If the saved resource contains scripts it will try to execute them (this is where your second attempt fails: the stored scripts declare classes, but those classes already exist)
  • If the saved resource contains an embedded PlayerInfo and Inventory it will use these.

When working with resources in the editor you have these groups of operations:

  • “New ‘ResourceType’” creates a new embedded resource
  • “Quick Load” and “Load” let you add a reference to a standalone resource
  • “Save” and “Save As” let you save an embedded resource as a standalone resource
  • “Make Unique” and “Make Unique (Recursive)” let you make copies of standalone resources and embed those copies
1 Like