Godot multiplayer help

Godot Version

4.2

Question

Hi, so i am trying to make a multiplayer fps game, but I ran into a small issue.
when I test and connect with two instances the arms move on the other player if I move them on my player. I have tried fixing it but nothing is working
Also here is the video link:

also here is the code

extends Node3D

var peer = ENetMultiplayerPeer.new()
@export var player_scene : PackedScene

func _on_host_pressed():
	peer.create_server(6969)
	multiplayer.multiplayer_peer = peer
	multiplayer.peer_connected.connect(add_player)
	add_player()
	$CanvasLayer.hide()

func _on_join_pressed():
	peer.create_client("127.0.0.1", 6969)
	multiplayer.multiplayer_peer = peer
	$CanvasLayer.hide()

func add_player(id = 1):
	var player = player_scene.instantiate()
	player.name = str(id)
	call_deferred("add_child", player)

func exit_game(id):
	multiplayer.peer_disconnected.connect(del_player)
	del_player(id)

func del_player(id):
	rpc("_del_player", id)
	
@rpc("any_peer", "call_local")
func _del_player(id):
	get_node(str(id)).queue_free()

Could you post the player script? I believe your camera control is not based on multiplayer authority. Maybe it’s an _input override that needs a matching set_process_input(is_multiplayer_authority())?

1 Like

Hi, I believe it has to do with mutliplayer authority, which decides which instance can control what

When a player is added to the scene, you should set its authority with set_multiplayer_authority(x) where x is an int.

A solution I have seen multiple times is to set the player’s name to the client id when it is added to the scene:

var player = player_scene.instantiate()
player.name = str(id)
players.add_child(player)

(players in this case is the node containing all my players)
Then set the player’s authority in the ready or enter_tree function like so set_multiplayer_authority(name.to_int())
Then you can check if the character should be controlled or not with if is_multiplayer_authority(): ...

In my project I decided to opt for what I believe is a clean way to handle this, I explain it briefly in this topic: How to sync different players? - #3 by godotenjoyer3000

Cool server port btw

extends CharacterBody3D


const BASE_FOV = 100.0
const FOV_CHANGE = 3.5

var speed
var CROUCH_SPEED = 3
var crouch_speed = 3
const WALK_SPEED = 6.2
const SPRINT_SPEED = 8.0
const JUMP_VELOCITY = 5.5
const sensitvity = 0.01
const SPEED_SPRINT = 10.0
var default_height = 1.5
var crouch_height = 0.3
const bob_freq = 2.3
const bob_amp = 0.08
var t_bob = 0.0
var damage = 100


var instance


@onready var head = $Neck
@onready var camera = $Neck/Camera3D
@onready var collision = $CollisionShape3D
@onready var headbonker = $HeadBonker
@onready var Aimcast = $Neck/Camera3D/hand/AimCast
@onready var animshoot = $AnimationPlayer

var gravity = 9.8

func _enter_tree():
	set_multiplayer_authority(name.to_int())



func _ready():
	if not is_multiplayer_authority(): return 
	camera.current = true
	


func _unhandled_input(event):
	if event is InputEventMouseMotion:
		head.rotate_y(-event.relative.x * sensitvity)
		camera.rotate_x(-event.relative.y * sensitvity)
		camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-40),deg_to_rad(60) )
		
		
	if event is InputEventMouseButton:
		Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
	elif event.is_action_pressed("ui_cancel"):
		Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)


func fire():
		if Input.is_action_pressed("fire"):
			if not animshoot.is_playing():
				animshoot.play("Shoot")


