Godot can't synchronize spawned nodes

thanks I think this will work since it is literally a direct example of bullets and multiplayer. Thanks so much.

1 Like

I just have one question, what is the “bullet parent”? @onready var bullet_parent = $BulletParent

That’s just an empty node used to organize the scene a little better. The bullets could all be added to the main scene or wherever really.

That creates a layout like this:

  • MainGame
    • PeerPlayerParent
      • PeerPlayer1
      • PeerPlayer2
    • BulletParent
      • Bullet 1
      • Bullet 2
      • Bullet 3
      • etc…
1 Like

I had a vector 2 issue here with rotating but I just solved it

OK I’ve fixed the error but the problem is the bullets still don’t sync
server code:

extends Node2D


var peer =  ENetMultiplayerPeer.new()
@export var player_scene: PackedScene
@onready var menu = get_node("CanvasLayer/PanelContainer")
var bullet_scene = preload("res://scenes/bullet.tscn").instantiate()
@onready var bullet_parent = $BulletParent

func _on_host_pressed():
	menu.hide()
	peer.create_server(8120)
	multiplayer.multiplayer_peer = peer
	multiplayer.peer_connected.connect(add_player)
	add_player(multiplayer.get_unique_id())


func add_player(id):
	var player = player_scene.instantiate()
	player.name = str(id)
	add_child(player)

func _on_join_pressed():
	menu.hide()
	peer.create_client("localhost",8120)
	multiplayer.multiplayer_peer = peer


func _on_button_pressed():
	menu.show()
	


@rpc("any_peer", "call_local", "reliable")
func create_bullet(bullet_position, bullet_direction):
	var bullet = bullet_scene
	#bullet.direction = bullet_direction
	bullet_direction.angle_to_point(position)
	bullet.position = bullet_position
	
	bullet_parent.add_child(bullet)

player code

extends CharacterBody2D
var health = 10
@onready var cooldown = %Cooldown
var canshoot = true
func _enter_tree():
	set_multiplayer_authority(str(name).to_int())

@onready var camera = %Camera2D
const SPEED = 330.0
const JUMP_VELOCITY = -400.0
#@onready var main = $"."
@onready var maingame = get_node("/root/main")

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")


func _physics_process(delta):
	if not is_multiplayer_authority(): camera.enabled = false
	if is_multiplayer_authority(): 
		if Input.is_action_pressed("use_shoot") && canshoot == true:
			rpc("shoot")
			canshoot = false
			cooldown.start()
			
		
	#if not is_multiplayer_authority(): return
	# Add the gravity.
	if not is_on_floor():
		velocity.y += gravity * delta

	# Handle jump.
	if is_multiplayer_authority(): 
		if Input.is_action_pressed("i_up") and is_on_floor():
			velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
		var direction = Input.get_axis("i_left", "i_right")
		if direction:
			velocity.x = direction * SPEED
		else:
			velocity.x = move_toward(velocity.x, 0, SPEED)
		move_and_slide()
					
func take_damage(amount):
	print("YOUCH")
	print("YOUCH")
	health -= amount
	if health <= 0:
		queue_free()
		
@rpc("authority", "call_local", "reliable")
func shoot():
	if is_multiplayer_authority():
		var mouse_pos = get_global_mouse_position()
		var shoot_direction = mouse_pos
		maingame.create_bullet(self.get_global_position(), shoot_direction)



func _on_area_2d_body_entered(body):
	if body.is_in_group("Arbullet"):
		queue_free()


func _on_cooldown_timeout():
	canshoot = true

also I do get a new error occasionally
Invalid set index 'position' (on base: 'previously freed') with value of type 'Vector2'.
this error is for line 39 on the servers code

I also get this error
E 0:00:05:0224 multiplayermenu.gd:41 @ create_bullet(): Can't add child 'Bullet' to 'BulletParent', already has a parent 'BulletParent'.

The first thing that stands out is this line at the top:

var bullet_scene = preload("res://scenes/bullet.tscn").instantiate()

You’re instantiating the bullet once and trying to reparent it each time instead of creating a new one. Instead, you’d typically preload the scene and store the result in a variable. Then, each time you create one, instantiate and add it as a child.

const bullet_scene = preload("res://scenes/bullet.tscn")

# Later when creating a new bullet
var bullet = bullet_scene.instantiate()

heres what happened when I tried to do that:

extends Node2D


var peer =  ENetMultiplayerPeer.new()
@export var player_scene: PackedScene
@onready var menu = get_node("CanvasLayer/PanelContainer")
const bullet_scene = preload("res://scenes/bullet.tscn")
@onready var bullet_parent = $BulletParent

func _on_host_pressed():
	menu.hide()
	peer.create_server(8120)
	multiplayer.multiplayer_peer = peer
	multiplayer.peer_connected.connect(add_player)
	add_player(multiplayer.get_unique_id())


func add_player(id):
	var player = player_scene.instantiate()
	player.name = str(id)
	add_child(player)

func _on_join_pressed():
	menu.hide()
	peer.create_client("localhost",8120)
	multiplayer.multiplayer_peer = peer


func _on_button_pressed():
	menu.show()
	


func create_bullet(bullet_position, bullet_direction):
	var bullet = bullet_scene
	#bullet.direction = bullet_direction
	bullet_direction.angle_to_point(position)
	#bullet.global_transform = bullet_position
	bullet_parent.add_child(bullet)
	bullet = bullet_scene.instantiate()

