Quick answer: Spawn the node via MultiplayerSpawner so the path matches across peers. Set multiplayer_authority on the owner. Decorate RPC with @rpc annotation.
You instantiate a Player on the server. RPC fires. Client logs nothing. The Player exists on the server only; client has no node at that path to dispatch the RPC to.
The Symptom
RPC sender runs cleanly. Receivers don’t. Console silent — no error.
The Fix
# Server-side spawn via MultiplayerSpawner
var spawner = $MultiplayerSpawner
# spawner.spawn_path = NodePath("/root/Game/Players")
# spawn scene preload set in inspector
func spawn_player(peer_id: int) -> Node:
var p = preload("res://player.tscn").instantiate()
p.name = str(peer_id)
p.set_multiplayer_authority(peer_id)
$Players.add_child(p, true) # force_readable_name
return p
# Player.gd
@rpc("any_peer", "call_local", "reliable")
func take_damage(amount: float):
hp -= amount
Spawner replicates the spawn event, so identical nodes appear at identical paths on every peer. Authority pins which peer is canonical. RPC then resolves correctly.
Verifying
Server damages player. Client’s player.hp drops. Client damages own player; server’s copy updates. Without spawner: clients have no Player node and RPC drops.
“Spawner makes paths agree. Authority resolves. RPC dispatches.”
Related Issues
For client prediction, see prediction. For canvas layer input, see canvas input.
Spawner. Authority. RPC arrives.