func _physics_process(delta):
	if is_multiplayer_authority():
		fire()
	
	
	if	Input.is_action_just_pressed("fire"):
		if Aimcast.is_colliding():
				var target = Aimcast.get_collider()
				if target.is_in_group("ENEMY"):
					print("hit enemy")
					target.health -= damage
				
	if Input.is_action_just_pressed("quit"):
		$"../".exit_game(name.to_int())
		get_tree().quit()


	
	#print(stanima)
	if not is_on_floor():
		velocity.y -= gravity * delta

	if headbonker.is_colliding():
		collision.shape.height -= crouch_speed * delta 
		collision.shape.height = clamp(collision.shape.height, crouch_height, default_height)

	# Handle jump.
	if is_on_floor() or is_on_wall():			
		if Input.is_action_just_pressed("Jump"):
			velocity.y = JUMP_VELOCITY
		
	

	if Input.is_action_pressed("sprint"):
		speed = SPRINT_SPEED
	elif headbonker.is_colliding():
		speed = CROUCH_SPEED
	else:
		speed = WALK_SPEED

	if Input.is_action_pressed("crouch"):
		speed = CROUCH_SPEED
		collision.shape.height -= crouch_speed * delta 
	else:
		collision.shape.height += crouch_speed * delta
		
	collision.shape.height = clamp(collision.shape.height, crouch_height, default_height)



	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir = Input.get_vector("left", "right", "forward", "back")
	var direction = (head.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if is_on_floor():
		if direction:
			velocity.x = direction.x * speed
			velocity.z = direction.z * speed
		else:
			velocity.x = lerp(velocity.x, direction.x * speed, delta * 7.0)
			velocity.z = lerp(velocity.z, direction.z * speed, delta * 7.0)
		
	else:
		velocity.x = lerp(velocity.x, direction.x * speed, delta * 2.0)
		velocity.z = lerp(velocity.z, direction.z * speed, delta * 2.0)
	
	t_bob += delta * velocity.length() * float(is_on_floor())
	camera.transform.origin = _headbob(t_bob)


	#fov
	var velocity_clamped = clamp(velocity.length(), 0.5, SPRINT_SPEED * 2)
	var target_fov = BASE_FOV + FOV_CHANGE * velocity_clamped
	camera.fov = lerp(camera.fov, target_fov, delta * 8)



	move_and_slide()



func _headbob(time) -> Vector3:
	var pos = Vector3.ZERO
	
	pos.y = sin(time * bob_freq) * bob_amp 
	pos.x = cos(time * bob_freq / 2) * bob_amp
	return pos
	

this should be the most up to date player script

hi thank you for a response but.

Where would I get the players from.

Cool! So looks like the camera.current and fire() is controlled by is_multiplayer_authority but none of the inputs are!

Instead of putting if is_multiplayer_authority(): above each function I would recommend disabling the _input and _physics_process functions entirely. This _ready sample does just that.

func _ready() -> void:
    var player_controlled := is_multiplayer_authority()
    camera.current = player_controlled
    set_process_unhandled_input(player_controlled)
    set_physics_process(player_controlled)

The position should still update with the MultiplayerSynchronizer; you might need to add camera rotation to it’s list if you haven’t already.

I would just add

if !is_multiplayer_authority(): return

At the top of input and process functions for a quick fix

1 Like

Hi it fixed the issue, but now when I rotate with the player, it does not show up on the other persons screen

Add the rotation property in your multiplayer synchronizer

1 Like

Can you post a screenshot of the multiplayer synchronizer? I believe the second part of my post should fix that by adding the camera’s rotation property to the synchronizer.

Hi, here is the synchronizer

Does the head/neck contain the arms and weapon? I suppose it’s up to the scene tree, post that if you have any more issues. If the camera has no visible children then I am wrong, make sure to synchronize the visible parts

hi the neck, does include the arm and the gun.

So if I want to synchronize the weapon and the arm, I should add them to the synchronizer right?

Depends on what gets rotated, I see these lines tell us the $Neck and $Neck/Camera get rotated.

head.rotate_y(-event.relative.x * sensitvity)
camera.rotate_x(-event.relative.y * sensitvity)

Looking at your video again the weapons do move side-to-side (with the $Neck) and up-and-down (with the $Neck/Camera) so they must be children of camera.

If the arms only synchronize up/down then add the $Neck rotation to the synchronizer. if the arms do not move at all then it is a synchronizer issue, maybe the multiplayer authority isn’t being assigned correctly? I highly doubt it’s a synchronizer issue as it worked in the first video.

1 Like

that worked, thank you so much.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.