MultiplayerSpawner Not Spawning Flare Scene on Clients (Flare Shot from Flare Gun in P2P)

Godot Version

4.4.1 Stable

Question :red_question_mark:

I’m working on a multiplayer game in Godot 4 where a flare gun spawns a flare using a MultiplayerSpawner, but the flare only appears on the host and not on the clients. What could be causing it to not spawn on clients?

Further Explanation :magnifying_glass_tilted_left:

I’m developing a multiplayer first-person game using Godot 4. The host can start a game using either ENetMultiplayerPeer or SteamMultiplayerPeer, and clients can join successfully. Players on both ends can equip Flare Guns and fire flares. Most synchronization works as expected, but the flares only spawn and appear on the host’s screen — even when fired by a client.

Inspecting the Remote Scene debugger confirms that flares are only spawning on the host’s instance. They are not hidden or deactivated on the client — they simply don’t exist there. I’m not getting any related errors or warnings in the debugger either.

I’ve been stuck on this problem for almost a week now and have asked for help on other platforms with no answers, so any help would be greatly appreciated. Thank you.

All Details :clipboard:

Below I’ve included the relevant scene structure, node setup in the Inspector, and the code for both the Flare Gun and Flare. The Host and Client both have a Flaregun. Multiplayer is P2P and the Host is the Server.

Flaregun :water_pistol:

Scene :clapper_board:

BulletSpawner (MultiplayerSpawner) Inspector :hammer_and_wrench:

Flaregun.gd :gear:

extends RigidBody3D

#==============================================================================#
#                            EXPORTED VARIABLES                                #
#==============================================================================#
@export var fire_power := 0.5           # Initial shooting force
@export var flare_duration := 90.0        # How long the flare lasts
@export var flare_brightness := 50.0      # Maximum brightness of the flare
@export var flare_color := Color.RED      # Color of the flare light

#==============================================================================#
#                             ONREADY VARIABLES                                #
#==============================================================================#
@onready var bullet_spawner: MultiplayerSpawner = $BulletSpawner
@onready var spawn_point: Marker3D = $SpawnPoint


#==============================================================================#
#                             MEMBER VARIABLES                                 #
#==============================================================================#
const FLARE = preload("res://Items/ItemResources/Equippable/Flaregun/Flare.tscn")
var can_fire = true

var is_on: bool = false       # Whether the flare gun is currently active/equipped
var phone_open: bool = false  # Whether the in-game phone is currently open (blocks flare gun use)

var this = 0.0
#==============================================================================#
#                                FUNCTIONS                                     #
#==============================================================================#
func _ready() -> void:
	is_on = true  # Set active when equipped

func _physics_process(_delta: float) -> void:
	if not is_multiplayer_authority(): return
	# Toggle phone state
	if Input.is_action_just_pressed("phone"):
		phone_open = !phone_open

func use() -> void:
	if not is_multiplayer_authority(): return
	if is_on and not phone_open and Input.is_action_just_pressed("primary_action"):
		action()

func action() -> void:
	
	if multiplayer.multiplayer_peer != null:
		request_flare_shot.rpc()
		can_fire = false
		$Cooldown.start()

	else:
		if can_fire:
			shoot_flare()
			can_fire = false
			$Cooldown.start()

@rpc("any_peer", "call_local")
func request_flare_shot() -> void:
	
	if multiplayer.is_server():
		shoot_flare()
	

func shoot_flare() -> void:

	# Instantiate the flare scene
	var flare : RigidBody3D = FLARE.instantiate()

	# If instantiated then spawn
	if flare:
		# Make independent of parent transforms
		flare.set_as_top_level(true)
		flare.transform = $SpawnPoint.global_transform

		flare.set_multiplayer_authority(get_multiplayer_authority())
		
		# Spawn flare
		spawn_point.add_child(flare, true)
		
	
	var flare_direction = flare.transform.basis.z
	
	# Apply an impulse to the flare so it travels forward independently
	flare.apply_central_impulse(flare_direction * fire_power)
	
	# Initialize the flare with its duration, brightness, color, and direction
	flare.initialize_flare(flare_duration, flare_brightness, flare_color, flare_direction)

func _on_cooldown_timeout() -> void:
	can_fire = true

Flare :sparkle:

Scene :clapper_board:

MultiplayerSynchronizer Inspector :hammer_and_wrench:

Flare.gd :gear:

extends RigidBody3D

#==============================================================================#
#                            EXPORTED VARIABLES                                #
#==============================================================================#
@export var air_friction := 10.0 
@export var rotation_friction := 2.0
@export var propulsion_duration := 2.0
@export var propulsion_thrust := 1.0


#==============================================================================#
#                             ONREADY VARIABLES                                #
#==============================================================================#
@onready var flare_light: OmniLight3D = $FlareLight

#==============================================================================#
#                             MEMBER VARIABLES                                 #
#==============================================================================#
var time_remaining : float = 0.0
var max_brightness : float = 20.0
var flare_color : Color
var propulsion_direction : Vector3

#==============================================================================#
#                                FUNCTIONS                                     #
#==============================================================================#
func initialize_flare(light_duration: float, brightness: float, color: Color, direction : Vector3) -> void:
	time_remaining = light_duration
	max_brightness = brightness
	flare_color = color
	propulsion_direction = direction
	
	flare_light.light_color = flare_color
	flare_light.light_energy = 10.0
	flare_light.omni_range = 20.0

func _physics_process(delta: float) -> void:
	
	# Control propulsion and deployment after propulsion
	if propulsion_duration <= 0:
		linear_damp = air_friction
		angular_damp = rotation_friction
		flare_light.light_energy = max_brightness
		flare_light.omni_range = 200.0
	else:
		propulsion_duration -= delta
		apply_force(propulsion_direction)
	
	# Flicker effect
	flare_light.light_energy = max_brightness * randf_range(0.8, 1.0)
	
	# Remove when time expires
	if time_remaining <= 0:
		queue_free()
	else:
		time_remaining -= delta