Chunk Loading failing when game is exported

Looking over that comment, that is the intended logic.

So the reason I use a combination of file paths is so that at runtime, if the player selects a new save file, it loads the original chunk scenes defined in the editor. But as the player moves through the world, the chunks unload and save to disk, where I use “user://,” and then any subsequent loading checks if those chunks have already been saved by seeing if the “user://” path exists. If it does, it loads that chunk; if it doesn’t, it checks whether the chunk has an original instance; if that instance doesn’t exist, it loads a static default chunk that is never saved. Does that help?

It doesn’t appear to do that, or at least for load_single_chunk. This function uses the previously mentioned res:// path directly into this load_or_instantiate function which checks if the res:// path exists and tries to load it, there isn’t a user://-side check or substitution happening in this code.

If you look here:

func load_single_chunk(coord: String) -> void:
	var coords    := string_to_vec2i(coord) #converts coord to vec 2
	var save_path := _chunk_save_path(coords)   #converts coords to "user://" path

	var scene_path := "res://scenes/chunks/" \
		+ str(coords.y) \
		+ "/chunk(" + str(coords.x) + "," + str(coords.y) + ").tscn"
	var fallback := get_chunk_scene(scene_path) #calculates "res://" scene
	if fallback == null:
		fallback = load("res://scenes/chunks/chunk(default).tscn") #defaults to default chunk

	var chunk_instance := NodeSerializer.load_or_instantiate(
		save_path,
		fallback,
		func(n): if n.has_method("mark_loaded_from_save"): n.mark_loaded_from_save()
	)

The user path is calculated. through chunk_save_path like this:

func _chunk_save_path(coords: Vector2i) -> String:
	return _chunk_save_folder() + "chunk_%d_%d.tscn" % [coords.x, coords.y]

func _chunk_save_folder() -> String:
	var slot := _slot_from_save_location()
	return "user://saves/" + GlobalData.version \
		+ "_saveload" + str(slot) + "/Chunks/"

while the fallback (“res://”) path is calculated by using the get_chunk scene function. If get_chunk scene returns null, it defaults to the default chunk as intended and then passes the “user://” and “res://” paths into load_or_instantiate.

Here, the code checks if the save_path (“user”) exists and loads it accordingly; otherwise, it loads the fallback scene (“res”), which has already been precalculated if it needs to be the default chunk.

static func load_or_instantiate(
	save_path:      String, #user path
	fallback_scene: PackedScene, #res path
	before_add_fn:  Callable = Callable(),  
) -> Node:
	var instance: Node = null

	if FileAccess.file_exists(save_path): #check if user exists
		var packed := ResourceLoader.load(
			save_path, "", ResourceLoader.CACHE_MODE_IGNORE
		) as PackedScene
		if packed == null:
			push_error("NodeSerializer: failed to load packed scene: " + save_path)
			return null
		instance = packed.instantiate()
		if before_add_fn.is_valid():
			before_add_fn.call(instance)
	else: #otherwise load res
		if fallback_scene == null:
			push_error("NodeSerializer: no save found and no fallback scene provided.")
			return null
		instance = fallback_scene.instantiate()

	return instance

This code all works as intended. Is there something I’m missing?

Looking at your solution, I don’t really understand what changed. Could you explain to me how I could implement that into my code?

Apologies, I must’ve conflated save_path and scene_path together.

Have you posted any errors or print statements from your exported build?

No worries! I wanted to make sure I didn’t miss something. Not yet. How do I check for those? I didn’t know you could do that.

If you open the exported game in a terminal it will log to that terminal. On windows you can shift-right click the exported folder and select “open power shell here” then type in the executable’s name MyGame.exe to run it. On linux and mac you may a similar option from your file explorer, or you can type commands to change directories to your exported folder.

I updated my last reply in that thread with the code I ended up making so you can compare the two.

Ok, so I just ran it in the terminal and in the editor, and here’s what the print statements returned:

When in the editor:

testing chunk
testing the chunk
not in cache yet
setting cache

when exported:

testing chunk

testing the chunk

not in cache yet

setting cache

WARNING: ‘res://scenes/chunks/0/chunk(-3,0).tscn’: In external resource #2, invalid UID: ‘uid://lbnmjcoai2px’ - using text path instead: ‘res://scenes/nav colliders/trees/basic_tree/basic_tree_large.tscn’.

 at: open (core/io/resource_format_binary.cpp:1092)

WARNING: ‘res://scenes/chunks/0/chunk(-3,0).tscn’: In external resource #3, invalid UID: ‘uid://deu4ddmm6tf5f’ - using text path instead: ‘res://scenes/nav colliders/trees/basic_tree/basic_tree_mid.tscn’.

 at: open (core/io/resource_format_binary.cpp:1092)

WARNING: ‘res://scenes/chunks/0/chunk(-3,0).tscn’: In external resource #5, invalid UID: ‘uid://djlw4hb37y1kh’ - using text path instead: ‘res://scenes/nav colliders/trees/basic_tree_berries/berry_tree_mid.tscn’.

 at: open (core/io/resource_format_binary.cpp:1092)

WARNING: ‘res://scenes/chunks/0/chunk(-3,0).tscn’: In external resource #8, invalid UID: ‘uid://k41gp2pxlgto’ - using text path instead: ‘res://scenes/NPCs/new_dog.tscn’.

I added print statements in the following places: (-3, 0) is the only one that isn’t appearing:

func get_chunk_scene(path: String) -> PackedScene:

	if "(-3,0)" in path:
		print('testing the chunk')
		
	if not chunk_scene_cache.has(path):
		if "(-3,0)" in path:
			print('not in cache yet')
		if ResourceLoader.exists(path):
			if "(-3,0)" in path:
				print('setting cache')
			chunk_scene_cache[path] = load(path)
		else:
			if "(-3,0)" in path:
				print('doesnt exist, setting null')
			chunk_scene_cache[path] = null
	return chunk_scene_cache[path]

func load_single_chunk(coord: String) -> void:
	var coords    := string_to_vec2i(coord)
	var save_path := _chunk_save_path(coords)  # computed NOW, from current slot
	

	if "_-3_0" in save_path:
		print('testing chunk')
		
	var scene_path := "res://scenes/chunks/" \
		+ str(coords.y) \
		+ "/chunk(" + str(coords.x) + "," + str(coords.y) + ").tscn"
	var fallback := get_chunk_scene(scene_path)
	
	if fallback == null:
		if "_-3_0" in save_path:
			print('fallback on -3, 0')
		fallback = load("res://scenes/chunks/chunk(default).tscn")

Well you may need to re-save your berry trees and/or chunk -3, 0 to generate/fix it’s UID but that shouldn’t be a huge problem, only a warning after all, maybe it’s a worse failure after exporting.

Thank you!! So I refactored my enemies a little while ago, and I hadn’t saved it since then, so all I had to do was delete the old instance and save!