Преглед изворни кода

Fixes to allow object-less callables throughout Godot

This fixes #81887
Mai Lavelle пре 1 година
родитељ
комит
5e15586ec2

+ 1 - 2
core/core_bind.cpp

@@ -1211,8 +1211,7 @@ void Thread::_start_func(void *ud) {
 	Ref<Thread> t = *tud;
 	memdelete(tud);
 
-	Object *target_instance = t->target_callable.get_object();
-	if (!target_instance) {
+	if (!t->target_callable.is_valid()) {
 		t->running.clear();
 		ERR_FAIL_MSG(vformat("Could not call function '%s' on previously freed instance to start thread %s.", t->target_callable.get_method(), t->get_id()));
 	}

+ 23 - 10
core/object/object.cpp

@@ -1109,8 +1109,7 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
 	Error err = OK;
 
 	for (const Connection &c : slot_conns) {
-		Object *target = c.callable.get_object();
-		if (!target) {
+		if (!c.callable.is_valid()) {
 			// Target might have been deleted during signal callback, this is expected and OK.
 			continue;
 		}
@@ -1133,7 +1132,8 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
 					continue;
 				}
 #endif
-				if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
+				Object *target = c.callable.get_object();
+				if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && target && !ClassDB::class_exists(target->get_class_name())) {
 					//most likely object is not initialized yet, do not throw error.
 				} else {
 					ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(c.callable, args, argc, ce) + ".");
@@ -1313,8 +1313,14 @@ void Object::get_signals_connected_to_this(List<Connection> *p_connections) cons
 Error Object::connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) {
 	ERR_FAIL_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is null.");
 
-	Object *target_object = p_callable.get_object();
-	ERR_FAIL_NULL_V_MSG(target_object, ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null.");
+	if (p_callable.is_standard()) {
+		// FIXME: This branch should probably removed in favor of the `is_valid()` branch, but there exist some classes
+		// that call `connect()` before they are fully registered with ClassDB. Until all such classes can be found
+		// and registered soon enough this branch is needed to allow `connect()` to succeed.
+		ERR_FAIL_NULL_V_MSG(p_callable.get_object(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null.");
+	} else {
+		ERR_FAIL_COND_V_MSG(!p_callable.is_valid(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is not valid: " + p_callable);
+	}
 
 	SignalData *s = signal_map.getptr(p_signal);
 	if (!s) {
@@ -1352,6 +1358,8 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
 		}
 	}
 
+	Object *target_object = p_callable.get_object();
+
 	SignalData::Slot slot;
 
 	Connection conn;
@@ -1359,7 +1367,9 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
 	conn.signal = ::Signal(this, p_signal);
 	conn.flags = p_flags;
 	slot.conn = conn;
-	slot.cE = target_object->connections.push_back(conn);
+	if (target_object) {
+		slot.cE = target_object->connections.push_back(conn);
+	}
 	if (p_flags & CONNECT_REFERENCE_COUNTED) {
 		slot.reference_count = 1;
 	}
@@ -1398,9 +1408,6 @@ void Object::disconnect(const StringName &p_signal, const Callable &p_callable)
 bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) {
 	ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot disconnect from '" + p_signal + "': the provided callable is null.");
 
-	Object *target_object = p_callable.get_object();
-	ERR_FAIL_NULL_V_MSG(target_object, false, "Cannot disconnect '" + p_signal + "' from callable '" + p_callable + "': the callable object is null.");
-
 	SignalData *s = signal_map.getptr(p_signal);
 	if (!s) {
 		bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal) ||
@@ -1420,7 +1427,13 @@ bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable,
 		}
 	}
 
-	target_object->connections.erase(slot->cE);
+	if (slot->cE) {
+		Object *target_object = p_callable.get_object();
+		if (target_object) {
+			target_object->connections.erase(slot->cE);
+		}
+	}
+
 	s->slot_map.erase(*p_callable.get_base_comparator());
 
 	if (s->slot_map.is_empty() && ClassDB::has_signal(get_class_name(), p_signal)) {

+ 16 - 6
core/object/undo_redo.cpp

@@ -136,17 +136,22 @@ void UndoRedo::add_do_method(const Callable &p_callable) {
 	ERR_FAIL_COND(action_level <= 0);
 	ERR_FAIL_COND((current_action + 1) >= actions.size());
 
-	Object *object = p_callable.get_object();
-	ERR_FAIL_NULL(object);
+	ObjectID object_id = p_callable.get_object_id();
+	Object *object = ObjectDB::get_instance(object_id);
+	ERR_FAIL_COND(object_id.is_valid() && object == nullptr);
 
 	Operation do_op;
 	do_op.callable = p_callable;
-	do_op.object = p_callable.get_object_id();
+	do_op.object = object_id;
 	if (Object::cast_to<RefCounted>(object)) {
 		do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object));
 	}
 	do_op.type = Operation::TYPE_METHOD;
 	do_op.name = p_callable.get_method();
+	if (do_op.name == StringName()) {
+		// There's no `get_method()` for custom callables, so use `operator String()` instead.
+		do_op.name = static_cast<String>(p_callable);
+	}
 
 	actions.write[current_action + 1].do_ops.push_back(do_op);
 }
@@ -161,18 +166,23 @@ void UndoRedo::add_undo_method(const Callable &p_callable) {
 		return;
 	}
 
-	Object *object = p_callable.get_object();
-	ERR_FAIL_NULL(object);
+	ObjectID object_id = p_callable.get_object_id();
+	Object *object = ObjectDB::get_instance(object_id);
+	ERR_FAIL_COND(object_id.is_valid() && object == nullptr);
 
 	Operation undo_op;
 	undo_op.callable = p_callable;
-	undo_op.object = p_callable.get_object_id();
+	undo_op.object = object_id;
 	if (Object::cast_to<RefCounted>(object)) {
 		undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object));
 	}
 	undo_op.type = Operation::TYPE_METHOD;
 	undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends;
 	undo_op.name = p_callable.get_method();
+	if (undo_op.name == StringName()) {
+		// There's no `get_method()` for custom callables, so use `operator String()` instead.
+		undo_op.name = static_cast<String>(p_callable);
+	}
 
 	actions.write[current_action + 1].undo_ops.push_back(undo_op);
 }

