Project works when launched from IDE, fails when built

Godot Version

v4.2.1.stable.official [b09f793f5]
Godot v4.2.1.stable - macOS 14.2.1 - Vulkan (Forward+) - integrated Apple M1 - Apple M1 (8 Threads)

Question

When I launch the game from the editor, it works just fine, but the actual build of the same project doesn’t launch a particular starter scene (and gets stuck basically), both on my computer and on other people’s computers using Windows.

My setup: I use a “Scene Manager” node to swap levels during the game. When the game just launches, the Scene Manager (among other things) retrieves files from a particular path and lists them into an array in the Ready function, and then calls a deferred spawning function to spawn the first level.

This spawning function first checks the path is valid etc, then actually composes the path from which the scene is meant to be loaded, and then loads a particular scene based on its serial number. The code where I’m getting different readings based on the way I’m launching the project is:

SL = load(sequential_levels_directory+sequential_levels_list[T]) # T is the serial number
SN = SL.instantiate()

I wrapped this into print()s to check what I’m getting:

label.text = "SL file: " + sequential_levels_directory+sequential_levels_list[T] # this indeed returns the path
# loading and instantiating code from above
label.text += "\nSL: " + str(SL) + "\nSN: " + str(SN) # but this does not work from build

Here are the readings I’m getting when launched from IDE (L) and from the build (R):


As you can see, the last two lines show that on the left the node loads just fine and gets spawned as expected. But on the right it is reporting there’s nothing loaded to begin with.
Am I doing something obviously wrong or is this some setting that I’m supposed to adjust when exporting or?…

And just to confirm, I’m using built-in signing on MacOS:
image

It seems that the path is somehow not composed properly and is breaking in the build, because when I just copy paste a path of a node containing the level, the build actually works. It’s strange, since the composed path and the path I pasted wholesale are meant to be identical (it can be seen in the screenshots)…

The pasted path did get a “tscn.remap” extension (what’s that?).

ok this was a massive, massive PITA, took me forever to figure this out.

What was breaking it is that the files were getting a “tscn.remap” extension and of course they were failing to load because I was composing the paths as they are seen in the editor, had no idea there is such a thing as “remapping” of the files.

It turns out there is a project setting that effectively makes “load()” dysfunctional as is (?) where it works in the IDE and doesn’t work in a build, and by default it’s set to “ON”. So if I want to

load(level_name_path)

this won’t work because the path_name will change to path_name + “.remap” once the project is built.

This is what I had to turn off to make it work:


Now I am at least able to see the game running (although it’s all glitchy and again the build behaves differently than the game launched from the IDE but it’s a step forward…).

More info here:

Another solution (but one wonders, is this really necessary):

Apparently this whole remap case is because the tscn files don’t actually exist later, fair enough I guess it’s necessary, but then one wonders what is the point of “load()” in it’s current form if it’ll break once you make a build…

load() does not break once you make a build. You didn’t provide any meaningful code so I’m only assuming that the code was trying to load() a .remap file which won’t work as that’s not a known Resource extension.

If you do load("res://my_scene.tscn") it will work in the editor and in a exported build.

That is not my impression after reading the Github threads.

Anyway here is the code, this’ll fail in build and work perfectly fine when launched from IDE:

func _ready():
	var path_dir = "res://levels/"
	var levels_list = DirAccess.open(path_dir)
	var sequential_levels_list = levels_list.get_files()
	
	var path_get = path_dir+sequential_levels_list[0]
	
	var S = load(path_get)
	var L = S.instantiate()
	add_child(L)

Just make an empty project with this setup:
image
and add the code to the project.tscn.

And this is what it looks like on my end (L: launched from IDE, R: build) (Edit: the white square in the upper left corner is the “room 01.tscn”):

My assumption was right then. When you do it from the editor the tscn files are there so DirAccess.get_files() return the tscn files. When you do it on an exported build with Convert text files to binary enabled the tscn files are not included but remap files are so DirAccess.get_files() returns the remap files which, as said before, won’t work with load() as they are not a known Resource extension.

This should work

var sequential_levels_list = levels_list.get_files().map(func(level:String):
	if level.get_extension() == "remap":
		# Remove the .remap extension
		return level.replace(".remap", "")
	else:
		return level
)

…yes exactly, this solution is what is already used as a workaround on Github in the thread I linked to (and elsewhere in the same or related threads there are a few different variations too).

So the solution is known. But that is not the point, the problem is that the default settings are such that they guarantee “load()” will fail without the patch / workaround / trimming in this relatively common setup. It’s completely counterintuitive (do you think anyone would add the “.remap” trimming function spontaneously before discovering how the whole remapping works?). I understand to some extent why this happens and I’m not arguing against it, I’m just saying that it’s weird.

1 Like