MultiplayerSpawner 'Node not found' Error

Godot Version

4.3-stable_win64

Question

I’m trying to implement reconnection and joining mid game. Starting the game works just fine, no errors. However, once I disconnect and connect back again, it throws the get_node: Node not found error for the second nested MultiplayerSpawner. Looking at the remote scene trees, they both have the exact same nodes, and the spawner is on the path.
image
image
image

When the client disconnects do you delete synced nodes like arena and players? So they can be spawned again on connection?

Yeah, I do.
image
I can see the level being unloaded in the tree as well.
There’s also this function which I use right now, calling it from a disconnect button.
image

Do you call disconnect_peer(TARGET_PEER_SERVER)?

I saw it mentioned here

No, but the function gets called anyways, and the player disconnects properly. That seems like a weird thing to have to do and it’s not mentioned in any of the official articles.
I should also mention that I tried this on a new project, with minimal code and it worked as expected. I really have no idea what’s happening in this project that’s causing it to act weird.

It could be a race condition. I wonder if the multiplayer API (defaults to SceneMultiplayer) has some buffer that wants to try and despawn something during the disconnect. But the client already deleted the spawner and throws an error. You could test this by setting the packet_peer to null and defer the rest of the disconnect on a different frame.

The disconnect button doesn’t immediately reconnect. There’s no difference even if I wait for the nodes to be despawned.

That error to me seems like some implicit or explicit RPC action taking place from the authority spawner, on the client.

This is just speculation to the root of the problem but the multiplayer API has some cached information in its replication data. It could be necessary to also wipe the multiplayer API as well?

I think it’s the spawn path. It doesn’t have enough time to initialize the spawn path node in the tree, the spawner, and then the nested spawners which are children of the spawn path.

It seems we’ve gone full circle, bud.

The related forum discussion leads to this forum, which you’ve also replied to.

I have a pretty complicated multiplayer setup, that is join whenever, with nested spawners. When I disconnect I close the whole process of the client ( this is prototype phase ). So everything is freed at that point. And when I join with another process it connects fine.

The error comes from a peer calling get_node, via a spawn RPC, with a path that doesn’t exist. To me, since the client peer disconnecting, and reconnecting something isn’t cleared.

Try this when you connect each time.

# suedo code
var m_api := SceneMultiplayer.new()
var enet := EnetMultiplayerPeer.new()
# create enet client 
m_api.multiplayer_peer = enet
get_tree().set_multiplayer(m_api)

This should hopefully clear all internal state on a peer by resetting the multiplayer API object, as this API holds all the replication configs for spawners and syncers.

1 Like

That’s not changing anything unfortunately. It’d also be very incovenient to set the multiplayer of the tree each time instead of the node, which is how it’s supposed to work anyways.

1 Like

I thought I had mentioned this but, the same problem occurs if I join with another client mid game. For some reason the nested MultiplayerSpawner can’t be found by whichever client joins while the Arena level is loaded.

Okay, further testing reveals that it only happens after a player disconnects.
I have no extra code that does anything when a player disconnects, and my disconnection code is standard as I’ve shown. So I have no idea what’s happening here…

Here are the other errors that might be related


It always says ID 4 not found, it doesn’t matter what the ID of the client was or is.

I think that ID is short hand for some spawnable node in the scene. i.e. its not related to player id but more like a spawned scene id.

I’m looking around the source code and i’m troubled by some of the magic values in the code base.

There is a //TODO in the code that looks problematic.

// TODO: Find a way to cleanup recv_nodes without breaking visibility and RPCs interactions.

and recv_nodes is very relevant to these errors.

Anyway I’m trying to replicate the issue locally, and there is some behavior i want to mention.

  1. when the packet peer is set to null, all MultiplayerSpawners nodes are despawned.

This would suggest that spawnable scenes don’t need to be deleted on the client side once multiplayer.multiplayer_peer = null is set. You should however do this on server side when necessary. like remove a disconnected fighter.

on this I want to ask you a few questions about what you have shared so far.

  1. Are your spawners static? or are they part of the spawnable scenes? Is the FighterSpawner saved with the spawnable level? or is it part of some other mechanism?
  2. why do you have to call level_spawner.unload_all_levels()? is this a server only function? if client can calls this what does this function do?

I’ve also been looking at the source code for the functions in the error. I don’t think it’s related to those functions, but it must be a problem elsewhere. Some improper cleanup code that would take a bit of digging to find.

To answer your questions:

  1. The LevelSpawner is static, it’s in the main scene. FighterSpawner, which is responsible for spawning the fighters for each player, is in the Arena scene which is a level spawned by LevelSpawner. I saw this setup in Godot’s official scene replication article and it seems like a great way to handle multiplayer scene switching. However, the writers clearly overlooked nested spawners, or they know something I don’t to avoid this error.
  2. The unload_all_levels function is there for the reason you mentioned above, unloading the levels once the client disconnects. It’s meant to be either for the client or the server, whoever needs to use it, it just seems like a useful function. Although since I confirmed that it doesn’t fix the error I commented it out becuase I don’t need it.
1 Like

I was able to reproduce the problem.

code
extends Node2D

const PORT = 5555
const IP_ADDR = "localhost"
enum {
	SERVER,
	CLIENT
}

var block_a = preload("res://block_a.tscn")
var block_b = preload("res://block_b.tscn")
func _ready():
	var enet = ENetMultiplayerPeer.new()
	
	if OS.has_feature("server"):
		var error = enet.create_server(PORT)
		if error != OK : return
		multiplayer.peer_connected.connect(_on_peer_connected)
		multiplayer.multiplayer_peer = enet
		create_spawnable_nodes()
		$Label.text = "Server"
	else:
		print("create client")
		enet.create_client(IP_ADDR,PORT)
		multiplayer.server_disconnected.connect(func():print("server disconnect"))
		multiplayer.multiplayer_peer = enet
		$Label.text = "Client"

func create_spawnable_nodes():
	$a.add_child(block_a.instantiate(),true)
	#await get_tree().create_timer(1.0).timeout
	$a/BlockA/b.add_child(block_b.instantiate(),true)

func _on_peer_connected(id):
	print(name, " peer connected: ", id)


func _on_disconnect_pressed() -> void:
	print("disconnecting ", $Label.text)
	multiplayer.multiplayer_peer = null
	var node_a = get_node_or_null("a/BlockA")
	var node_b = get_node_or_null("a/BlockA/b/BlockB")
	if node_a:
		node_a.queue_free()
	if node_b:
		node_b.queue_free()
	

func _on_connect_pressed() -> void:
	var enet = ENetMultiplayerPeer.new()
	if OS.has_feature("server"):
		var error = enet.create_server(PORT)
		if error != OK : return
		multiplayer.multiplayer_peer = enet
		create_spawnable_nodes()
		
	else:
		print("create client")
		enet.create_client(IP_ADDR,PORT)
		multiplayer.multiplayer_peer = enet

image
image

I was diffing the 4.2.2 with 4.3 and there are some changes to the node caching in the scene cache interface. This could be the bug you pointed out before. as I’m pretty sure this worked before.

Ah, my problem turned out to be a user error, but the issue is eerily similar. I basically forgot to set a spawn list for block A so when the peer went to connect, block A didn’t spawn and then the host tried to spawn Block B. which doesn’t exist on the client.

I guess I would check your level spawner to make sure all your levels are listed in the spawn list

I also tested my multiplayer game on 4.3 and it didn’t have any issue.

Here’s an MRP, works on 4.2, 4.3, and 4.4, produces the same errors: