Loading Signal Errors

Godot Version

4.4

Question

Hey there! I've been having some issues with my game. I have a checkpoint system in my game at the moment, that saves and loads a scene. In the main level node, it uses this to save it:

When the player dies, it uses this to load it:

However, when it reloads, every signal that’s emitted gives this error.

E 0:00:29:0680 testing_grounds.gd:15 @ _process(): Signal ‘title4’ is already connected to given callable ‘CharacterBody3D(Player.gd)::_on_title_4’ in that object.
<C++ Error> Method/function failed. Returning: ERR_INVALID_PARAMETER
<C++ Source> core/object/object.cpp:1358 @ connect()
testing_grounds.gd:15 @ _process()

All changing to fit the signals name, of course:

What should I do to fix this? It doesn’t crash the game or anything, just effects performance.

The signals are connected via .emit().

You’re trying to connect a signal that’s already connected. The error probably isn’t in the (pictures of) code you’re showing.

Before connecting, check if you’re already connected first.

edit: just a friendly suggestion, but instead of posting pictures of code, consider posting formatted code as text

Hypothesis: you connect signals in your scene during `_ready`. Then the scene is saved, and the connections are saved with the scene. When the scene is loaded, the connections already exist. Then you add the scene to the scene tree, which calls `_ready` on the scene again, which tries to connect the signals again.

That’s the strange part. None of the signals are emitted during the ready function. All of the signals are emitted during unrelated actions to the ready.

Post scene’s structure and code.

The player script is extremely large, so this is a similar, smaller script that provides the same issue, with the coo signal.

extends CharacterBody3D

enum states{
	WANDER,
	WATCH,
	DIE,
	LAUNCH,
	STOP
}

var player = null
@export var currentState : states
@export var waypointIndex : int
@export var health = 2
@export var knockback: Vector3 = Vector3.ZERO
@export var knockback_timer: float = 0.15
@export var cooing = true
@export var yesdead = false

signal dead
signal coo
signal launch

@export var player_path : NodePath
@export var waypoints : Array[Marker3D]

@onready var face = $MeshInstance3D/AnimatedSprite3D
@onready var hand = $MeshInstance3D/AnimatedSprite3D2
@onready var nav_agent : NavigationAgent3D
@onready var anim = $AnimationPlayer
@onready var shield = $CSGSphere3D

const WANDER_SPEED = 8
const BULLET_SPEED = 60

# Called when the node enters the scene tree for the first time.
func _ready():
	face.play("default")
	hand.play("default")
	
	nav_agent = $NavigationAgent3D
	player = get_node(player_path)
	nav_agent.set_target_position(waypoints[0].global_position)
	shield.visible = false
	
	#CHECKPOINT STUFF
	if yesdead == true:
		$MeshInstance3D/Bulletbox/CollisionShape3D.set_deferred("disabled", true)
		face.play("flash")
		await get_tree().create_timer(0.1).timeout
		face.play("scream")
		currentState = states.DIE
		$"Wander Timer".stop()
		anim.play("Death")
		$CollisionShape3D.set_deferred("disabled", true)
		$Launchbox/CollisionShape3D.set_deferred("disabled", true)
	else:
		pass


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	match currentState:
		states.WANDER:
			anim.play("Walk")
			if(nav_agent.is_navigation_finished()):
				currentState = states.WATCH
				cooing = true
				$"Wander Timer".start()
				return
			var targetPos = nav_agent.get_next_path_position()
			var direction = global_position.direction_to(targetPos)
			velocity = direction * WANDER_SPEED
			look_at(targetPos)
			if health == 1:
				face.play("death")
			else:
				face.play("default")
			move_and_slide()
		states.WATCH:
			anim.play("RESET")
			await get_tree().create_timer(1).timeout
			look_at(Vector3(player.global_position.x, global_position.y, player.global_position.z), Vector3.UP)
			if health == 2:
				face.play("look")
				coo.emit()
		states.DIE:
			pass
		states.LAUNCH:
			position += transform.basis * Vector3(0, 0, BULLET_SPEED) * delta
		states.STOP:
			pass
	
	if knockback_timer > 0.0:
		velocity = knockback
		knockback_timer -= delta
		if knockback_timer <= 0.0:
			knockback = Vector3.ZERO
	else:
		pass
	


func _on_wander_timer_timeout():
	cooing = false
	currentState = states.WANDER
	waypointIndex += 1
	if waypointIndex > waypoints.size() - 1:
		waypointIndex = 0
	nav_agent.set_target_position(waypoints[waypointIndex].global_position)


