Is it possible to save resources that are within another resource?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By GameBubby

is it possible to save resources that are within another resource? For example I have a save file resource that contains another resource (player inventory) and when I load the save the inventory resource doesn’t get saved along with the overall save file. My save file script:

extends Resource
class_name SaveFile

export(int) var map_seed = 0
export(int) var depth = 1
export(Vector2) var grid_pos = Vector2()
export(float) var ang = 0
export(Vector3) var rotation = Vector3()
export(Resource) var attributes = null
export(Resource) var inventory = null
export(Resource) var equipment = null

I create a new inventory (Inventory.new()) during new save file creation and when I load up the game it doesn’t seem to save the inventory resource (or any of the other resource exports). Am I doing something wrong or can subresources just not be saved?

:bust_in_silhouette: Reply From: Parv

ive had the same question myself.
turns out you just need to use the flag FLAG_BUNDLE_RESOURCES as specified on the docs, because by default the tres/res file will just save a reference to the path of subresources.
so it will look like this:
ResourceSaver.save(path, resource, ResourceSaver.FLAG_BUNDLE_RESOURCES)

also for some reason the opposite load function will not work if you try to cast it like:
var save = ResourceLoader.load(path) as SaveResource which will return null.
so just use duck typing when loading it back.

Thank you so much, it works!

GameBubby | 2021-12-17 04:06

What do you mean with ducktyping? I can not get any data back from user ResourceLoader.load(path).

voylin | 2022-02-05 06:48

dont try to cast it to your save class when loading since for some reason it will break it.

Parv | 2022-02-05 18:28

A bit late, but:

The reason you can’t cast is that bundling resources re-declares that resource locally. Any resource kept in that file is now of type res_file_name_goes_here/res_name_goes_here.tres, even the very resource of the file itself, and Godot treats the bundled resources and the script resources as completely different objects. So, because you’re trying to cast this bundled resource as a script resource, Godot thinks you’re saying that this apple is an orange, and flips out.

SeaFishelle | 2023-02-08 05:31

Trying to figure this out in Godot 4, but can’t seem to understand it. Would you mind explaining this in more detail?

Currently, I’m saving a resource (player data) with a sub resource (inventory data).
Looking at the saved file in a text editor confirms that I am saving these sub resources,
or at least references of them? (I’m using the flag: FLAG_BUNDLE_RESOURCES).

(I’ll try to provide more detail once I’m next to my computer)

But anyway, upon loading this resource, I get an error saying that the sub class is hiding a script (if I remember correctly, but again, I’ll double check once I’m by my PC).

So I guess the problem is, how do I load a resource which has multiple sub resources nested inside it?

Thanks, and sry for any confusion.

Queble | 2023-03-28 19:56

1 Like

@Queble running into this exact same issue in v4.4 - certain saved resources within a master resource save file will not load. When Saved using the ResourceSaver.FLAG_BUNDLE_RESOURCES flag upon load I get a "Class “MyResourceClass” hides a global script class error. FWIW this seems to happen with a 2D Array of Resources within a master resource save file.

Apologies to necropost but curious if any further solution or guidance here since the thread has gone cold.

@ei8htbit so coming back to this, I actually ended up just using the default flag (FLAG_NONE), since Godot will automatically save all sub-resources by default.

I’m pretty sure “bundling” the sub-resources will actually try and construct a new class when loaded, since it basically stores the entire script inside the file as well instead of just saving the changed properties. Not sure if I’m correct about that as the docs are a bit lacking in this area.

I actually ended up making a video on this once I figured it out haha: https://www.youtube.com/watch?v=VGxYtJ3rXdE

@Queble Thanks for your response, I really appreciate the feedback - and yes I’ve watched your video, super helpful. Despite all of this, I’ve tried a few different ways but can’t seem to find a way using Resources to persistently load an Array holding an Array of Resources that contain a Dictionary member variable of data. The file saves and opening it up I can see it holds references to the resources and sub resources, but on load it’s just an empty hull.

The resource I’m trying to pack can be simplified as the following:

class_name SaveResource
extends Resource

@export var array_of_array_of_dictionary: Array = [
    [DictionaryResource.new(), DictionaryResource.new(), DictionaryResource.new()],
    [DictionaryResource.new(), DictionaryResource.new(), DictionaryResource.new()],
    [DictionaryResource.new(), DictionaryResource.new(), DictionaryResource.new()],
]

I can save this Resource file and the data remains, I can load this data live while the program is still running, but upon closing the app and opening, and then loading this resource file saved to desktop (and verifying the file is loaded from the same directory source and name, etc) it only loads empty Dictionarys within this array (structure remains but dictionary data empty).

Is it because I’m declaring the size and structure of the initial Array with “.new()” constructors for the Dictionary resources? Would the existing save file not just overwrite those values locally upon load?

Here’s a sample strucutre of the dictionary resource in case that helps:

class_name DictionaryResource
extends Resource

var data_stuff: Dictionary = {
	"team_one": {
		"team_name": "",
		"team_number": 0,
		"data_thing": 0,
	},
	"team_two": {
		"team_name": "",
		"team_number": 0,
		"data_thing": 0,
	}
}

Recently ran into this with my project. I have nested resource arrays, but the same error "Class “MyResourceClass” hides a global script class error.

I’m not sure if this is gonna help you but I had a slightly similar problem. I have a savefile resource where I put all my save data and I wanted to save my items data which includes a list of items that I can modify on runtime, for example when I upgrade an item its stat change. By default it would save the list but the resources inside would be “linked” to those in my folders and godot wouldnt care that they have been modified so it would just categorise them as “ext” and save only a path. So before I save the game I would have to loop thru the items and add 2 things:

item.take_over_path(“”) # this unlinks the resource
item.resource_local_to_scene = true

Maybe it will help someone in the future

1 Like

Landed on this topic because I’m facing the same issue.

@cas-p: Thank you so much for the workaround! I would have never guessed it.
Just to make it very obvious to anyone who sees this topic, do not use the FLAG_BUNDLE_RESOURCES. So the code would be something like:

func save_shop_state() -> void:
	for item in DataManager.shop_data.shop_item_list:
		item.take_over_path("")
		item.resource_local_to_scene = true
	ResourceSaver.save(shop_data, save_folder_path + shop_list_save_filename)

(And hi @Queble, love your channel!)

1 Like