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
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.
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.
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
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.
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()