func _on_bulletbox_body_hit(dam):
	currentState = states.WANDER
	health -= dam
	if health < 2:
		face.play("death")
		$Cooing.stop()
		$Damage.play()
	if health <= 0 and yesdead == false:
		yesdead = true
		$MeshInstance3D/Bulletbox/CollisionShape3D.set_deferred("disabled", true)
		face.play("flash")
		await get_tree().create_timer(0.1).timeout
		face.play("scream")
		currentState = states.DIE
		$"Wander Timer".stop()
		anim.play("Death")
		$CollisionShape3D.set_deferred("disabled", true)
		$Launchbox/CollisionShape3D.set_deferred("disabled", true)
		dead.emit()

func apply_knockback(direction: Vector3, force: float, knockback_duration: float) -> void:
	knockback = direction * force
	knockback_timer = knockback_duration

func invincible():
	shield.visible = true
	$MeshInstance3D/Bulletbox/CollisionShape3D.set_deferred("disabled", true)


func _on_anti_virus_dead():
	shield.visible = false
	$MeshInstance3D/Bulletbox/CollisionShape3D.set_deferred("disabled", false)


func _on_coo():
	if cooing == true:
		cooing = false
		$Cooing.play()


func _on_launchbox_area_entered(area):
	if area.name == "Slide":
		$Launchbox/CollisionShape3D.set_deferred("disabled", true)
		$MeshInstance3D/Bulletbox/CollisionShape3D.set_deferred("disabled", true)
		$"Wander Timer".stop()
		currentState = states.WATCH
		dead.emit()
		launch.emit()
		await get_tree().create_timer(0.02).timeout
		queue_free()

Plus scene tree:

Which lines in that script are causing the errors?

Btw you should get rid of all awaits. Especially in _process()

The error stems from the level node, which houses the loading scripts. The reason I bring up this script is because they’re tied in some way. Or at least that’s the best way I can describe it. This is the script for the loading stuff in the level node:

extends Node

signal testingKills
signal testingTime

func _ready():
	testingKills.emit()
	testingTime.emit()

func _process(_delta):
	if Input.is_action_just_pressed("2"):
		if !FileAccess.file_exists("user://levelCheckpoint.tscn"):
			get_tree().reload_current_scene()
		else:
			var new_scene = ResourceLoader.load("user://levelCheckpoint.tscn").instantiate()
			get_tree().current_scene.queue_free()
			get_tree().root.add_child(new_scene)
			get_tree().current_scene = new_scene


func _on_battery_2_checkpoint():
	var node_to_save = self
	var scene = PackedScene.new()
	scene.pack(node_to_save)
	ResourceSaver.save(scene, "user://levelCheckpoint.tscn")
	CheckpointLoader.checkpoint_reached = true

The previous script, as well as the player script, come from separate scenes that are dropped into this main level node scene, like this:

The error is on line 15, or “var new_scene = ResourceLoader.load(“user://levelCheckpoint.tscn”).instantiate()”

Sorry if I’m being a bit difficult to work with, it’s been a while since I’ve used the forums, and i’ve been a bit stressed!

Also, thanks for the heads up on the awaits

Where are all those signals the engine is complaining about? In levelCheckpoint.tscn? Post the scene tree for that scene. How do you make those signal connections, in script or in editor?

Most of them are in the player script. The signal connections are made by making the signal via “signal slide_light” for example, typing out “slide_light.emit()” and then connecting it back into the main player node via the signals tab. Every signal is self-contained in the first main node.

Start by getting rid of all awaits.

Yea I should probably get to that. Brb

What should I replace them with? Timer nodes?

You can still use scene tree timers, just don’t await for their signals. timeout is a signal. Use signal handling functions instead.

Ah, ok. Sounds good! Why not use awaits, if you don’t mind me asking?

It’s very hard to mentally follow their execution, they tend to produce cryptic bugs and if you don’t fully understand how they work (i.e. what a coroutine is) they can badly mess up your code execution flow without you even realizing it.

Ignoring the awaits for now, I’ve found a temporary solution, but I still need some help. Setting the player scene to Make Local inside the main level scene fixes the issue, which is great!

However, this brings about another issue, where any edits I make to the individual player scene are not saved to the local one.

The problem probably roots from it being an instanced scene being packed up using PackedScenes

It’s not a question of when the signals are emitted, but of when they are connected.