Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

On export, every .tres file is renamed to .tres.remap #66014

Closed
Kyle-Mendes opened this issue Sep 17, 2022 · 28 comments
Closed

On export, every .tres file is renamed to .tres.remap #66014

Kyle-Mendes opened this issue Sep 17, 2022 · 28 comments

Comments

@Kyle-Mendes
Copy link

Godot version

4.0 Beta 1

System information

Mac OS

Issue description

When exporting my game in Godot 4, every .tres file is exported as .tres.remap This means that I can't call load(resource) because Godot throws:

ERROR: No loader found for resource: res://Resources/Items/Seeds/AutumnWingsGourdSeeds.tres.remap.

I think this started around Godot 4 - Alpha 16

I'm not sure what the cause is, and there is very little information available online about .tres.remap files.

Steps to reproduce

Here's the sample code I'm using to encounter this:

func _ready():
	# Animals
	load_resource('res://Resources/Animals', Directory.new(), animals, animal_dict)

func load_resource(path: String, dir = Directory.new(), array = null, dict = {}):
	dir.open(path)
	dir.list_dir_begin()

	var file_name = dir.get_next()

	while(file_name != ""):
		if dir.current_is_dir():
			var newDir = Directory.new()
			load_resource("{0}/{1}".format([path, file_name]), newDir, array, dict)
		elif dir.file_exists("{0}/{1}".format([path, file_name])) and '.tres' in file_name and not '.tres.remap' in file_name:
			var r = load("{0}/{1}".format([path, file_name]))
			if array != null:
				array.append(r)

			if dict != null:
				dict[r.id] = r 

		file_name = dir.get_next()

The goal is to load all the animals at run time and store them in an array & dict in memory for later use. This worked in Godot 3 and much of the 4 Alphas, but has stopped working for me recently.

Minimal reproduction project

No response

@Kyle-Mendes
Copy link
Author

It looks like all the tres files are being remaped to point to .godot instead:

[remap]

path="res://.godot/exported/133200997/export-2363f4d1f6f2aee8abf54dc65c183031-FlintCorn.res"

This is pretty problematic because there're definitely use cases where you want to dynamically load files in a directory, but now they're all remapped. Ideally, load would know to say if remap: load(remap.path)

@Kyle-Mendes
Copy link
Author

I was able to work around this like so:

	while(file_name != ""):
		if dir.current_is_dir():
			var newDir = Directory.new()
			load_resource("{0}/{1}".format([path, file_name]), newDir, array, dict)
		elif dir.file_exists("{0}/{1}".format([path, file_name])) and '.tres' in file_name:
			if '.tres.remap' in file_name: # <---- NEW
				file_name = file_name.trim_suffix('.remap') # <---- NEW

			var r = load("{0}/{1}".format([path, file_name]))
			if array != null:
				array.append(r)

			if dict != null:
				dict[r.id] = r 

		file_name = dir.get_next()

However, this doesn't feel like the right approach for all users. I think adding the ability to load remap files (at the least) would be a big improvement.

@TokisanGames
Copy link
Contributor

TokisanGames commented Sep 21, 2022

Specifying load("res://file.tres") should load on export and if it doesn't that's a bug.

There have been long standing issues with dynamically generated names on export, eg. load(directory + filename). See #42507 #25672

And here was a proposed solution for those issues
#59334

You can try changing your project settings to enable convert_text_resources_to_binary (on export) and see if that helps. Although that might change to yet another name. I have this in my code:

				# Capture renamed binary export files
				if fname.matchn("*.tres.converted.res"):
					fname = fname.replace(".converted.res", "")
				if fname.matchn("*.tres") and ResourceLoader.exists(fname):
					var item = load(fname)
					if not item:
                                         ...

@henriquelalves
Copy link
Contributor

This also happens on ".tscn" scenes I tried loading in runtime, a simple "Levels" directory inside my project with all the level scenes. I get a _load: No loead found for resource: res://Scenes/Levels/Level1.tscn.remap and so on.

I feel this is really unintuitive for users. A "Levels" directory is even an example Godot gives in its own documentation (on DirAccess), and exporting it like this is impossible to know what is the problem without arriving to this specific Issue. Why load can't trim the .remap itself?

Dunkhan added a commit to Dunkhan/sankari that referenced this issue Oct 21, 2022
…odotengine/godot#66014 This might be fixed by Godot in the future and this change should be reverted
Dunkhan added a commit to Dunkhan/sankari that referenced this issue Oct 21, 2022
…odotengine/godot#66014 This might be fixed by Godot in the future and this change should be reverted
@CsloudX
Copy link