I get this error: Invalid type in function 'add_child' in base 'Node'. The Object-derived class of argument 1 (PackedScene) is not a subclass of the expected argument class.
and this error still E 0:00:03:0315 multiplayermenu.gd:39 @ create_bullet(): Parameter "p_child" is null. <C++ Source> scene/main/node.cpp:1409 @ add_child() <Stack Trace> multiplayermenu.gd:39 @ create_bullet() CharacterBody2D.gd:58 @ shoot() CharacterBody2D.gd:22 @ _physics_process()

I can’t add it unless I instantiate so I guess I put the code in the wrong order?

Correct, you can’t just add anything as a child, there are types that are allowed. PackedScene won’t work, you need to instantiate it so you get the Node and add it.

Take a look at the documentation of:
Node.add_child: Node — Godot Engine (stable) documentation in English
PackedScene.instantiate: PackedScene — Godot Engine (stable) documentation in English

2 Likes

I’m really confused this is difficult for me to understand. var child_node = get_child(0) if child_node.get_parent(): child_node.get_parent().remove_child(child_node) add_child(child_node) I have to get the child? and then get it’s parent and then remove it, and then add it again? I don’t know how I could implement this into my code. Thank you though.

I fail to see why you would need this, removing parent that is. The bullet you just instantiated won’t have any parent until you set it. That part of the code in the documentation is there so you know how to unparent the node before setting its parent in case your node has a parent already.

Just so you know MultiplayerSynchronizer has a replication config that you need to setup properties of the node that should sync. Position rotation and velocity are the basic props to sync.

Godot multiplayer is geared to an authoritative server and the host will own all nodes by default, except for nodes you synchronize authority to a peer. So if the synchronizer isn’t authed to the peer on the server and the peer has authed itself. The server will reject any communication. This also is true for MultiplayerSpawner.

The server and peer need to agree, and be synced, on authority of a node. Especially for the MultiplayerSynchronizers and MultiplayerSpawners.

Generally speaking for an auth server, the peer should only send input to the server and there server will create and destroy nodes that will be replicated to the clients.

You can try and do peer to peer but you will be working against the grain of Godot’s design.

Some issues I see in your code that I think will give you some hardship is when a bullet is destroyed.

For Auth server, or peer-to-peer, you need to check if the node authority is deleting the node otherwise it may get deleted on the remote side but the authority instance still exists and begins to throw errors on the remote end saying node doesn’t exist.

Use

If is_multyplayer_authority():
  self.queue_free()
   ...

To check before you delete.

1 Like

the multiplayer synchronizer is another issue using the properties does not fix it, also I think I fixed the nodes spawning it’s just this error keeps freezing my window and I don’t know whats going on, heres the code that causes the error: server:

@rpc("any_peer", "call_local", "reliable")
func create_bullet(bullet_position, bullet_direction):
	var bullet = bullet_scene.instantiate()
	bullet.direction = bullet_direction
	bullet.position = bullet_position

	bullet_parent.add_child(bullet)

player:

@rpc("authority", "call_local", "reliable")
func shoot():
	if is_multiplayer_authority():
		var mouse_pos = get_viewport().get_mouse_position()
		var shoot_direction = (mouse_pos - position).normalized()
		maingame.create_bullet.rpc(position, shoot_direction)

heres the error itself:

Invalid set index 'direction' (on base: 'Area2D (bullet.gd)') with value of type 'Vector2'.

I tested the code again and now the bullets seem to not be spawning, I keep repeating the same loop and stuck with the same errors, I have no idea why this is happening.

That last error you mentioned is pointing at this.

Bullet_direction is vector2, but bullet.direction seems like it could be some other type. bullet seems to be an area2d and that doesn’t have a direction property. So I assume it’s something custom?

Just so you know, working with multiplayer is hard. I’m still running into issues myself. The main hurdle with multiplayer development is debugging, and Godot is lacking when it comes to attempting multiplayer debugging.

One trick I employ sometimes is running one side of the server or client in the editor and the other via the command line. This allows you to isolate breakpoints and output logs.

The other is the remote tree view. Whenever you experience a problem hit the pause button and start sorting through the running tree view and look at property values of nodes and resources.

Last resort, is get into unit testing the code.

Anyway, I think your new code snippet looks good. I couldn’t say for sure what the problem is.

If you are using MultiplayerSpawners make sure to always use human readable names when you add a child. But how your code is setup now, that RPC should create a bullet for all currently connected peers.

1 Like

I tried implementing it for a characterbody2d but uh it still fails.

Invalid set index 'direction' (on base: 'CharacterBody2D (bullet.gd)') with value of type 'Vector2'. I also did it with a node2D, but I can’t find any other ways to rotate stuff online…

also the menu seemed to be working, because I could spawn players in and they moved together and you could see it, but for some reason it randomly stopped working, before it stopped working it had an error but the error did nothing the error is now affecting the menu it is: E 0:00:01:0213 multiplayermenu.gd:6 @ _ready(): Node not found: "CanvasLayer/PanelContainer" (relative to "/root/Multiplayermenu"). <C++ Error> Method/function failed. Returning: nullptr <C++ Source> scene/main/node.cpp:1638 @ get_node() <Stack Trace> multiplayermenu.gd:6 @ _ready() the code that I use to get the panel container is: @onready var menu = get_node("CanvasLayer/PanelContainer") I also have another error with finding a bullet parent in the scene, these errors have a simple fix, but for some reason nothing can fix them, I don’t know if it’s just my code or if it’s godot, but I don’t think it’s godot.

for context the “bulletparent” is a node used to group the bullets, but It bugs out so I removed it this is what I was using to find it: @onready var bullet_parent = $"/root/Multiplayermenu/BulletParent"