Godot Version
v4.6.2.stable.official [71f334935]
Question
Hello! I’m having another issue with my multiplayer game. When spawning enemies, it seems that one instance of the game cannot find the enemies, even though they are clearly present. This seems to be an issue with the synchronizer node each enemy has.
I’m fairly sure this is related to some other problems I’m having (enemies not attacking at all, enemies attacking many times in a row, enemies’ health not updating on one machine, etc.)
Thanks for any help you can provide!
Is this error coming from the host or the peer? And in what part of your code do you add the node to the synchronizer vs. what part is trying to access the resource.
I might not be completely right, but I will assume that replication is async by nature, hence the peer might be trying to access a node that hasn’t been correctly replicated yet.
The issue is coming from the peer. The nodes have synchronizers as a child in each one:
I have no idea what is trying to access the synchronizers.
Do you also synchronize their spawn? What I mean is, you are trying to synchronize their values through the synchronizer, but is there a MultiplayerSpawner node anywhere in your level scene that takes care of replication when you spawn the entities?
I did not use a MultiplayerSpawner node. Should I be? That seems to come up frequently.
Yes, you should add a MultiplayerSpawner node and add your entities as child to the target spawn node you select there. Also make sure to add the entity scene on the autospawn list in the inspector else it won’t work. Once you do this I believe the issue will be solved 
Maybe this will help: I am instantiating a “dungeon” scene as a child of the “main” scene:
Inside are all of the enemies. Where should I put the MultiplayerSpawner? As a child of the “enemies” node?
You should put it as a child of the dungeon and then set the enemies node as your spawn path in the inspector for the MultiplayerSpawner. Similar to the following:
if you have no limit to how many entities you can have, then just keep it as 0
Still seems to be giving me the same error, but on the host machine this time.
EDIT: nope! Still on the other machine.
could you share your MultiplayerSpawner settings?
I have to go somewhere, I’ll be back in about 2 hours.
Does the Spawn Limit of 0 impact anything? I get the sense that it means it can spawn infinite if set to 0.
No. As you said it simply means there’s not limit to the spawn number.
Are you calling any RPC in your script? The logs show this process_rpc error targeting a node that seems to not be available on both sides.
What I can also suggest to try and do is to compare the two Trees from the host and peer side. If they are not exactly the same then synching issues start to raise. Try to find what’s missing or what’s the anomaly and backtrack to where this is coming from
Multiplayer Spawner will not do well with already existing nodes, you probably do not need one for enemies, however you could and probably should make use of a multiplayer spawner for the entire dungeons. How are you instantiating the dungeon right now? Are you sure the client is also instantiating the same dungeon?
Actually, instantiating the same dungeon was a problem I have faced. I am using an @rpc("any_peer", "call_local") annotation to spawn the same dungeon on each instance. Actually, in the image I attached to MultiplayerSynchroniser issues - #9 by Endertul0 , you can see, starting on line 18, I am working on that. I am always spawning the same dungeon type on each player.
I will work on using the MultiplayerSpawner node for the dungeon as a whole once I am at my main computer.
Hard to read screenshots; can you paste the rpc’d functions, for example generateDungeon is rpc’d but I have no idea what goes on in there, it could be spawning something totally different for each client
There are several @rpc calls in my code. I have no idea why it wouldn’t be available on both sides, as the nodes have the same name and structure on both instances. Do you think IDs have anything to do with the error? It seems to be working with a path parameter, though, which should be valid.
I don’t have the code in front of me right now, but the best that I can remember it is:
@rpc("any_peer", "call_local")
func generateDungeon(style: int):
if $world.has_child(0):
push_error("(UID {0}) already has dungeon {1}!".format(
[
str(multiplayer.get_unique_id()),
str(style)
]
)
else:
var dungeon = load(dungeons[style]).instantiate()
$world.add_child(dungeon)
dungeon.name = "dungeon" + str(style)
How are you replicating the dungeon over the network? That also needs a multiplayer spawner as far as I am aware.
But the biggest advice I can give you is to check the Tree on both ends and check what’s not matching
@gertkeno Here’s my code for spawning the dungeon:
@onready var world: Node2D = $world
var dungeons: Array[String] = [
"res://scenes/styles/dungeon_0.tscn",
"res://scenes/styles/dungeon_1.tscn",
"res://scenes/styles/dungeon_2.tscn"
]
var shops: Array[String] = ["res://scenes/styles/shop_0.tscn"]
var bossDungeons: Array[String] = ["res://scenes/styles/boss_0.tscn"]
func _ready() -> void:
var determiner = Node.new()
determiner.name = "Debug" + str(multiplayer.get_unique_id())
add_child(determiner)
if multiplayer.get_unique_id() == 1:
var dungeonStyle = randi_range(0, dungeons.size() - 1)
for k in Global.players:
if Global.levelSeq[Global.levelNum] == 0:
generateDungeon.rpc_id(int(Global.players[k].id), dungeonStyle)
elif Global.levelSeq[Global.levelNum] == 1:
generateShop.rpc_id(int(Global.players[k].id))
else:
generateBossfight.rpc_id(int(Global.players[k].id))
var j = 0
var toSpawn = []
for player_index in Global.players:
var newPlayer: PlayerEntity = preload("res://scenes/player.tscn").instantiate()
newPlayer.get_node("playerCam").enabled = false
newPlayer.get_node("playerCam").visible = false
newPlayer.set_color(Global.players[player_index].color)
var id = Global.players[player_index]["id"]
newPlayer.set_playerNum(id)
toSpawn.append(newPlayer)
add_child(newPlayer)
newPlayer.name = str(id)
print_rich("[color=salmon](UID {0}) {1}'s name is now {2}".format(
[
str(multiplayer.get_unique_id()),
str(id),
newPlayer.name
]
))
for spawn in $spawnLocs.get_children():
if spawn.name == str(j):
if newPlayer is PlayerEntity:
newPlayer.global_position = spawn.global_position + Vector2(newPlayer.get_index(), 0)
j += 1
@rpc("any_peer", "call_local")
func generateDungeon(style: int):
if $world.has_node(NodePath("dungeon" + str(style))):
push_warning("Player {1}'s world already has dungeon {0}".format(
[
str(style),
str(multiplayer.get_unique_id())
]
))
return
else:
print_rich("[color=green](UID {1}) Generating dungeon {0}[/color]".format(
[
str(style),
str(multiplayer.get_unique_id())
]
))
var dungeon = load(dungeons[style]).instantiate()
dungeon.name = "dungeon" + str(style)
$world.add_child(dungeon)
colelli: the only thing that doesn’t match is intended to not match. It’s a node with the multiplayer’s unique_id property as the name.