CsloudX commented Nov 6, 2022

I was meeting this bug too. And it's take my so many time to debug, because my code was work correct in editor bug can't work when exported.

...
for i in DirAccess.get_files_at(SHEEP_RES_DIR):
	if i.ends_with(".tres"):
		var sheep_name = i.get_basename()
		if not unlocked_sheeps.has(sheep_name):
			locked_sheeps.append(sheep_name)
...

@EmptyForge
Copy link

Adding that this is still an issue in 4.0 beta 2 and is really unintuitive to fix:
ERROR: No loader found for resource: res://scenes/feat_buttons/RavenFeatButton.tscn.remap.

Code:

var feat_button_dir = DirAccess.open(feat_button_dir_path)
	if feat_button_dir:
		var all_fb = feat_button_dir.get_files()
...
		for idx in indices:
			var new_fb = load(feat_button_dir_path + all_fb[idx]).instantiate()

@keeganstothert
Copy link

keeganstothert commented Dec 8, 2022

I'm also experiencing this issue on 4.0 beta 7 ios export.

@bend-n
Copy link
Contributor

bend-n commented Dec 10, 2022

if '.tres.remap' in file_name: # <---- NEW
	file_name = file_name.trim_suffix('.remap') # <---- NEW

Its also possible to simply file_name = file_name.trim_suffix('.remap'), as if the suffix isnt there, it simply doesnt do anything, and this way it works for all kinds of resources.

@clayjohn clayjohn added the bug label Jan 25, 2023
@clayjohn clayjohn moved this from To Assess to Todo in 4.x Priority Issues Jan 25, 2023
@FeralBytes
Copy link
Contributor

FeralBytes commented Feb 4, 2023

This also affects tscn files now too... it is like the .import issue and the .gdc issue, Godot needs to have a file access method that works like a virtual one, that can remember the way the files were when we are developing the game, as compared to having to hack around all of the ways in which the engine edits our files. Also I though .godot folder was going to fix this issue?

See #42507 & #25672
I now have at least 3 distinct hacks for this type of growing issue. If you look at the other issues you will find my other hacks.

if ".remap" in file:
    file = file.replace(".tscn", "")

@akien-mga
Copy link
Member

This is intentional, and the usability issue around trying to load such files with DirAccess/FileAccess instead of loading them as resources using high level APIs is tracked in #25672.

@github-project-automation github-project-automation bot moved this from Todo to Done in 4.x Priority Issues Feb 22, 2023
@akien-mga akien-mga removed the bug label Feb 22, 2023
@giulong
Copy link

giulong commented Apr 10, 2023

Still an issue in Godot 4.0.2.stable when exporting for android, at least for .tscn files. Temporarily solved with the already proposed workaround:

if file_name.ends_with(".remap"):
	file_name = file_name.trim_suffix(".remap")

@MTadder
Copy link

MTadder commented Nov 30, 2023

This is STILL an issue. Tested in Godot 4.1.3.
That being said, I'm really not sure why someone has closed this Issue. If it has not been resolved, it's still an issue, right???
The fore-mentioned fix works, but is somewhat cumbersome. Godot pls fix.
if f_path.ends_with(".remap"): f_path = f_path.trim_suffix(".remap");

@MTadder
Copy link

MTadder commented Nov 30, 2023

This is intentional, and the usability issue around trying to load such files with DirAccess/FileAccess instead of loading them as resources using high level APIs is tracked in #25672.

Umm, why would you intentionally make such a function do this? Why not VERY SIMPLY add if f_path.ends_with(".remap"): f_path = f_path.trim_suffix(".remap"); into load?

@AThousandShips
Copy link
Member

AThousandShips commented Nov 30, 2023

This happens because scenes and resources are exported in the binary format by default, see editor/export/convert_text_resources_to_binary, but you can't just store the file as tres because that wouldn't work, that format has to be text, so instead you use a remap to point to the real data in a res file

Understand that the actual tres file does not exist in the exported project, that's the whole point, it is accessible with load but not with FileAccess

So there's no bug here but intended behaviour, and the linked issue is about how to improve usability with regards to searching and accessing files in the exported project 🙂

@richardbonneau
Copy link

@AThousandShips I can't seem to find the best practice if we want to load all resource files from a folder without having to manually update them everytime we add or remove one? The workaround just feels hacky. Any ideas?

