extends Node3D const statsTemplate := "FPS: %d\nPing: %d ms" var runner_scene := preload("res://scenes/runner.tscn") var chaser_scene := preload("res://scenes/chaser.tscn") var _ping := 0.0 @onready var _chasers_node: Node3D = %Chasers @onready var _runners_node: Node3D = %Runners func spawn_player(player_id: int, runner: bool) -> void: if not multiplayer.is_server(): return var player = runner_scene.instantiate() if runner else chaser_scene.instantiate() player.player_id = player_id player.name = str(player_id) _runners_node.add_child(player) if runner else _chasers_node.add_child(player) func despawn_player(player_id: int) -> void: if not multiplayer.is_server(): return var player_id_str := str(player_id) for runner_or_chaser in _runners_node.get_children() + _chasers_node.get_children(): if runner_or_chaser.name == player_id_str: runner_or_chaser.queue_free() func _process(_delta: float) -> void: if DisplayServer.get_name() == "headless": return %StatsLabel.text = statsTemplate % [ Engine.get_frames_per_second(), _ping, ] func _get_timestamp() -> int: return floor(Time.get_unix_time_from_system() * 1000) func _on_ping_timer_timeout() -> void: if multiplayer.is_server(): %PingTimer.stop() return _ping_call.rpc_id(1, _get_timestamp()) @rpc("any_peer", "call_remote", "unreliable", 99) func _ping_call(timestamp: int) -> void: if multiplayer.is_server(): _ping_call.rpc_id(multiplayer.get_remote_sender_id(), timestamp) return _ping = _get_timestamp() - timestamp