Help with synchronizing the state of an object in multiplayer

Godot Version

4.7

Question

I’m using a MultiplayerSynchronizer to sync the state of a table between clients, if i set the pencil property to be sync’d “Always” then it doesnt update when a player picks up the pencil, if i set the pencil property to be sync’d “On Change” then it only syncs for the player client that picks up or puts down the pencil. the MultiplayerSynchronizer is located in the main scene as a child of the Table node.

Table Script:

@export var pencil = true
@export var playerpath: NodePath

func interact():
	if pencil and playerpath:
		get_node(playerpath).getpencil()
		$pencil.visible = false
		pencil = false
	elif !pencil and playerpath:
		get_node(playerpath).droppencil()
		$pencil.visible = true
		pencil = true

Player Script:

func _physics_process(delta: float) -> void:
	if !is_multiplayer_authority(): return
	
	
	#interacting
	if InteractCast.is_colliding():
		var target = InteractCast.get_collider()
		if target != null and target.has_method("interact"):
			$InteractHover.play("interact")
			if Input.is_action_just_pressed("Interact"):
				$InteractAction.play("interactaction")
				target.interact()
				if "playerpath" in target:
					target.playerpath = get_path()
		else:
			$InteractHover.stop()
	else:
		$InteractHover.stop()

Is there a reason this is the outcome i’m getting? and is there a way to make it work as intended?

i fixed this by deleting the multiplayer synchronizer on the table and instead using rpc calls:

func interact():
	if pencil and playerpath and get_node(playerpath).pencil == false:
		get_node(playerpath).getpencil.rpc()
		pencilscript.rpc()
	elif !pencil and playerpath and get_node(playerpath).pencil == true:
		get_node(playerpath).droppencil.rpc()
		pencilscript2.rpc()

@rpc("any_peer", "reliable")
func pencilscript():
	pencil = false
	$pencil.visible = false

@rpc("any_peer", "reliable")
func pencilscript2():
	pencil = true
	$pencil.visible = true

in the table script

in the player script moving the interact script under if !is_multiplayer_authority(): return and using rpc calls instead there too


func _physics_process(delta: float) -> void:

		
	#everything below only is run for clients and likely sync'd with a  multiplayer synchroniser
	if !is_multiplayer_authority(): return
	
	
	#interact raycasting
	if InteractCast.is_colliding():
		var target = InteractCast.get_collider()
		if target != null and target.has_method("interact"):
			$InteractHover.play("interact")
			if Input.is_action_just_pressed("Interact"):
				$InteractAction.play("interactaction")
				target.interact()
				if "playerpath" in target:
					target.playerpath = get_path()
		else:
			$InteractHover.stop()
	else:
		$InteractHover.stop()


##item stuff
@rpc("authority","call_local", "reliable")
func droppencil():
	$Head/PlayerCamera/Hands/hookhand/handpos/pencil.visible = false
	pencil = false

@rpc("authority","call_local", "reliable")
func getpencil():
	$Head/PlayerCamera/Hands/hookhand/handpos/pencil.visible = true
	pencil = true


I know you fixed this by moving to RPC calls, and I’m late to the party, but for edification and completeness: I think you were likely dealing with an authority issue on the synchronizer.

By default, the synchronizer will only update a property across the network if the change is made on the peer who owns it’s multiplayer authority. If the authority is set to, say, peer 1 (the host) which it is by default, but peer 2 picks up the pencil, it would locally be picked up. When set to always, it would then immediately receive an update from the server that it wasn’t actually picked up. So on Always it would appear to not work at all. Then with “On Change” the client would locally pick up the pencil, but the server detected no change, so no update is sent and the client is now desynced from the server.