+ 7 - 0
core/variant/callable.cpp

@@ -47,6 +47,13 @@ void Callable::callp(const Variant **p_arguments, int p_argcount, Variant &r_ret
 		r_call_error.expected = 0;
 		r_return_value = Variant();
 	} else if (is_custom()) {
+		if (!is_valid()) {
+			r_call_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL;
+			r_call_error.argument = 0;
+			r_call_error.expected = 0;
+			r_return_value = Variant();
+			return;
+		}
 		custom->call(p_arguments, p_argcount, r_return_value, r_call_error);
 	} else {
 		Object *obj = ObjectDB::get_instance(ObjectID(object));

+ 2 - 2
scene/animation/tween.cpp

@@ -675,7 +675,7 @@ bool CallbackTweener::step(double &r_delta) {
 		return false;
 	}
 
-	if (!callback.get_object()) {
+	if (!callback.is_valid()) {
 		return false;
 	}
 
@@ -740,7 +740,7 @@ bool MethodTweener::step(double &r_delta) {
 		return false;
 	}
 
-	if (!callback.get_object()) {
+	if (!callback.is_valid()) {
 		return false;
 	}
 

+ 0 - 14
servers/physics_2d/godot_area_2d.cpp

@@ -78,13 +78,6 @@ void GodotArea2D::set_space(GodotSpace2D *p_space) {
 }
 
 void GodotArea2D::set_monitor_callback(const Callable &p_callback) {
-	ObjectID id = p_callback.get_object_id();
-
-	if (id == monitor_callback.get_object_id()) {
-		monitor_callback = p_callback;
-		return;
-	}
-
 	_unregister_shapes();
 
 	monitor_callback = p_callback;
@@ -100,13 +93,6 @@ void GodotArea2D::set_monitor_callback(const Callable &p_callback) {
 }
 
 void GodotArea2D::set_area_monitor_callback(const Callable &p_callback) {
-	ObjectID id = p_callback.get_object_id();
-
-	if (id == area_monitor_callback.get_object_id()) {
-		area_monitor_callback = p_callback;
-		return;
-	}
-
 	_unregister_shapes();
 
 	area_monitor_callback = p_callback;

+ 4 - 4
servers/physics_2d/godot_body_2d.cpp

@@ -615,7 +615,7 @@ void GodotBody2D::integrate_velocities(real_t p_step) {
 		return;
 	}
 
-	if (fi_callback_data || body_state_callback.get_object()) {
+	if (fi_callback_data || body_state_callback.is_valid()) {
 		get_space()->body_add_to_state_query_list(&direct_state_query_list);
 	}
 
@@ -676,7 +676,7 @@ void GodotBody2D::call_queries() {
 	Variant direct_state_variant = get_direct_state();
 
 	if (fi_callback_data) {
-		if (!fi_callback_data->callable.get_object()) {
+		if (!fi_callback_data->callable.is_valid()) {
 			set_force_integration_callback(Callable());
 		} else {
 			const Variant *vp[2] = { &direct_state_variant, &fi_callback_data->udata };
@@ -692,7 +692,7 @@ void GodotBody2D::call_queries() {
 		}
 	}
 
-	if (body_state_callback.get_object()) {
+	if (body_state_callback.is_valid()) {
 		body_state_callback.call(direct_state_variant);
 	}
 }
@@ -719,7 +719,7 @@ void GodotBody2D::set_state_sync_callback(const Callable &p_callable) {
 }
 
 void GodotBody2D::set_force_integration_callback(const Callable &p_callable, const Variant &p_udata) {
-	if (p_callable.get_object()) {
+	if (p_callable.is_valid()) {
 		if (!fi_callback_data) {
 			fi_callback_data = memnew(ForceIntegrationCallbackData);
 		}

+ 0 - 12
servers/physics_3d/godot_area_3d.cpp

@@ -87,12 +87,6 @@ void GodotArea3D::set_space(GodotSpace3D *p_space) {
 }
 
 void GodotArea3D::set_monitor_callback(const Callable &p_callback) {
-	ObjectID id = p_callback.get_object_id();
-	if (id == monitor_callback.get_object_id()) {
-		monitor_callback = p_callback;
-		return;
-	}
-
 	_unregister_shapes();
 
 	monitor_callback = p_callback;
@@ -108,12 +102,6 @@ void GodotArea3D::set_monitor_callback(const Callable &p_callback) {
 }
 
 void GodotArea3D::set_area_monitor_callback(const Callable &p_callback) {
-	ObjectID id = p_callback.get_object_id();
-	if (id == area_monitor_callback.get_object_id()) {
-		area_monitor_callback = p_callback;
-		return;
-	}
-
 	_unregister_shapes();
 
 	area_monitor_callback = p_callback;

+ 4 - 4
servers/physics_3d/godot_body_3d.cpp

@@ -674,7 +674,7 @@ void GodotBody3D::integrate_velocities(real_t p_step) {
 		return;
 	}
 
-	if (fi_callback_data || body_state_callback.get_object()) {
+	if (fi_callback_data || body_state_callback.is_valid()) {
 		get_space()->body_add_to_state_query_list(&direct_state_query_list);
 	}
 
@@ -759,7 +759,7 @@ void GodotBody3D::call_queries() {
 	Variant direct_state_variant = get_direct_state();
 
 	if (fi_callback_data) {
-		if (!fi_callback_data->callable.get_object()) {
+		if (!fi_callback_data->callable.is_valid()) {
 			set_force_integration_callback(Callable());
 		} else {
 			const Variant *vp[2] = { &direct_state_variant, &fi_callback_data->udata };
@@ -771,7 +771,7 @@ void GodotBody3D::call_queries() {
 		}
 	}
 
-	if (body_state_callback.get_object()) {
+	if (body_state_callback.is_valid()) {
 		body_state_callback.call(direct_state_variant);
 	}
 }
@@ -798,7 +798,7 @@ void GodotBody3D::set_state_sync_callback(const Callable &p_callable) {
 }
 
 void GodotBody3D::set_force_integration_callback(const Callable &p_callable, const Variant &p_udata) {
-	if (p_callable.get_object()) {
+	if (p_callable.is_valid()) {
 		if (!fi_callback_data) {
 			fi_callback_data = memnew(ForceIntegrationCallbackData);
 		}