godotengine / godotengine/godot

`modules/multiplayer/scene_replication_interface` @243 throws error perhaps change this check to a warning?

Open
#98,104 0 comments 2 reactions 0 assignees View on GitHub
discussion topic:multiplayer
Dominant language
C++
Stars
117k
Forks
26.8k
PR merge metrics
PR metrics pending

Description

### Tested versions

4.3 release (not C#)

### System information

Godot v4.3.stable.mono - Windows 10.0.22631 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3090 (NVIDIA; 32.0.15.6094) - AMD Ryzen 9 5900X 12-Core Processor (24 Threads)

### Issue description

Hopefully this issue is a nice break from the others, it's kind of like a reverse-bug where an error is thrown, but the correct behavior is observed.

I'm following an implementation pattern similar to this blog post: https://godotengine.org/article/multiplayer-in-godot-4-0-scene-replication/

Essentially, when a Node is spawned, and the multiplayer authority of a child MultiplayerSynchronizer node is changed during _ready() or _enter_tree(), this error is thrown:

```
E 0:00:06:0613 on_replication_start: The MultiplayerSynchronizer at path "/root/Lobby/Spawns/676522849/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.
Condition "pending_sync_net_ids.is_empty()" is true. Returning: ERR_INVALID_DATA
modules/multiplayer/scene_replication_interface.cpp:243 @ on_replication_start()
```

But if the node is inspected in the remote scene trees, the correct multiplayer authority is displayed for all clients. The error is repeated again when a new client joins, for every current client. (i.e. first client join sees 1 error, second client join is 4 total errors, third client brings the total to 9, ...)

The error is NOT thrown if a base Node is used instead of a MultiplayerSynchronizer - so I'm worried there may still be something behind the error going wrong I'm not aware of.

I've also tried adding a timed delay after the player joins, and the player node is instantiated, but before it is added to the scene by parenting to the Spawn node - so I believe this is unrelated to spawning a player node too soon after a player connects.

### Steps to reproduce

I've created a micro example if anyone wants to try, you only need to make two scenes:

**Lobby.tscn** *(default start scene)*

* Add MultiplayerSynchronizer as child of this Node
* Add Node as child, name it Spawns
* Add the following script to the Lobby:
```gdscript
extends Node

@onready var spawns: Node = $Spawns
@onready var spawner: Node = $MultiplayerSpawner

const PORT = 7000
const DEFAULT_SERVER_IP = "127.0.0.1"
var Player = preload("res://player.tscn")

func _ready() -> void:
spawner.spawn_path = spawns.get_path()
spawner.add_spawnable_scene("res://player.tscn")
create_buttons()

func create_game():
print('Creating Game')
var peer = ENetMultiplayerPeer.new()
peer.create_server(PORT)
multiplayer.multiplayer_peer = peer
multiplayer.peer_connected.connect(add_player)

func join_game():
print('Joining Game')
var peer = ENetMultiplayerPeer.new()
peer.create_client(DEFAULT_SERVER_IP, PORT)
multiplayer.multiplayer_peer = peer

func add_player(peer_id):
print('Adding Player, Peer ID: %s' % [str(peer_id)])
# await get_tree().create_timer(2.0).timeout
# print('Timeout Done')
var player = Player.instantiate()
player.name = str(peer_id)
spawns.add_child(player, true)

func create_buttons():
var vbox = VBoxContainer.new()
add_child(vbox)

var host_button = Button.new()
host_button.text = "HOST"
host_button.connect("pressed", create_game)
vbox.add_child(host_button)

var join_button = Button.new()
join_button.text = "JOIN"
join_button.connect("pressed", join_game)
vbox.add_child(join_button)
```

**player.tscn**
* Add MultiplayerSynchronizer as child
* Add this script to the MultiplayerSynchronizer:
```gdscript
extends MultiplayerSynchronizer

func _enter_tree() -> void:
print("NetID: %s is setting multiplayer authority to %s" % [str(multiplayer.get_unique_id()), str(get_parent().name)])
set_multiplayer_authority(get_parent().name.to_int())
print("NetID: %s sees authority: %s" % [str(multiplayer.get_unique_id()), str(get_multiplayer_authority())])
```

Now to test it, you should see an error like:
```
E 0:00:06:0613 on_replication_start: The MultiplayerSynchronizer at path "/root/Lobby/Spawns/676522849/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.
Condition "pending_sync_net_ids.is_empty()" is true. Returning: ERR_INVALID_DATA
modules/multiplayer/scene_replication_interface.cpp:243 @ on_replication_start()

```
and output in the terminal like:
```
Creating Game
Joining Game
Adding Player, Peer ID: 622658096
NetID: 1 is setting multiplayer authority to 622658096
NetID: 1 sees authority: 622658096
NetID: 622658096 is setting multiplayer authority to 622658096
NetID: 622658096 sees authority: 622658096
```
And if you inspect the MultiplayerSynchronizer node in the Scene -> Remote panel, both sessions will show the correct authority.

### Minimal reproduction project (MRP)

[multiplayer_synchronizer_MRP.zip](https://github.com/user-attachments/files/17347389/multiplayer_synchronizer_MRP.zip)

Contributor guide

Open the contributing guide

Research direction

Start with the minimal reproduction project and reproduce the repeated error from modules/multiplayer/scene_replication_interface.cpp:243 in on_replication_start(). Trace the authority changes made in the MultiplayerSynchronizer _enter_tree() callback and compare them with the pending spawn handling. Done means the behavior is understood and the issue's error-versus-warning outcome is verified without hiding a real replication failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
game-dev, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.