• Godot Help2D
  • Strange error : Invalid call. Nonexistent function ... in base 'String'.

Hello everyone,

Sorry for the poor post title but I had no ideas 🙂
I am facing a weird error that I really cannot figure out.
Im not sure what I have changed that created the error but this system use to work fine and now throw an error :

Invalid call. Nonexistent function 'ReturnUpdatedCharacter' in base 'String'.

This is the line of code that throw the error :
main_interface.ReturnUpdatedCharacter(playerId, nfts[nftId], role, "success", message)

main_interface is defined as so :
onready var main_interface = get_node("/root/GameServer")

In the screenshot, I think you can see all relevant data :
main_interface get the good node ( "gameserver:1281")

you can see that that node has the server.gd script.

and you can see that the function exist in the server.gd:
func ReturnUpdatedCharacter(player_id, characterSheet, role, code, message)

I am very confused as why it think that the function is from a base string while its trying to launch it from the gameServer node (which is a simple node by the way)

If anyone sees something that i missed, i'll ge very grateful 🙂
Thanks

And of course feel free to ask for any more details.

  • yeeaahh.

    I finally found it.
    The problem was caused by the way I save the data on that singleton :

    func loadNftData():
    	var data = {}
    	var filetemp = File.new()
    	if filetemp.file_exists("user://nftdata.json"):
    		filetemp.open("user://nftdata.json", File.READ)
    		var text = filetemp.get_as_text()
    		data = parse_json(text)
    		filetemp.close()	
    		for key in data:
    			self[key]=data[key]

    Originaly that singleton was only to store data so this worked fined.
    But I forgot about that and now the main_terface was being overwritten by its saved value as a string 🙂

    I just have to exclude it from the save/load function and all good 🙂

    Thanks for the help anyway and hopefully it will help someone someday 🙂

    SOLVED

So.. What exactly Server.gd defines?
A class named 'GameServer' that has a few methods like 'ReturnUpdatedCharacter'?

My guess is that 'main_interface' is NiL or some random value when you called the function 'ReturnUpdatedCharacter', you could try printing the value of 'main_interface' or check against null before called the function, and add a breakpoint if main_interface value is not intended type/value, to see where gets it corrupted or override it by accident or anything that might change the value of that varirable.

Just a guess though, w/o knowing the whole architecture behind your server/client logics.

So as you can see on the screen :

  • GameServer is a node.
  • It has attached the server.gd script which contain all sorts of client/server function.
  • The one function that for some reason won't work returns an updated character sheet to the player
  • This system is working very well for all other functions and was working fine until I apparently touched something that broke it but i really don't understand why
  • you can see on the screen that my mouse is hovering above the main_interface variable and it does get the good one. the gameServer:1281 which is definitly not a string...

