How to properly resolve authority if I want sharp FPP movement with no input delay?

Godot Version

Godot Engine v4.3.stable.steam.77dcf97d8

Question

I throw the towel, I am stuck on one simple problem for the 6th day now.

I’m trying to make a first person perspective multiplayer game, but I have an issue where if you add a player prefab from peer_connected call but the prefab has client authority, you CANNOT modify it. Something as simple as setting a position of a newly spawned player CANNOT be set by the server, or by any other client for that matter.

Some people say that it works if you set position before adding the child, but it’s simply not true - this doesn’t work either (example code below).

One solution I have found to this whole issue is to have server authority over player prefabs. I followed many guides of the legend Battery Acid Dev (like this video) and he seems to like server authority approach for his TPP games - the problem is if you go this way you get input delay. Now it’s not a lot and it’s not as noticable for TPP games, but it is enough to be subconsciously noticable in FPP since it is equal to the ping of the player, and well - first person perspective movement with input delay just feels awful.

Can somebody please tell me how can I solve this seemingly basic issue? How can I keep client authority for player prefab so I have sharp client side movement, but so that I can also manipulate its fields like position via server? I suppose some kind of RPC call would solve all of it but I just cannot find an elegant solution to this problem.

Example of a code that doesn’t work for client authoritive player prefab (and how I set client authority for said prefab). The general idea for this I got from this video.

# gameplay scene code
[...]
multiplayer.peer_connected.connect(add_player)
[...]
func add_player(id = 1):
	var player = player_scene.instantiate() as Node3D
	player.name = str(id)
	player.position = Vector3(10, 0, 10) # <- spawns at 0, 0, 0 regardless
	call_deferred("add_child", player)
# player prefab code
func _enter_tree():
	set_multiplayer_authority(name.to_int())

The tricky thing here is multiplayer spawners will always put the node at position zero of the watched node. The synchronizer will move it to the new location.

This is the issue:
Basically you set the position and authority on the host. The host copy of the player peer will be at desired position, but the synchronizer is waiting for authority peer updates. Then remote spawner will spawn it on the authority peer at zero position. Then the peer takes its authority and will now sends its zero position back to the host.

To fix this you need to use the custom spawn function of the multiplayer spawner.

this may have bugs, and is for reference only
extends MultiplerSpawner

func _init():
  self.spawn = my_custom_spawn

func my_custom_spawn(position, player_id):
  var p = player_scene.instanctiate()
  p.set_authority(player_id)
  p.name = str(player_id)
  p.position = position
  return p

Then in the main


func add_player(id = 1):
	var pos= Vector3(10, 0, 10)
	$MultiplayerSpawnee.spawn(pos, id) # <-- these parameters will be sent to all current peers

Doing it this way, you will instruct the spawner to setup the node before it’s added to the tree in both host and peer.

Be aware that you can’t send premade objects into the custom spawn function. This is because of security to prevent remote code execution. Basic type like ints strings and vectors or okay. Arrays and dictionarys are okay as long as the don’t have objects . ( You can disable the security feature but it is not recommended)

Regarding host authority approach to fix input delay, you need client side prediction.