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

Invalid call. Nonexistent function 'build_def_text' in base 'Resource (QodotFGDSolidClass)' #42

Closed
VincentHill opened this issue Jun 1, 2023 · 11 comments
Labels
bug Something isn't working

Comments

@VincentHill
Copy link

When I try to export the FGD or create the Game Folder I get this error:
res://addons/qodot/src/resources/game-definitions/fgd/qodot_fgd_file.gd:52 - Invalid call. Nonexistent function 'build_def_text' in base 'Resource (QodotFGDSolidClass)'.
I tried a new project with just the addon and it still gives me the error. here is that project.
QodotTest.zip

I using linux and my Godot is version:
v4.0.stable.mono.official [92bee43ad]

@Paragrimm
Copy link

I can confirm this. This is the same error I'm getting.

@mode777
Copy link
Contributor

mode777 commented Jun 21, 2023

I'm getting the same error the latest version of Godot 4 (v4.0.3.stable.mono.official [5222a99f5])

So I decided to poke around a bit and found some really peculiar behaviour. So the method in question is implemented in QodotFGDClass and our entity is also of this type, so it should have the method.

I added some code to debug and the output is really peculiar

func build_class_text() -> String:
	var res : String = ""

	for base_fgd in base_fgd_files:
		res += base_fgd.build_class_text()

	var entities = get_fgd_classes()
	for ent in entities:
		if ent.qodot_internal:
			continue
		if(ent is QodotFGDClass):
			for m in ent.get_method_list(): # Get all methods of current entity
				if(m.name == "build_def_text"):
					print(m) # If it prints, it has the method "build_def_text"...
			
			print("Has method: " + str(ent.has_method("build_def_text"))) # ...and this should be true
		else:
			print("ent is not fgdclass")
					
		var ent_text = ent.build_def_text() #Error occurs here
		res += ent_text
		if ent != entities[-1]:
			res += "\n"
	return res

Suprisingly this has the following output.

Exporting FGD to C:/Users/z002f2au/Downloads/TrenchBroom-Win64-v2023.1-Release/games/Qodot/Qodot.fgd
{ "name": "build_def_text", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 4, "hint": 0, "hint_string": "", "usage": 6 } }
Has method: false
  res://addons/qodot/src/resources/game-definitions/fgd/qodot_fgd_file.gd:62 - Invalid call. Nonexistent function 'build_def_text' in base 'Resource (QodotFGDSolidClass)'.

So it does have the method but has_method returns false? Might this be a gdscript bug?

@GrauBlitz
Copy link

GrauBlitz commented Jun 25, 2023

Can confirm on v4.1.beta3.mono.offical

@mode777
Copy link
Contributor

mode777 commented Jul 2, 2023

I added @tool on top of qodot_fgd_class.gd and now it works after restarting the editor /re-enabling the plugin. I'm not a 100% sure if this was the cause. Maybe someone can confirm. At least I can say that, if this error occurs the _init function of qodot_fgd_solid_class is not called. If it doesn't occur, it is called. Maybe this helps.

EDIT: There is one more change needed. The changes above would still not call the constructor thus not setting the prefix correctly. I needed to call ´ent._init()´ manually before calling build_def_text this works but it is of course quite ugly.

@GelosoFederico
Copy link
Contributor

I added @tool on top of qodot_fgd_class.gd and now it works after restarting the editor /re-enabling the plugin. I'm not a 100% sure if this was the cause. Maybe someone can confirm. At least I can say that, if this error occurs the _init function of qodot_fgd_solid_class is not called. If it doesn't occur, it is called. Maybe this helps.

EDIT: There is one more change needed. The changes above would still not call the constructor thus not setting the prefix correctly. I needed to call ´ent._init()´ manually before calling build_def_text this works but it is of course quite ugly.

I might be doing something wrong, but I added only the @tool command and it didn't work with that, then I added the init for each ent, it worked, then reverted back and it continued to work... for some reason. I don't know if the init is doing something to the resource itself.

I'm on Godot 4.0.3.

@mode777
Copy link
Contributor

mode777 commented Jul 3, 2023

Same here, I tried with a differen machine (Windows) and same version and got the error again (now complaining _init is not available because I added it). It seems like some kind of race condition or missing initialization. My gut tells me we are doing some things with resources we aren't supposed to be doing but I know not enough abaout Godot nor gdscript. Anyway it's really anoying as I cannot export fgds for my custom entities.

@DeerTears
Copy link
Member

@mode777 @GelosoFederico @GrauBlitz @Paragrimm @VincentHill I pushed a fix for this on the latest master branch. Try exporting a config after downloading the current state of the repo as a .zip and putting it in your project.

Backup your project before you try updating Qodot, I moved some entity definitions out of Qodot_fgd (you can add .tres files back into the array yourself) but barebones configs should at least build. Once I hear back that it's finally building configs for people, I'll restore the rest of the entity definitions.

@mode777
Copy link
Contributor

mode777 commented Jul 5, 2023