@AThousandShips
Copy link
Member

I don't know in this case, but see #25672

@derkork
Copy link

derkork commented Jan 1, 2024

@richardbonneau I made an addon to solve this problem. With the addon you define a "resource group" (which is a new resource type) which contains a list of patterns (e.g. folder/**/*.tres) and which will automatically maintain a list of resources matching that pattern (you can add exclude patterns as well). Then in your code you can get all the resources in the group like this:

var resource_group:ResourceGroup = load("res://path/to/resource_group.tres")
var all_resources_in_the_group:Array[Resource] = resource_group.load_all()

The list is maintained by an editor plugin so it doesn't rely on any non-documented file mapping that Godot applies on export. It will also not do any file system scans when you call load_all as the list is baked into the resource on export.

@jamesresend
Copy link

This issue still persists in v4.2.2.stable.official [15073af]

@bend-n
Copy link
Contributor

bend-n commented Jun 10, 2024

The issue isnt an issue and hasnt been "fixed".

@ElAbuelo2
Copy link

ElAbuelo2 commented Jun 12, 2024

I had to read the whole thread to finally understand that FileAccess isn't mean for dynamic resource loading. Just attempt a load() and check if returned value is null rather than an actual resource. Use load to check if resource exists, not FileAccess.

Use FileAccess when doing something with paths given by user or for custom save game format (personal preference: encrypted json). As general rule, if you have FileAccess doing something in "res://", at least suspect you can refactor to not use FileAccess at all.

What about preload()? Well, those resources must exists with 100% certainty. Otherwise not even have it in your code.

I will share my specific case so people can realize it:

I have a directory for skills (RPG game). skills that apply stats boost may have a companion skillname_stats.tres, so you may (or not) have the following:

slash.tres
slash_stats.tres

The second is optional, and not referenced in slash.tres. Why? Because if referenced in a @export var, you will forget to update that var with correct resource when duplicating (in editor) other skills.tres to save time. Resulting in misbehaving skill, and adding to debug time. If something can be dynamically guessed, prefer it (my personal motto). Example: text files encoding. Do not ask the user for it, they don't know.

At battle screen loading, there is a load_skill(skill_name) function that attempts to dynamically load the companion *_stats.tres that may not exist for every skill.

Original code that misuse FileAccess that works on PC but fails on Device:

 func load_skill (res_name):
 	var filename = "res://skills/" + res_name + ".tres"
	if FileAccess.file_exists(filename):
		var skill = load(filename)
 		SKILLS[res_name] = skill
 		var stats_filename = "res://skills/" + res_name + "_stats.tres"
		if FileAccess.file_exists(stats_filename):
			skill.stats = load(stats_filename)
 	else:
 		print("WARNING: load_skill: requested skill doesn't exist: " + res_name)

Correct code. Use load() and check if null returned.

func load_skill (res_name):
	var filename = "res://skills/" + res_name + ".tres" # No need for weird string manipulation
	var skill = load(filename)
	if skill:
		SKILLS[res_name] = skill
		var influence_stats = load("res://skills/" + res_name + "_stats.tres")
		if influence_stats:
			skill.stats = influence_stats
	else:
		# Good place for assert, but we have a bunch of non existent skills right now
		# and we want to test the game, so we just allow it to continue for now.
		print("WARNING: load_skill: requested skill doesn't exist: " + res_name)

At battle scene loading, you call this function as there are unique skills in all units participating in the battle. If you have the following unique skills ["Jump", "Slash", "Aim", "FocusStance"] distributed among all units (allies and enemies) the scene _ready code will call load_skill() 4 times.

@myin142
Copy link

myin142 commented Jun 22, 2024

@ElAbuelo2 I think the problem isn't about loading the resource, at least for me.

Godot on default changes all .tres resources to .tres.remap and I need to load all the resources in a folder which I get via DirAccess.get_files_at(). But when I try to load the remap resource I get a No loader found for resource because I'm loading the .remap file and not the .tres itself.

The simplest solution is to just disable convert_text_resources_to_binary.

@ElAbuelo2
Copy link

@myin142 If I got this right, load() should be smart enough to try for the ".tres.remap" version when ".tres" doesn't exist. This is the very reason this is not considered even a problem, and was never patched.

But maybe I got this wrong, and this only works if path starts with "res://" protocol. In that case, mixing DirAccess with load() is dangerous without first translating to "res://" like paths and removing the ".remap" at the end.

I understand you are getting a list of filenames all ending with ".remap".

