Online Multiplayer - Error when queue_free() player

Godot Version

4.2.1

Question

I’m making an online multiplayer game and keep getting an error when calling queue_free() on a player node - a client (not the server) is the authority.

I’m testing this for Steam multiplayer so am running Godot debug both on my host PC and also on a virtual machine on the same PC. I’m only getting these errors on the client. I’ve tried to resolve in the following 2 ways:

1)AUTHORITY QUEUE_FREE()
I tried this code to only let the authority, which is the client in this case, queue_free() the node:

#kill player:
	if Globals.player_lives1 < 0:
		if !is_multiplayer_authority():
			print('is not authority')
			return
		if is_multiplayer_authority():
			print('authority queue_freed player')
			queue_free()

Got this error:
on_despawn_receive: Condition “!pinfo.recv_nodes.has(net_id)” is true. Returning: ERR_UNAUTHORIZED

2)SERVER QUEUE_FREE()
I tried this code to only let the server, which is also not the authority of the node in this case, queue_free() the node:

#kill player:
	if Globals.player_lives1 < 0:
		if !multiplayer.is_server():
			print('is not server')
			return
		if multiplayer.is_server():
			print('server queue_freed player')
			queue_free()

Got this error:
on_despawn_receive: Condition “!pinfo.recv_nodes.has(net_id)” is true. Returning: ERR_UNAUTHORIZED

I’m able to verify with the print statements for both attempts that either the multiplayer authority or the server have queue_freed() the node successfully as intended but still get the error in both cases.

I have the player node in the auto spawn list of a MultiplayerSpawner in my level scene. I also have a MultiplayerSynchronizer in the player scene. And I do not get this issue when the player node controlled by the server queue_free()'s.

Is there a resolution for this? Or perhaps I should just overlook this error since it doesn’t cause the game to crash?

If anyone can share how they effectively queue_free() nodes in online multiplayer I would greatly appreciate the help

The peer that has authority over a MultiplayerSpawner (default is the host) should be the only one allowed to queue free.

So on clients you should check if they are the the authority, or server, before freeing.

Thanks for the reply! Sorry, accidentally posted the topic and then had to go back and edit my initial post. What are your thoughts?

Does the player node have child spawners?

And you are doing the correct thing, not sure what the issue is. You definitely don’t want the client to free a node that it isn’t authed to. This would be considered desync and could create invisible players for the client.

That was a great thought, thank you for that

The client player scene does have a MultiplayerSpawner node on it used for spawning powerups for the player

So I removed that MultiplayerSpawner on the player scene to see if it would resolve the error but unfortunately the error persists

I also wanted to ask about how you authorize nodes. And just so you are aware, setting authority is recursive unless you pass a second parameter to not do that.

So if you set the root node authority of the player, all nodes underneath get authed too. So if a spawner is authed to a client, I wonder if there is some issue going on there, but you did say you tried removing it, and it did not change anything.

Also a good question!

The authority is set via an autoload script for each player spawned with peer_id being the unique multiplayer id:

Specifically it’s set with this line in an autoload script when the level scene is loaded:

player.set_authority.rpc(peer_id)

Here’s the function on the player script:

@rpc("any_peer", "call_local")
func set_authority(id : int) -> void:
	set_multiplayer_authority(id)

I’m at a bit of a loss right now because the issue persists after I just:
1)verified the authorities with print statements and they appear to be set correctly.
2)tried running the project on 2 separate PC’s to see if the issue was that I was using a debugger on a host PC and a virtual client on the same PC.
3)tried upgrading the project to Godot 4.3

I’m going to share the following autoload code if you have time to take a look. Sorry for the long post!

this loads the level scene:

@rpc("call_local") 
func load_world():
	# Change scene.
	var world = load("res://scenes/levels/2.tscn").instantiate()
	get_tree().get_root().add_child(world)
	
	#hide lobby
	get_tree().get_root().get_node("Lobby").hide()

	get_tree().set_pause(false) 

this adds the players to the level scene and starts the game

func begin_game():
	#this is meant to be called on the server only
	assert(multiplayer.is_server())
	
	#call load_world on all clients
	load_world.rpc()
	
	#grab the world node and player scene
	var world : Node2D = get_tree().get_root().get_node("2") #this has to match scene name instantiated
	
	var player_scene_array = [
		load("res://scenes/player0.tscn"),
		load("res://scenes/player1.tscn"),
		load("res://scenes/player2.tscn"),
		load("res://scenes/player3.tscn")	
	]
	
	
	var spawn_index := 0
	
	for peer_id in players:
		var player : CharacterBody2D = player_scene_array[spawn_index].instantiate()
		player.set_player_name(players[peer_id])
		world.get_node("Players").add_child(player, true)
		
		#Set the authorization for the player. This has to be called on all peers to stay in sync.
		player.set_authority.rpc(peer_id)
		
		#Grab our location for the player.
		var target : Vector2 = world.get_node("Players").get_child(spawn_index).position
		
		#The peer has authority over the player's position so
		#we need to set that position from that peer with an RPC.
		player.teleport.rpc_id(peer_id, target)
		
		spawn_index += 1
			
		if spawn_index > len(players):
			return

Or if it’s easier, would you happen to have a recommendation for a specific tutorial or sample project for integrating online multiplayer?

I don’t, reviewing the topic again.

If the server has a player spawner, only the server should queue free the player. If the player has a child spawner, and the chold spawner is authed to the client (since set authority is recursive by a default parameter), the client should only queue free the children.

So if the above scenario describes your case, you need to first, on the client, queue free spawnable children before the server queue frees the player.

My recommendation is to either give all spawners to the server or detangle the concerns some other way.

When it comes to spawning it’s less about the authority of the spawnable scenes, but more the authority of the spawners themselves.

Thank you! I’ll continue to work on this and will post a solution when I figure out what I’m speficially doing wrong here

Okay here’s the best way I found to remove a node that is auth’d to the client without any debug errors. See code below.

Globals.player_lives1 is synced with a MultiplayerSynchronizer in the client player scene.

when the client player dies:
1)first the client toggles off the public_visibility on the MultiplayerSynchronizer in the client player scene- this fixes the common “node not found” error
2)then the server queue_free()'s the client player scene - this avoids any despawn errors


@onready var multiplayer_sync = $MultiplayerSynchronizer

func _process(delta)

	if Globals.player_lives1 < 0:
	
		#client has to toggle off multiplayer synchronizer to avoid debug error
		if owner_id == multiplayer.get_unique_id():
			multiplayer_sync.public_visibility = false
				
		#then server has to queue_free() to avoid debug error
		if !multiplayer.is_server():
			return
		queue_free()
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.