Just tested it and it worked fine. Also added the missing definitions and my custom ones back in - still working. Due to the flaky behaviour of this bug I think we should wait for more confirmations. I'll test it on my Mac as well and report back.

EDIT: Tested it on my mac and it worked like a charm. May I ask what the main cause for this problem was? The behaviour seemed very strange.

@laurheth
Copy link

Tried the most recent version on my end (Windows, new project). Seems to be fixed for me as well.

@DeerTears
Copy link
Member

DeerTears commented Jul 11, 2023

@mode777

Just tested it and it worked fine. Also added the missing definitions and my custom ones back in - still working. Due to the flaky behaviour of this bug I think we should wait for more confirmations. I'll test it on my Mac as well and report back.

EDIT: Tested it on my mac and it worked like a charm. May I ask what the main cause for this problem was? The behaviour seemed very strange.

Somewhere Godot changed how its .tres files link subresources like scripts, materials, and meshes. Before it was just an index of [id=1] and [id=2], which was limited to all subresources within the resource.

Old Qodot_fgd.tres:

[ext_resource path="res://addons/qodot/game_definitions/fgd/base_classes/target_base_class.tres" type="Resource" id=1]
[ext_resource path="res://addons/qodot/game_definitions/fgd/solid_classes/func_group_solid_class.tres" type="Resource" id=2]
[ext_resource path="res://addons/qodot/game_definitions/fgd/point_classes/signal_point_class.tres" type="Resource" id=3]
[ext_resource path="res://addons/qodot/game_definitions/fgd/base_classes/targetname_base_class.tres" type="Resource" id=4]
[ext_resource path="res://addons/qodot/game_definitions/fgd/solid_classes/trigger_solid_class.tres" type="Resource" id=5]

New Qodot_fgd.tres:

[ext_resource type="Resource" path="res://addons/qodot/game_definitions/fgd/base_classes/target_base_class.tres" id="1"]
[ext_resource type="Resource" path="res://addons/qodot/game_definitions/fgd/solid_classes/func_group_solid_class.tres" id="2"]
[ext_resource type="Resource" path="res://addons/qodot/game_definitions/fgd/point_classes/signal_point_class.tres" id="3"]
[ext_resource type="Resource" uid="uid://ccdjpf5j25aua" path="res://addons/qodot/game_definitions/fgd/base_classes/targetname_base_class.tres" id="4"]
[ext_resource type="Resource" path="res://addons/qodot/game_definitions/fgd/solid_classes/trigger_solid_class.tres" id="5"]

Note at the end of each line, there's now id="1"] instead of id=1]. It's such a tiny difference but those quotation mark are literally the only thing that has been modified by loading Qodot .tres files in the new version of the engine.

But if I make a brand new fgd .tres file, this is what it starts with:

[ext_resource type="Resource" uid="uid://c36hv0o6g0udo" path="res://addons/qodot/game_definitions/fgd/qodot_fgd.tres" id="1_oc0dk"]
[ext_resource type="Script" path="res://addons/qodot/src/resources/game-definitions/fgd/qodot_fgd_file.gd" id="1_svwqg"]

Woah, id="1_oc0dk"? id="1_svwgg"? These days, when you generate a .tres file, it generates a type of string hash, but old .tres files are migrated to string equivalents of their indices: [id="1"] and [id="2"]. My hunch is that these strings are actually namespaces now, instead of just local indices within the resource file. I have no evidence this is the case, but it's fishy that these resources fail to load (the script isn't attached, so the function cannot be called) when these old id values are used.

So my best guess is that some subresources get swapped or overwritten by having conflicting names with one another. Keep in mind, all migrated resources were using id=1 and so on, so they all converted to id="1".

The error happens because the script fails to load as a ext_resource of a resource. I'm not sure if it has to be embedded in another resource's array like we do in Qodot. But because the script doesn't load, it cannot invoke class methods and properties, from my testing it couldn't prove to own any properties or methods of the script. But for some reason it could still tell me its class name correctly. (I may be forgetting this detail). But magically, if all the ext_resource IDs are unique, everything is fine.

So yeah! This actually could be reported to Godot as a migration issue with resources, but I'm too tired of dealing with this bug to make a minimum reproduction 😅 I'm only assuming you could reproduce it reliably by:

  1. opening Godot 3.x
  2. writing a foo() method in a script on a resource that prints to console or something
  3. making a new resource, and embedding the original resource in an exported array
  4. writing a function to accessing that array in code, and calling the foo() method on the original resource in the array
  5. migrating the resources and scripts to Godot 4.x (modifying the script to be 4.x compatible does not change the ID values)
  6. calling the new function, causing a similar Invalid call. error

I'm assuming they actually tested this and wouldn't let migrated resources die like this. That's why my hunch is when it's embedded multiple levels deep, there's some overlap of id values somewhere, or just something else going wrong that causes the migrated resource script to remove itself.

@DeerTears
Copy link
Member

DeerTears commented Jul 11, 2023

This is definitely more detail than you needed, but it's late and I am happy to hear that it's fixed for people 😊 thanks for testing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Development

No branches or pull requests

7 participants