And I understand we want to just walk that list with a for loop. But the "unelegance" of having to remove ".remap" at the end or prepend "res://", before calling load() is small price to pay. By disabling convert_text_resources_to_binary you are paying a bigger price. I don't know the specifics of your project, of-course. And convert_text_resources_to_binary can be turned off for a reason.

I have many projects using Godot. Mainly prototypes. And this is the first time I had problems of this sort, only because I finally mixed FileAccess with load(). I assumed it was a bug, and ended here. Only to learn that is not and no patch is required.

Said in another way, of these three:

  1. var res = load("res://asset_category/asset_name.tres")
  2. var res = load("C:\\Program files\\My game\\asset_category\\asset_name.tres")
  3. var res = load("asset_category/asset_name.tres")
  4. var res = load("res://asset_category/asset_name.tres.remap")

I'm sure number 1 will try for ".tres.remap" version if ".tres" doesn't exist and just work. Number 4 will surely fail, even if ".remap" version exists. And I wonder about the other two.

@myin142
Copy link

myin142 commented Jun 23, 2024

There just doesn't seem to be a clear way on how to load resources dynamically on runtime. What I don't like about changing the path manually is, that you have to know the specifics on how Godot handles these files and who knows if they will change it in the future or not. Having it behave differently when developing and on export is just making it confusing. I already have encountered problems on export multiple times.

By using just one simple option to fix all that is at least worth it for me. It's even documented in convert_text_resources_to_binary to disable it if you rely on runtime loading of files. And from what I see the only downsides is that it will be larger and probably readable by the users. But my projects aren't that big anyway, so I never had problems with that. And most of my code is open-source.

But I'm questioning anyway if I should use resources at all, since I could write it all in a script directly..

@MTadder
Copy link

MTadder commented Jul 12, 2024

So after all this, can we agree that this is not a design issue, but an issue with the overall implicit usability and compatibility of the system's internals?

Godot should be doing everything in it's power to maintain the mapping that the developer has in their mind about where things should be in the filesystem (even if that means just storing a literal dictionary of mappings somewhere ({"player.tscn": "player.tscn.remap"})). Why would the dev be expected to implicitly know that, upon compilation, everything in their res:// morphs and shapeshifts with .remap suffixes? I would think that this is an implementation detail that a dev does not need to know, and should not be held accountable for.

@Odaimoko
Copy link

I think it's better to add some API for ResourceLoader, eg. ResourceLoader.get_files_in_directory. Such API should be similar to FileAccess/DirAccess, but deal with the internal remapping.
In this way, FileAccess/DirAccess remain the low level file system utility, which can be used to access user's file system without breaking changes.
If we change how FileAccess work, this can happen: what if the player of your game accidentally have some files with remap or import suffix, but your Godot app cannot read them with FileAccess.

For another workaround, you can always build an index for your resources in game, and scan the index instead (that's what I always do both in Godot and Unity).

@RudyFisher7
Copy link

RudyFisher7 commented Oct 31, 2024

Okay, just ran into this myself with 4.2.2.stable. Seriously, if all is needed is to remove the .remap suffix (which is an implementation detail), why doesn't ResourceLoader just do that for us under the hood? This would give the Godot devs more freedom to change how they want to handle these paths and it abstracts away this detail from us Godot users so that our projects don't break if/when the implementation changes (say, the Godot devs what to use some other mechanism than adding .remap suffix to the paths)? Or at least implement a ResourceLoader.get_files_in_dir() function to support this as previously suggested here. I love Godot as a nice piece of software but this has me raising my brow to be honest. It's a no-brainer to abstract these kinds of details from the user and to ensure the 2 API's (ResourceLoader and DirAccess/FileAccess) are compatible. Perhaps this could be a good "first time" issue a new contributor could implement.

Also, is there a list of such suffixes (.import, .remap, etc.) we can use as reference to make sure our projects work after export?

@zane222
Copy link

zane222 commented Dec 5, 2024

I had this exact same issue. I fixed the issue completely by turning off Convert Text Resources To Binary in Project Settings > Editor: Export (this setting was turned on by default for me)

@jonOsm
Copy link

jonOsm commented Dec 25, 2024

I just ran into this issue attempting to dynamically load level resources. I didn't see any mention of this in the docs but i might have missed it.

Very new to Godot btw. Overall great experience so far but this was a bit of a head stratcher.

LettucePie added a commit to LettucePie/rpg-life that referenced this issue Dec 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

No branches or pull requests