I am extremelly confused about that error to be honnest (today i'll try to reverse the lasts changes i applied to the best of my abilities and try to revert to a functionnal stage
I will keep this post updated if I find the solution

and when i say its a node. i mean it, its not a node2d or node3d or whatever, its a basic node 🙂

    Is it possible that you're setting main_interface to a String value?
    That's the advantage of static typing. It results in an error message if you try to change the type of a variable.

    hjiul and when i say its a node. i mean it, its not a node2d or node3d or whatever, its a basic node

    Using "Node", rather than "node", helps to clarify that.

    Make sens to say Node instead of node. Didnt thought of that 🙂

    so the main_interface is declared like so :

    #onready var main_interface = get_tree().root.get_node("GameServer")
    onready var main_interface = get_node("/root/GameServer")

    both way leads to the same results

    this is the entire function that crashes if you see where i made a mess. It all happend after i added the message to the returned variables but i dont see why. The message is built from a concatenation, but it prints correctly. I tried to pass a string instead of the message var but same error happens.

    func updateSheet(nftId,nftData,typeUpdate,playerId,role):
    	
    	# check that nft exists :
    	if not nfts.keys().has(nftId) :
    		print("trying to update non existant nft : ",nftId)
    		main_interface.ReturnUpdatedCharacter(playerId, {}, "", "Nft not found", "That nft wasn't found in the server database, please contact an admin")
    	else:
    		if typeUpdate == "stat":
    			#check that the modified stats are legal
    			var currentSheet = nfts[nftId]
    			var currentTotalPoints = currentSheet.statPoints
    			for stat in currentSheet.stats:
    				currentTotalPoints+=currentSheet.stats[stat]
    			
    			var checkNewTotalPoints = nftData.statPoints
    			for stat in nftData.stats:
    				checkNewTotalPoints+=nftData.stats[stat]
    			
    			if not checkNewTotalPoints == currentTotalPoints:
    				print("Illegal stat points : ",checkNewTotalPoints," - ",currentTotalPoints, " for nft : ",nftId)
    				main_interface.ReturnUpdatedCharacter(playerId, {}, "", "Illegal stat points", "Something seems to be wrong with your total points spent, please contact an admin")
    			else:
    				nfts[nftId].stats = nftData.stats
    				nfts[nftId].statPoints = nftData.statPoints
    				saveNftData()
    				refreshPlayerTeamSheets(playerId, role, nfts[nftId])
    				var message = role + " stats updated correctly."
    				print("message is : ",message)
    				main_interface.ReturnUpdatedCharacter(playerId, nfts[nftId], role, "success", message)
    		elif typeUpdate == "lvlup":
    			var expOverload = nftData.exp[0]-nftData.exp[1]
    			if expOverload < 0:
    				expOverload=0
    			nfts[nftId].lvl +=1
    			var expToNextLevel = int((int(nftData.Lvl)*1.15)*10000)
    			nfts[nftId].statPoints +=3
    			nfts[nftId].exp = [expOverload,expToNextLevel]
    			saveNftData()
    			refreshPlayerTeamSheets(playerId, role, nfts[nftId])
    			var message = "Your " + role + " has gained a level"
    			main_interface.ReturnUpdatedCharacter(playerId, nfts[nftId], role, "success", message)
    		elif typeUpdate == "custom":
    			nfts[nftId].custom = nftData.custom
    			saveNftData()
    			refreshPlayerTeamSheets(playerId, role, nfts[nftId])
    			var message = role + " appearence updated correctly."
    			print("message is : ",message)
    			main_interface.ReturnUpdatedCharacter(playerId, nfts[nftId], role, "success", message)

    All my client-server communication follow that same model and all works fine except this one. (and it was working fine until I added the message)

    this is the function called on the main interface :

    func ReturnUpdatedCharacter(player_id, characterSheet, role, code, message):
    	print("boubibop : ",message)
    	rpc_id(player_id,"ReturnUpdatedCharacter",characterSheet, role, code, message)

    And the function receiving on client side :

    remote func ReturnUpdatedCharacter(characterSheet, role, code ,message):
    	print(code," : ",message," : ",role," : ",characterSheet)	
    	if code == "success":
    		PlayerDatabase.teamSheets[role] = characterSheet
    	if globalFunc.loading == true:		
    		globalFunc.loading = false
    	globalFunc.setMessageScreenContent({
    		"title":code,
    		"message":message
    	})

    the part that confuse me is that is say that the function do not exsit on a base string. Meaning that for the code main_interface is somehow a string.
    But if i hover the mouse on the main_terface, we can see that it actually refer to the correct node ( gameserver:1281 )

    Which is why i came here, it really don't make any sens to me 🙁

    yeeaahh.

    I finally found it.
    The problem was caused by the way I save the data on that singleton :

    func loadNftData():
    	var data = {}
    	var filetemp = File.new()
    	if filetemp.file_exists("user://nftdata.json"):
    		filetemp.open("user://nftdata.json", File.READ)
    		var text = filetemp.get_as_text()
    		data = parse_json(text)
    		filetemp.close()	
    		for key in data:
    			self[key]=data[key]

    Originaly that singleton was only to store data so this worked fined.
    But I forgot about that and now the main_terface was being overwritten by its saved value as a string 🙂

    I just have to exclude it from the save/load function and all good 🙂

    Thanks for the help anyway and hopefully it will help someone someday 🙂

    SOLVED