Setting Position on Spawn in Multiplayer causes client to spawn at (0,0,0)

Godot Version

4.2.2

Question

I have a function that adds a player and spawns it at a specified location.

func _add_player_to_lobby(player : Node):
	var _player_spawn_node = get_node("/root/Game/MultiplayerLobby/Map/Players")
	player.position = Vector3(0,5,0)
	#players_in_lobby[i]["Platform"].position + Vector3(0, 3, 4)
	_player_spawn_node.add_child(player)
	player.set_multiplayer_authority(player.name.to_int())
	player.look_at(player.position + Vector3(-1, 0, 0), Vector3.UP) 
	return

When I launch two instances, the host gets spawned at the specified location, while the client remains at 0,0,0 although the debugger states that it has the specified position.

Interestingly if I don’t use a MultiplayerSynchronizer the spawnpoint appears correctly on the Hostside while it remains faulty on the clientside so I think it has something to do with the synchronization althought I can’t be for certain.
Does anyone know a fix for this?

How is the function called for client? An RPC is not shown.

Are you sure you were looking at the right debug instance?

Is the exception that all nodes spawn at (0,5,0)?And fall to 0,0,0?

What properties are being synchronized by the MultiplayerSynchronizer?

2 Likes

I find the MultiplayerSpawner fails to set position to added children. I’d recommend using it’s .spawn() function and giving it the position as an argument.

Yes I was looking at the right debug instance. Sometimes you can see the Player glitching from its supposed position to the 0, 0, 0 position.

The MultiplayerSynchronizer synchronizes the position and rotation of the player.

But adding the player itself is not an RPC function. That indeed could be the problem. I will look into that as soon as I can. Thanks for you help!

Thanks a lot! It was because I forgot to call it via RPC!
Final code looks like this:

func _add_player(id: int):
	print("Player %s joined the game!" % id)
	var player = dummy_player.instantiate()
	player.name = str(id)
	_add_player_to_lobby(player)
	_position_player.rpc(player.name)

@rpc("call_local")
func _position_player(player_name):
	var user = get_node("/root/Game/MultiplayerLobby/Map/Players/%s" % player_name)
	user.position = Vector3(2, 3, 0)
	user.look_at(player.position + Vector3(-1, 0, 0), Vector3.UP) 

Sometimes the solution is right before your eyes…thanks a lot!

Had the same problem and this solved it, so thank you!
But what is the reason behind it? If I understand correctly using rpc we ensure that the client calls the method on the server. But why does it matter?

@attila-suranyi
Basically it boils down to the order of signals, authority and synchronization. Although I don’t know if he’s using a MultiplayerSpawner but kind of sounds like it.

Peer connects and peer_connected signal is called on the host and triggers the host spawn. (It’s been awhile so I don’t know if the peer_connected is emitted on the connecting peer, but let’s assume not)

The host creates a new node, authority is set with the set name internally. (this is a common way to set peer authority from a lot of tutorials, so I assume it is same (which could be done better)). It adds the node at a non-zero position, and the MultiplayerSpawner sees the new node and replicates it to the remote peer. Since the node is waiting for authority synchronizer of the remote peer, it waits and does not synchronize.

On the remote peer, the name of the node is kept the same as the player Id and, when it’s added to the peer’s tree, it too will set it’s authority to the peer id.

BUT the spawner does not replicate anything else beyond the name of the node, and the node will be in a vanilla state. I.E. it will be at position zero, AND when the authority is set the MultiplayerSynchronizer begins sending back the vanilla values to the other peers (including the host), setting the node back to zero position.

By making the RPC it bypasses authority and sets the node position after it has spawned. This could lead to one or more frames of the node at zero then snaps to the RPC’d position.

To summarize:

  • host creates peer player node and sets position and authority.
  • node is spawned by a MultiplayerSpawner on peer with default state and has internal mechanism to set authority based on name.
  • peer node’s MultiplayerSynchronizer sends back default state to host, moving the position to zero.
  • position RPC moves node back to desired location.

( All in all this approach works, but will not be scalable and is not utilizing Godots tools to their fullest. I kind of have a notion to make a Godot multiplayer tutorial to try and un-teach the early Godot 4 tutorial methods, and maybe improve the old Godot docs. But I would like to get to a complete game done first, so I can provide solutions to many common replication problems that someone may face. )

1 Like

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