Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/debugger/scene_debugger_object.cpp
20934 views
1
/**************************************************************************/
2
/* scene_debugger_object.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#ifdef DEBUG_ENABLED
32
33
#include "scene_debugger_object.h"
34
35
#include "core/io/marshalls.h"
36
#include "core/object/script_language.h"
37
38
SceneDebuggerObject::SceneDebuggerObject(Object *p_obj) {
39
if (!p_obj) {
40
return;
41
}
42
43
id = p_obj->get_instance_id();
44
class_name = p_obj->get_class();
45
46
if (ScriptInstance *si = p_obj->get_script_instance()) {
47
// Read script instance constants and variables.
48
if (!si->get_script().is_null()) {
49
Script *s = si->get_script().ptr();
50
_parse_script_properties(s, si);
51
}
52
}
53
54
if (Node *node = Object::cast_to<Node>(p_obj)) {
55
// For debugging multiplayer.
56
{
57
PropertyInfo pi(Variant::INT, String("Node/multiplayer_authority"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY);
58
properties.push_back(SceneDebuggerProperty(pi, node->get_multiplayer_authority()));
59
}
60
61
// Add specialized NodePath info (if inside tree).
62
if (node->is_inside_tree()) {
63
PropertyInfo pi(Variant::NODE_PATH, String("Node/path"));
64
properties.push_back(SceneDebuggerProperty(pi, node->get_path()));
65
} else { // Can't ask for path if a node is not in tree.
66
PropertyInfo pi(Variant::STRING, String("Node/path"));
67
properties.push_back(SceneDebuggerProperty(pi, "[Orphan]"));
68
}
69
} else if (Script *s = Object::cast_to<Script>(p_obj)) {
70
// Add script constants (no instance).
71
_parse_script_properties(s, nullptr);
72
}
73
74
// Add base object properties.
75
List<PropertyInfo> pinfo;
76
p_obj->get_property_list(&pinfo, true);
77
for (const PropertyInfo &E : pinfo) {
78
if (E.usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) {
79
properties.push_back(SceneDebuggerProperty(E, p_obj->get(E.name)));
80
}
81
}
82
}
83
84
void SceneDebuggerObject::_parse_script_properties(Script *p_script, ScriptInstance *p_instance) {
85
typedef HashMap<const Script *, HashSet<StringName>> ScriptMemberMap;
86
typedef HashMap<const Script *, HashMap<StringName, Variant>> ScriptConstantsMap;
87
88
ScriptMemberMap members;
89
if (p_instance) {
90
members[p_script] = HashSet<StringName>();
91
p_script->get_members(&(members[p_script]));
92
}
93
94
ScriptConstantsMap constants;
95
constants[p_script] = HashMap<StringName, Variant>();
96
p_script->get_constants(&(constants[p_script]));
97
98
Ref<Script> base = p_script->get_base_script();
99
while (base.is_valid()) {
100
if (p_instance) {
101
members[base.ptr()] = HashSet<StringName>();
102
base->get_members(&(members[base.ptr()]));
103
}
104
105
constants[base.ptr()] = HashMap<StringName, Variant>();
106
base->get_constants(&(constants[base.ptr()]));
107
108
base = base->get_base_script();
109
}
110
111
HashSet<String> exported_members;
112
HashMap<String, PropertyInfo> non_exported_members;
113
114
if (p_instance) {
115
List<PropertyInfo> pinfo;
116
p_instance->get_property_list(&pinfo);
117
for (const PropertyInfo &E : pinfo) {
118
if (E.usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) {
119
exported_members.insert(E.name);
120
} else {
121
PropertyInfo pi = E;
122
pi.usage |= PROPERTY_USAGE_EDITOR;
123
non_exported_members.insert(E.name, pi);
124
}
125
}
126
}
127
128
// Members
129
for (KeyValue<const Script *, HashSet<StringName>> sm : members) {
130
for (const StringName &E : sm.value) {
131
if (exported_members.has(E)) {
132
continue; // Exported variables already show up in the inspector.
133
}
134
if (String(E).begins_with("@")) {
135
continue; // Skip groups.
136
}
137
138
Variant m;
139
if (p_instance->get(E, m)) {
140
const String script_path = sm.key == p_script ? "" : sm.key->get_path().get_file() + "/";
141
142
PropertyInfo pi;
143
const PropertyInfo *pi_ptr = non_exported_members.getptr(E);
144
if (pi_ptr == nullptr) {
145
pi.type = m.get_type();
146
} else {
147
pi = *pi_ptr;
148
}
149
pi.name = "Members/" + script_path + E;
150
151
properties.push_back(SceneDebuggerProperty(pi, m));
152
}
153
}
154
}
155
// Constants
156
for (KeyValue<const Script *, HashMap<StringName, Variant>> &sc : constants) {
157
for (const KeyValue<StringName, Variant> &E : sc.value) {
158
String script_path = sc.key == p_script ? "" : sc.key->get_path().get_file() + "/";
159
if (E.value.get_type() == Variant::OBJECT) {
160
Variant inst_id = ((Object *)E.value)->get_instance_id();
161
PropertyInfo pi(inst_id.get_type(), "Constants/" + E.key, PROPERTY_HINT_OBJECT_ID, "Object");
162
properties.push_back(SceneDebuggerProperty(pi, inst_id));
163
} else {
164
PropertyInfo pi(E.value.get_type(), "Constants/" + script_path + E.key);
165
properties.push_back(SceneDebuggerProperty(pi, E.value));
166
}
167
}
168
}
169
}
170
171
void SceneDebuggerObject::serialize(Array &r_arr, int p_max_size) {
172
Array send_props;
173
for (SceneDebuggerProperty &property : properties) {
174
const PropertyInfo &pi = property.first;
175
Variant &var = property.second;
176
177
Ref<Resource> res = var;
178
179
Array prop = { pi.name, pi.type };
180
PropertyHint hint = pi.hint;
181
String hint_string = pi.hint_string;
182
if (res.is_valid() && !res->get_path().is_empty()) {
183
var = res->get_path();
184
} else { //only send information that can be sent..
185
int len = 0; //test how big is this to encode
186
encode_variant(var, nullptr, len);
187
if (len > p_max_size) { //limit to max size
188
hint = PROPERTY_HINT_OBJECT_TOO_BIG;
189
hint_string = "";
190
var = Variant();
191
}
192
}
193
prop.push_back(hint);
194
prop.push_back(hint_string);
195
prop.push_back(pi.usage);
196
prop.push_back(var);
197
send_props.push_back(prop);
198
}
199
r_arr.push_back(uint64_t(id));
200
r_arr.push_back(class_name);
201
r_arr.push_back(send_props);
202
}
203
204
#define CHECK_TYPE(p_what, p_type) ERR_FAIL_COND(p_what.get_type() != Variant::p_type)
205
206
void SceneDebuggerObject::deserialize(const Array &p_arr) {
207
ERR_FAIL_COND(p_arr.size() < 3);
208
CHECK_TYPE(p_arr[0], INT);
209
CHECK_TYPE(p_arr[1], STRING);
210
CHECK_TYPE(p_arr[2], ARRAY);
211
212
deserialize(uint64_t(p_arr[0]), p_arr[1], p_arr[2]);
213
}
214
215
void SceneDebuggerObject::deserialize(uint64_t p_id, const String &p_class_name, const Array &p_props) {
216
id = p_id;
217
class_name = p_class_name;
218
219
for (int i = 0; i < p_props.size(); i++) {
220
CHECK_TYPE(p_props[i], ARRAY);
221
Array prop = p_props[i];
222
223
ERR_FAIL_COND(prop.size() != 6);
224
CHECK_TYPE(prop[0], STRING);
225
CHECK_TYPE(prop[1], INT);
226
CHECK_TYPE(prop[2], INT);
227
CHECK_TYPE(prop[3], STRING);
228
CHECK_TYPE(prop[4], INT);
229
230
PropertyInfo pinfo;
231
pinfo.name = prop[0];
232
pinfo.type = Variant::Type(int(prop[1]));
233
pinfo.hint = PropertyHint(int(prop[2]));
234
pinfo.hint_string = prop[3];
235
pinfo.usage = PropertyUsageFlags(int(prop[4]));
236
Variant var = prop[5];
237
238
if (pinfo.type == Variant::OBJECT) {
239
if (var.is_zero()) {
240
var = Ref<Resource>();
241
} else if (var.get_type() == Variant::OBJECT) {
242
if (((Object *)var)->is_class("EncodedObjectAsID")) {
243
var = Object::cast_to<EncodedObjectAsID>(var)->get_object_id();
244
pinfo.type = var.get_type();
245
pinfo.hint = PROPERTY_HINT_OBJECT_ID;
246
pinfo.hint_string = "Object";
247
}
248
}
249
}
250
properties.push_back(SceneDebuggerProperty(pinfo, var));
251
}
252
}
253
254
SceneDebuggerTree::SceneDebuggerTree(Node *p_root) {
255
// Flatten tree into list, depth first, use stack to avoid recursion.
256
List<Node *> stack;
257
stack.push_back(p_root);
258
bool is_root = true;
259
const StringName &is_visible_sn = SNAME("is_visible");
260
const StringName &is_visible_in_tree_sn = SNAME("is_visible_in_tree");
261
while (stack.size()) {
262
Node *n = stack.front()->get();
263
stack.pop_front();
264
265
int count = n->get_child_count();
266
for (int i = 0; i < count; i++) {
267
stack.push_front(n->get_child(count - i - 1));
268
}
269
270
int view_flags = 0;
271
if (is_root) {
272
// Prevent root window visibility from being changed.
273
is_root = false;
274
} else if (n->has_method(is_visible_sn)) {
275
const Variant visible = n->call(is_visible_sn);
276
if (visible.get_type() == Variant::BOOL) {
277
view_flags = RemoteNode::VIEW_HAS_VISIBLE_METHOD;
278
view_flags |= uint8_t(visible) * RemoteNode::VIEW_VISIBLE;
279
}
280
if (n->has_method(is_visible_in_tree_sn)) {
281
const Variant visible_in_tree = n->call(is_visible_in_tree_sn);
282
if (visible_in_tree.get_type() == Variant::BOOL) {
283
view_flags |= uint8_t(visible_in_tree) * RemoteNode::VIEW_VISIBLE_IN_TREE;
284
}
285
}
286
}
287
288
String class_name;
289
ScriptInstance *script_instance = n->get_script_instance();
290
if (script_instance) {
291
Ref<Script> script = script_instance->get_script();
292
if (script.is_valid()) {
293
class_name = script->get_global_name();
294
295
if (class_name.is_empty()) {
296
// If there is no class_name in this script we just take the script path.
297
class_name = script->get_path();
298
}
299
}
300
}
301
nodes.push_back(RemoteNode(count, n->get_name(), class_name.is_empty() ? n->get_class() : class_name, n->get_instance_id(), n->get_scene_file_path(), view_flags));
302
}
303
}
304
305
void SceneDebuggerTree::serialize(Array &p_arr) {
306
for (const RemoteNode &n : nodes) {
307
p_arr.push_back(n.child_count);
308
p_arr.push_back(n.name);
309
p_arr.push_back(n.type_name);
310
p_arr.push_back(n.id);
311
p_arr.push_back(n.scene_file_path);
312
p_arr.push_back(n.view_flags);
313
}
314
}
315
316
void SceneDebuggerTree::deserialize(const Array &p_arr) {
317
int idx = 0;
318
while (p_arr.size() > idx) {
319
ERR_FAIL_COND(p_arr.size() < 6);
320
CHECK_TYPE(p_arr[idx], INT); // child_count.
321
CHECK_TYPE(p_arr[idx + 1], STRING); // name.
322
CHECK_TYPE(p_arr[idx + 2], STRING); // type_name.
323
CHECK_TYPE(p_arr[idx + 3], INT); // id.
324
CHECK_TYPE(p_arr[idx + 4], STRING); // scene_file_path.
325
CHECK_TYPE(p_arr[idx + 5], INT); // view_flags.
326
nodes.push_back(RemoteNode(p_arr[idx], p_arr[idx + 1], p_arr[idx + 2], p_arr[idx + 3], p_arr[idx + 4], p_arr[idx + 5]));
327
idx += 6;
328
}
329
}
330
331
#undef CHECK_TYPE
332
333
#endif // DEBUG_ENABLED
334
335