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

"Node not found: MultiplayerSynchronizer" when node doesn't exist on all clients #76894

Open
riazey opened this issue May 10, 2023 · 18 comments
Open

Comments

@riazey
Copy link

riazey commented May 10, 2023

Godot version

4.0.2

System information

Windows 10

Issue description

For nodes with a MultiplayerSynchronizer child:

  • Error when joining a server but not instancing the node on the client, or when instancing the node only on one client
  • Error when deleting on only one client (toggle on/off on MRP's GUI script)
  • Goes away if you uncheck public_visibility

Originally I had planned to only instance scenes on a client if they were in the same "area" but maybe I am barking up the wrong tree~? >0<

image

Steps to reproduce

  • Start a server
  • Add a node to the server with a MultiplayerSynchronizer
  • Connect a client (but don't spawn the node)
  • Any client spawned after will have an error for every missing node

OR

  • Start a server
  • Connect Client
  • Add a node on the client side only

OR

  • Start a server + connect a client that share a node with a MultiSync on it
  • Delete the node on one client

Minimal reproduction project

Note: GUI script has a bool/toggle for deleting a node with a MultiSyncy on it after joining

Multiplayer_Test.zip

@Calinou
Copy link
Member

Calinou commented May 10, 2023

cc @Faless

@LivelyCarpet87
Copy link

LivelyCarpet87 commented Aug 20, 2023

I'm experiencing the same issue when attempting to delete or remove a node (which contains a MultiSync with local as authority, spawned in on local via MultiSpawner) on the server side, with one error message per node deleted. The error messages do not occur on clients connected to the server.

@benjamin-kirkbride
Copy link

Experiencing a similar issue. Any progress on this?

@Faless
Copy link
Collaborator

Faless commented Sep 18, 2023

For nodes that gets dynamically added/removed from tree, you want to use a MultiplayerSpawner to ensure proper replication across clients. See this article for example.

You can use the visibility API (public_visibility/set_visibility_for/is_visible_to/add_visibility_filter) to control which player will see and receive updates for a specific node/synchronizer.

@PPillau
Copy link

PPillau commented Sep 19, 2023

Got the same problem here. I think I set up everything correctly:

  1. Got the MultiplayerSpawner in my level, with spawn path to players-container and auto spawn list element 0 to my player.tscn.
  2. In my player.tscn got a MultiplayerSynchronizer that synchronizes all my properties with correct visiblity

Getting the same get_node: Node not found [...]Player/1/MultiplayerSynchronizer error.

After some debugging I found out what happens in my project: The host correctly gets spawned in (Player 1) on his side.
Then when the first client joins (Player 2), on the host-side the client player gets correctly spawned (host now has: Player 1 & Player 2 nodes).
But on the client side, only the client Player get spawned in, but not the host player (so client now only has Player 2 node), which means the client throws an error that Player 1 (the Player 1 node or more precisely it's MultiplayerSynchronizer) is missing...

Am I doing something wrong here? I think I followed the tutorial and setup correctly...
I can also share my project over a call if somebody wanna take a look with me

@igamigo
Copy link

igamigo commented Sep 20, 2023

What you are all likely missing is configuring your MultiplayerSpawner's Auto Spawn List to include your player scene, so that when a client joins, the host's player scene is spawned automatically and its MultiplayerSynchronizer node is in your node tree.

@olivatooo
Copy link

I'm having the same problem, but I'm using RPC calls to spawn a node in both client and server. Idk if it's relevant but I'm adding a scene into MultiplayerSpawner and instantiating an inherithed scene

@cidwel
Copy link

cidwel commented Sep 24, 2023

For nodes that gets dynamically added/removed from tree, you want to use a MultiplayerSpawner to ensure proper replication across clients. See this article for example.

Maybe it's not the ideal place to ask, but that article is adding the authority in getset. This seems to be forbidden in latest dev5. The tutorial adds auth like this:

# Set by the authority, synchronized on spawn.
@export var player := 1 :
	set(id):
		player = id
		# Give authority over the player input to the appropriate peer.
		$PlayerInput.set_multiplayer_authority(id)

I tried to do the same in c#

public int _clientId;

	public int clientId {
		get { return _clientId; }
		set {
			_clientId = value;
			GD.Print("SETTER > setmultiplayerauthority");
			SetMultiplayerAuthority(value);
		}
	}

I set clientId just before adding it in the tree, and this is what I get in console.

"/root/Game/ServerStore/Beach/Players/Player2/MultiplayerSynchronizer" is unable to process the pending spawn since it has no network ID. This might happen when changing the multiplayer authority during the "_ready" callback. Make sure to only change the authority of multiplayer synchronizers during "_enter_tree" or the "_spawn_custom" callback of their multiplayer spawner.
  <C++ Error>    Condition "pending_sync_net_ids.is_empty()" is true. Returning: ERR_INVALID_DATA
  <C++ Source>   modules/multiplayer/scene_replication_interface.cpp:243 @ on_replication_start()

Shall I create a github issue for this?

What you are all likely missing is configuring your MultiplayerSpawner's Auto Spawn List to include your player scene, so that when a client joins, the host's player scene is spawned automatically and its MultiplayerSynchronizer node is in your node tree.

Not in my case. The spawner is populated with the player scene.

My strategy is:

  • Server instantiates a map (scene), adds it to the tree
  • Server instantiates a player. Calls addChild for the new players inside the "players" node in that map.

Both map and player are inside the Multiplayerspawner

The server:

  • creates a map (map is also in spawner list)
  • Creates a player and adds it inside a node in the map

The client:

  • As soon as I connect to the client there's some kind of sync between the server, trying to communicate with the client. Then I get this:
    image

This is the remote window from client:
image

And this is remote tree from server:
image

It gets fixed if you disable the "Public Visibility" of the NetworkSynchroniser located inside each player, which will make the props not to sync.

My guess is that is accurate that the "Map" is not loaded when the sync starts. So I might need to set Public visibility to off and find a way to tell players that only the players inside that map should be able to sync variables (if I understand it correctly)

I will try to set the visibility manually only when the scene is completely loaded as this comment recommends and see if that helps

@olivatooo
Copy link

After tinkering a little bit I arrived into the following rule of thumb:

  • Add every scene that needs sync into multiplayer spawner ( that's the trickiest part, inherited scenes you need to manually/programmatically add all children)
  • If the server spawns a scene, ok, only spawn scenes in the server
  • Clients must make RPC calls to spawn the scene in the server, never spawn scenes in client

This apparently works well! But I guess for more performative and non prototype projects you should use low level network

But this looks much more like a Server-Client model than a P2P where every player is responsible for its own scenes... Maybe I'm still missing something

But my rule solves this issue problem

Basically you want something similar to this:

@rpc("any_peer", "call_local", "reliable")
func add_node(nodePath: String):
	if not multiplayer.is_server():
		return
	# Always send the nodePath
	var newNode = load(nodePath).instantiate()
	# Here is the MultiplayerSpawner Spawn Path
	# Don't forget this "true" in add child
	get_node("/root/Game/Level").add_child(newNode, true)


@rpc("any_peer", "call_local", "reliable")
func remove_node(nodePath: String):
	if not multiplayer.is_server():
		return
	# Here you MUST receive a node path!
	var e = get_node(nodePath)
	if e:
		e.queue_free()

When calling from server or client you can use

remove_node.rpc("res://your_node")
add_node.rpc("res://your_node")

# or 

remove_node.rpc(yourNode.get_path())
add_node.rpc(yourNode.get_path())

@nicholasrobertm
Copy link

Just as another note in case someone ends up here, the issue I ran into to cause something like this was accidentally calling add_child on the wrong node. E.g. if you have your players in a node in your scene called 'players' make sure when you add them to the scene you're not adding them somewhere other than what is listed as the spawn path in the MultliplayerSpawner. An obvious issue but easy to miss if you're new to godot's networking

@balassanne
Copy link

Either if I use RPCs or a predefined spawner, and am not missing the parent node, I can confirm this error. Adding a multiplayer synchronizer to dynamically spawned objects will cause this error on despawn. Everything works as intended, but why this error set is shown in client in each despawn? Something is really of here, and I feel that there is some unexpected thing happening.
I kind of confirm this problem too... but am also new to godot....

@sebastienwood
Copy link

I had the same issue while trying to fiddle around the project mentionned in the above comments (https://github.com/godotengine/tps-demo)

Succinctly, I tried to split the UI node from the menu scene in a loading, setting and login scene. Login hold the loading scene, hidden by default. The loading scene is generalist: it holds functions to load any resource and update itself with a loading bar while waiting for the ResourceLoader, and finally emit a signal when done. Its code is as follow:


var path = "res://scenes/world_screen/world_screen.tscn"

signal loaded(path: String)

@onready var loading_progress = %Progress
@onready var loading_done_timer = %DoneTimer

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
	if visible:
		var progress = []
		var status = ResourceLoader.load_threaded_get_status(path, progress)
		if status == ResourceLoader.THREAD_LOAD_IN_PROGRESS:
			loading_progress.value = progress[0] * 100
		elif status == ResourceLoader.THREAD_LOAD_LOADED:
			loading_progress.value = 100
			set_process(false)
			loading_done_timer.start()
		else:
			print("Error while loading level: " + str(status))
			hide()


func _load(new_path: String, cb: Callable, sub_thread: bool = true):
	show()
	path = new_path
	loaded.connect(cb)
	if ResourceLoader.has_cached(path):
		loaded.emit()
	else:
		ResourceLoader.load_threaded_request(path, "", sub_thread)


func _on_done_timer_timeout():
	loaded.emit(path)

Running it however throws the following set of errors:
image

This error do not happen if I do not use the loading scene and directly perform the load_threaded_request in the login scene.

@olivatooo
Copy link

There is a comprehensive way to do it? I never found a correct way of using godot multiplayer

@Faless
Copy link
Collaborator

Faless commented Feb 15, 2024

There is a comprehensive way to do it? I never found a correct way of using godot multiplayer

@olivatooo There is a comprehensive guide on how to spawn level and players here: https://godotengine.org/article/multiplayer-in-godot-4-0-scene-replication/

@banane42 I can't really tell what's happening, make sure you are not removing the level yourself, or try to make an MRP and open a new issue.

@des1redState

This comment was marked as duplicate.

@banane42
Copy link

banane42 commented Feb 17, 2024

@Faless

@banane42 I can't really tell what's happening, make sure you are not removing the level yourself, or try to make an MRP and open a new issue.

Removed my earlier comment as I discovered I'm an idiot and was removing the level on the connecting client via a signal.

@BatteryAcid
Copy link

I ran into the original issue, where a spawned player's MultiplayerSynchronizer was not found when other clients connected. My setup, really for experimenting, was to build a dedicated server, have the server auto-spawn a dummy player, so when clients join they can see them.

However, I was adding the player object (to be spawned) during the server's game_manager ready function, which led to this error because the scene was not ready to add a player. At least that's what I think is happening, because once I added a 3 second timer after the ready was hit, the dummy player was added to the scene as expected, and all clients had it spawned properly.

Maybe there is a bug as this is a slightly different use case, but it's likely something that's expected to be there, some node, isn't.

@Kairu1206
Copy link

I have the same issue, but the only twist is that it works where I am on the same computer, like running multiple instances of the game on the same machine. But when I open the game on two or more computers, the server computer will run into this issue. I don't know what happen at this point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests