1+ #include " stagehand/utilities/disconnect_node_signal_graph.h"
2+
3+ #include < unordered_set>
4+
5+ #include < godot_cpp/classes/object.hpp>
6+ #include < godot_cpp/variant/array.hpp>
7+ #include < godot_cpp/variant/callable.hpp>
8+ #include < godot_cpp/variant/dictionary.hpp>
9+
10+ namespace {
11+ using VisitedObjects = std::unordered_set<uint64_t >;
12+
13+ void cleanup_configuration_value_signals (const godot::Variant &value, VisitedObjects &visited_objects);
14+
15+ void disconnect_all_signal_connections (godot::Object *object) {
16+ if (object == nullptr ) {
17+ return ;
18+ }
19+
20+ const godot::TypedArray<godot::Dictionary> signal_list = object->get_signal_list ();
21+ for (int signal_index = 0 ; signal_index < signal_list.size (); ++signal_index) {
22+ const godot::Dictionary signal_info = signal_list[signal_index];
23+ const godot::Variant signal_name_variant = signal_info.get (" name" , godot::Variant ());
24+ if (signal_name_variant.get_type () != godot::Variant::STRING && signal_name_variant.get_type () != godot::Variant::STRING_NAME) {
25+ continue ;
26+ }
27+
28+ const godot::StringName signal_name = signal_name_variant;
29+ const godot::TypedArray<godot::Dictionary> connections = object->get_signal_connection_list (signal_name);
30+ for (int connection_index = 0 ; connection_index < connections.size (); ++connection_index) {
31+ const godot::Dictionary connection_info = connections[connection_index];
32+ const godot::Variant callable_variant = connection_info.get (" callable" , godot::Variant ());
33+ if (callable_variant.get_type () != godot::Variant::CALLABLE) {
34+ continue ;
35+ }
36+
37+ const godot::Callable callable = callable_variant;
38+ if (object->is_connected (signal_name, callable)) {
39+ object->disconnect (signal_name, callable);
40+ }
41+ }
42+ }
43+ }
44+
45+ void disconnect_signals_in_object (godot::Object *object, VisitedObjects &visited_objects) {
46+ if (object == nullptr ) {
47+ return ;
48+ }
49+
50+ const uint64_t instance_id = object->get_instance_id ();
51+ if (!visited_objects.insert (instance_id).second ) {
52+ return ;
53+ }
54+
55+ disconnect_all_signal_connections (object);
56+
57+ if (godot::Node *node = godot::Object::cast_to<godot::Node>(object)) {
58+ const godot::TypedArray<godot::Node> child_nodes = node->get_children ();
59+ for (int child_index = 0 ; child_index < child_nodes.size (); ++child_index) {
60+ disconnect_signals_in_object (godot::Object::cast_to<godot::Node>(child_nodes[child_index]), visited_objects);
61+ }
62+ }
63+
64+ const godot::TypedArray<godot::Dictionary> property_list = object->get_property_list ();
65+ for (int property_index = 0 ; property_index < property_list.size (); ++property_index) {
66+ const godot::Dictionary property_info = property_list[property_index];
67+ const uint32_t usage = static_cast <uint32_t >(property_info.get (" usage" , 0 ));
68+ if ((usage & (godot::PROPERTY_USAGE_STORAGE | godot::PROPERTY_USAGE_EDITOR)) == 0 ) {
69+ continue ;
70+ }
71+
72+ const godot::Variant property_name_variant = property_info.get (" name" , godot::Variant ());
73+ if (property_name_variant.get_type () != godot::Variant::STRING && property_name_variant.get_type () != godot::Variant::STRING_NAME) {
74+ continue ;
75+ }
76+
77+ const godot::StringName property_name = property_name_variant;
78+ if (property_name == godot::StringName (" script" )) {
79+ continue ;
80+ }
81+
82+ cleanup_configuration_value_signals (object->get (property_name), visited_objects);
83+ }
84+ }
85+
86+ void cleanup_configuration_value_signals (const godot::Variant &value, VisitedObjects &visited_objects) {
87+ switch (value.get_type ()) {
88+ case godot::Variant::OBJECT:
89+ disconnect_signals_in_object (value.get_validated_object (), visited_objects);
90+ break ;
91+ case godot::Variant::ARRAY: {
92+ const godot::Array array = value;
93+ for (int item_index = 0 ; item_index < array.size (); ++item_index) {
94+ cleanup_configuration_value_signals (array[item_index], visited_objects);
95+ }
96+ break ;
97+ }
98+ case godot::Variant::DICTIONARY: {
99+ const godot::Dictionary dictionary = value;
100+ const godot::Array keys = dictionary.keys ();
101+ for (int key_index = 0 ; key_index < keys.size (); ++key_index) {
102+ const godot::Variant key = keys[key_index];
103+ cleanup_configuration_value_signals (key, visited_objects);
104+ cleanup_configuration_value_signals (dictionary[key], visited_objects);
105+ }
106+ break ;
107+ }
108+ default :
109+ break ;
110+ }
111+ }
112+ } // namespace
113+
114+ void utilities::disconnect_node_signal_graph (godot::Node *root) {
115+ if (root == nullptr ) {
116+ return ;
117+ }
118+
119+ VisitedObjects visited_objects;
120+ const godot::TypedArray<godot::Node> child_nodes = root->get_children ();
121+ for (int child_index = 0 ; child_index < child_nodes.size (); ++child_index) {
122+ disconnect_signals_in_object (godot::Object::cast_to<godot::Node>(child_nodes[child_index]), visited_objects);
123+ }
124+ }
0 commit comments