Godot Version
4.4.1 Stable
Question 
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 
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 
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 
Scene 
BulletSpawner (MultiplayerSpawner) Inspector 
Flaregun.gd 
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 
Scene 
MultiplayerSynchronizer Inspector 
Flare.gd 
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