godot_bevy/plugins/
debugger.rs1use bevy_app::{App, Plugin, Update};
7use bevy_ecs::prelude::{Name, Resource, World};
8use bevy_ecs::world::EntityRef;
9use bevy_reflect::{PartialReflect, ReflectFromPtr, ReflectRef};
10use bevy_time::Time;
11use godot::classes::EngineDebugger;
12use godot::meta::ToGodot;
13use godot::prelude::{VarDictionary as Dictionary, *};
14
15use crate::interop::GodotNodeHandle;
16use crate::plugins::scene_tree::GodotChildOf;
17use bevy_ecs::reflect::AppTypeRegistry;
18
19#[derive(Resource)]
21pub struct DebuggerConfig {
22 pub enabled: bool,
24 pub update_interval: f32,
26}
27
28impl Default for DebuggerConfig {
29 fn default() -> Self {
30 Self {
31 enabled: true,
32 update_interval: 0.5, }
34 }
35}
36
37#[derive(Resource, Default)]
39struct DebuggerTimer {
40 elapsed: f32,
41}
42
43#[derive(Default)]
45pub struct GodotDebuggerPlugin;
46
47impl Plugin for GodotDebuggerPlugin {
48 fn build(&self, app: &mut App) {
49 app.init_resource::<DebuggerConfig>()
50 .init_resource::<DebuggerTimer>()
51 .add_systems(Update, debugger_exclusive_system);
52 }
53}
54
55fn debugger_exclusive_system(world: &mut World) {
56 let config = world.get_resource::<DebuggerConfig>();
57 let enabled = config.map(|c| c.enabled).unwrap_or(false);
58 let update_interval = config.map(|c| c.update_interval).unwrap_or(0.5);
59
60 if !enabled {
61 return;
62 }
63
64 let delta = world
65 .get_resource::<Time>()
66 .map(|t| t.delta_secs())
67 .unwrap_or(0.0);
68
69 let should_send = {
70 let mut timer = world.get_resource_mut::<DebuggerTimer>();
71 if let Some(ref mut timer) = timer {
72 timer.elapsed += delta;
73 if timer.elapsed < update_interval {
74 false
75 } else {
76 timer.elapsed = 0.0;
77 true
78 }
79 } else {
80 false
81 }
82 };
83
84 if !should_send {
85 return;
86 }
87
88 if !EngineDebugger::singleton().is_active() {
89 return;
90 }
91
92 let type_registry = world.get_resource::<AppTypeRegistry>().cloned();
94
95 let mut entities = VarArray::new();
96 let mut query = world.query::<EntityRef>();
97
98 for entity_ref in query.iter(world) {
99 let name = entity_ref
100 .get::<Name>()
101 .map(|n| n.as_str().to_string())
102 .unwrap_or_default();
103
104 let has_godot_node = entity_ref.get::<GodotNodeHandle>().is_some();
105
106 let parent_bits: i64 = entity_ref
107 .get::<GodotChildOf>()
108 .map(|child_of| child_of.get().to_bits() as i64)
109 .unwrap_or(-1);
110
111 let mut components = VarArray::new();
113 let archetype = entity_ref.archetype();
114
115 for component_id in archetype.components() {
116 let Some(component_info) = world.components().get_info(*component_id) else {
117 continue;
118 };
119
120 let mut component_dict = Dictionary::new();
121
122 let (full_name, short_name) = if let Some(ref registry) = type_registry {
124 let registry = registry.read();
125 if let Some(type_id) = component_info.type_id() {
126 if let Some(registration) = registry.get(type_id) {
127 let type_info = registration.type_info();
128 let table = type_info.type_path_table();
129 (table.path().to_string(), table.short_path().to_string())
130 } else {
131 extract_short_name(component_info.name().to_string())
132 }
133 } else {
134 extract_short_name(component_info.name().to_string())
135 }
136 } else {
137 extract_short_name(component_info.name().to_string())
138 };
139
140 if short_name == "GodotChildOf"
142 || short_name == "GodotChildren"
143 || full_name.contains("::GodotChildOf")
144 || full_name.contains("::GodotChildren")
145 {
146 continue;
147 }
148
149 component_dict.set("name", GString::from(&full_name));
150 component_dict.set("short_name", GString::from(&short_name));
151
152 if let Some(ref registry) = type_registry {
154 let registry = registry.read();
155 if let Some(type_id) = component_info.type_id()
156 && let Some(registration) = registry.get(type_id)
157 && let Some(reflect_from_ptr) = registration.data::<ReflectFromPtr>()
158 && let Ok(ptr) = entity_ref.get_by_id(*component_id)
159 {
160 let reflected = unsafe { reflect_from_ptr.as_reflect(ptr) };
162 let value_dict = reflect_to_dict(reflected);
163 component_dict.set("value", value_dict);
164 }
165 }
166
167 components.push(&component_dict.to_variant());
168 }
169
170 let mut entry = VarArray::new();
171 entry.push(&Variant::from(entity_ref.id().to_bits() as i64));
172 entry.push(&Variant::from(GString::from(&name)));
173 entry.push(&Variant::from(has_godot_node));
174 entry.push(&Variant::from(parent_bits));
175 entry.push(&components.to_variant());
176
177 entities.push(&entry.to_variant());
178 }
179
180 let mut debugger = EngineDebugger::singleton();
181 let message: GString = "bevy:entities".into();
182 debugger.send_message(&message, &entities);
183}
184
185fn extract_short_name(full_name: String) -> (String, String) {
187 let short = if let Some(pos) = full_name.rfind("::") {
188 full_name[pos + 2..].to_string()
189 } else {
190 full_name.clone()
191 };
192 (full_name, short)
193}
194
195fn reflect_to_dict(value: &dyn PartialReflect) -> Dictionary {
197 let mut dict = Dictionary::new();
198
199 match value.reflect_ref() {
200 ReflectRef::Struct(s) => {
201 dict.set("type", "struct");
202 let mut fields = Dictionary::new();
203 for i in 0..s.field_len() {
204 if let Some(field_name) = s.name_at(i)
205 && let Some(field_value) = s.field_at(i)
206 {
207 fields.set(field_name, reflect_value_to_variant(field_value));
208 }
209 }
210 dict.set("fields", fields);
211 }
212 ReflectRef::TupleStruct(ts) => {
213 dict.set("type", "tuple_struct");
214 let mut fields = VarArray::new();
215 for i in 0..ts.field_len() {
216 if let Some(field_value) = ts.field(i) {
217 fields.push(&reflect_value_to_variant(field_value));
218 }
219 }
220 dict.set("fields", fields);
221 }
222 ReflectRef::Tuple(t) => {
223 dict.set("type", "tuple");
224 let mut fields = VarArray::new();
225 for i in 0..t.field_len() {
226 if let Some(field_value) = t.field(i) {
227 fields.push(&reflect_value_to_variant(field_value));
228 }
229 }
230 dict.set("fields", fields);
231 }
232 ReflectRef::List(l) => {
233 dict.set("type", "list");
234 let mut items = VarArray::new();
235 for i in 0..l.len() {
236 if let Some(item) = l.get(i) {
237 items.push(&reflect_value_to_variant(item));
238 }
239 }
240 dict.set("items", items);
241 }
242 ReflectRef::Map(m) => {
243 dict.set("type", "map");
244 dict.set("len", m.len() as i64);
245 }
246 ReflectRef::Set(s) => {
247 dict.set("type", "set");
248 dict.set("len", s.len() as i64);
249 }
250 ReflectRef::Enum(e) => {
251 dict.set("type", "enum");
252 dict.set("variant", e.variant_name());
253 let mut fields = Dictionary::new();
254 for i in 0..e.field_len() {
255 let field_name = e
256 .name_at(i)
257 .map(|s| s.to_string())
258 .unwrap_or_else(|| i.to_string());
259 if let Some(field_value) = e.field_at(i) {
260 fields.set(field_name, reflect_value_to_variant(field_value));
261 }
262 }
263 if !fields.is_empty() {
264 dict.set("fields", fields);
265 }
266 }
267 ReflectRef::Array(a) => {
268 dict.set("type", "array");
269 let mut items = VarArray::new();
270 for i in 0..a.len() {
271 if let Some(item) = a.get(i) {
272 items.push(&reflect_value_to_variant(item));
273 }
274 }
275 dict.set("items", items);
276 }
277 ReflectRef::Opaque(_) => {
278 dict.set("type", "opaque");
279 if let Some(debug_str) = value.try_as_reflect().map(|r| format!("{r:?}")) {
281 dict.set("debug", GString::from(&debug_str));
282 }
283 }
284 }
285
286 dict
287}
288
289fn reflect_value_to_variant(value: &dyn PartialReflect) -> Variant {
291 if let Some(v) = value.try_downcast_ref::<f32>() {
293 return Variant::from(*v);
294 }
295 if let Some(v) = value.try_downcast_ref::<f64>() {
296 return Variant::from(*v);
297 }
298 if let Some(v) = value.try_downcast_ref::<i32>() {
299 return Variant::from(*v);
300 }
301 if let Some(v) = value.try_downcast_ref::<i64>() {
302 return Variant::from(*v);
303 }
304 if let Some(v) = value.try_downcast_ref::<u32>() {
305 return Variant::from(*v as i64);
306 }
307 if let Some(v) = value.try_downcast_ref::<u64>() {
308 return Variant::from(*v as i64);
309 }
310 if let Some(v) = value.try_downcast_ref::<bool>() {
311 return Variant::from(*v);
312 }
313 if let Some(v) = value.try_downcast_ref::<String>() {
314 return Variant::from(GString::from(v));
315 }
316 if let Some(v) = value.try_downcast_ref::<&str>() {
317 return Variant::from(GString::from(*v));
318 }
319
320 reflect_to_dict(value).to_variant()
322}