@satelliteoflove/godot-mcp 3.23.0 → 3.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -13,8 +13,10 @@ var _active: bool = false
|
|
|
13
13
|
var _specs: Array = [] # [{node, fields: [{key, resolver}]}]
|
|
14
14
|
var _hz: int = 20
|
|
15
15
|
var _duration_ms: int = 1000
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
# GAME time accumulated (unpaused, time_scale-scaled), NOT wall clock: under a
|
|
17
|
+
# godot_game_time freeze the tree is paused between steps, and counting wall time
|
|
18
|
+
# there would run the window down / log stale samples before the step that moves.
|
|
19
|
+
var _elapsed_ms: float = 0.0
|
|
18
20
|
var _frame_index: int = 0
|
|
19
21
|
var _sample_interval: int = 1 # sample every N frames
|
|
20
22
|
var _samples: Dictionary = {} # field_key -> Array of {t_ms, value}
|
|
@@ -41,7 +43,7 @@ func start(specs: Array, hz: int, duration_ms: int, signal_specs: Array = []) ->
|
|
|
41
43
|
_field_truncated = {}
|
|
42
44
|
_hz = clampi(hz, 1, 60)
|
|
43
45
|
_duration_ms = clampi(duration_ms, 100, 5000)
|
|
44
|
-
|
|
46
|
+
_elapsed_ms = 0.0
|
|
45
47
|
_frame_index = 0
|
|
46
48
|
_sample_interval = max(1, int(Engine.get_frames_per_second() / _hz)) if Engine.get_frames_per_second() > 0 else max(1, int(60.0 / _hz))
|
|
47
49
|
|
|
@@ -70,7 +72,6 @@ func start(specs: Array, hz: int, duration_ms: int, signal_specs: Array = []) ->
|
|
|
70
72
|
if not resolved_fields.is_empty():
|
|
71
73
|
_specs.append({"node": node, "node_path": node_path, "fields": resolved_fields})
|
|
72
74
|
|
|
73
|
-
_stop_time = 0
|
|
74
75
|
_active = true
|
|
75
76
|
set_process(true)
|
|
76
77
|
|
|
@@ -174,7 +175,7 @@ func _record_event(source: String, sig_name: String, args: Array) -> void:
|
|
|
174
175
|
_events_truncated = true
|
|
175
176
|
return
|
|
176
177
|
var ev := {
|
|
177
|
-
"t_ms":
|
|
178
|
+
"t_ms": int(_elapsed_ms),
|
|
178
179
|
"source": source,
|
|
179
180
|
"signal": sig_name,
|
|
180
181
|
}
|
|
@@ -217,18 +218,26 @@ func _exit_tree() -> void:
|
|
|
217
218
|
_disconnect_all()
|
|
218
219
|
|
|
219
220
|
|
|
220
|
-
func _process(
|
|
221
|
+
func _process(delta: float) -> void:
|
|
221
222
|
if not _active:
|
|
222
223
|
return
|
|
223
224
|
|
|
224
|
-
|
|
225
|
+
# Measure the window in GAME time and only sample while it actually advances.
|
|
226
|
+
# The sampler inherits PROCESS_MODE_ALWAYS, so _process keeps firing under a
|
|
227
|
+
# godot_game_time freeze (tree paused) even though gameplay is static. Counting
|
|
228
|
+
# those frames would (1) fill the window with stale values and (2) run the
|
|
229
|
+
# auto-stop down during frozen idle, so a later step lands outside the window.
|
|
230
|
+
# Skipping while paused makes the window span only live gameplay / step time.
|
|
231
|
+
var tree := get_tree()
|
|
232
|
+
if tree != null and tree.paused:
|
|
233
|
+
return
|
|
234
|
+
_elapsed_ms += delta * 1000.0 # time_scale-scaled, unpaused-only == game time
|
|
225
235
|
|
|
226
|
-
if
|
|
236
|
+
if _elapsed_ms >= float(_duration_ms):
|
|
227
237
|
_active = false
|
|
228
|
-
_stop_time = Time.get_ticks_msec()
|
|
229
238
|
set_process(false)
|
|
230
239
|
# Window over: stop recording signal emissions too. An emission landing
|
|
231
|
-
# between
|
|
240
|
+
# between the window closing and this frame is recorded with t_ms
|
|
232
241
|
# slightly past the window -- harmless and honest.
|
|
233
242
|
_disconnect_all()
|
|
234
243
|
return
|
|
@@ -246,7 +255,7 @@ func _process(_delta: float) -> void:
|
|
|
246
255
|
for field_info in spec.fields:
|
|
247
256
|
var arr: Array = _samples.get(field_info.full_key, [])
|
|
248
257
|
if arr.size() < MAX_SAMPLES_PER_FIELD:
|
|
249
|
-
arr.append({"t_ms":
|
|
258
|
+
arr.append({"t_ms": int(_elapsed_ms), "value": "freed"})
|
|
250
259
|
else:
|
|
251
260
|
_field_truncated[field_info.full_key] = true
|
|
252
261
|
continue
|
|
@@ -257,19 +266,13 @@ func _process(_delta: float) -> void:
|
|
|
257
266
|
continue
|
|
258
267
|
var arr: Array = _samples.get(field_info.full_key, [])
|
|
259
268
|
if arr.size() < MAX_SAMPLES_PER_FIELD:
|
|
260
|
-
arr.append({"t_ms":
|
|
269
|
+
arr.append({"t_ms": int(_elapsed_ms), "value": value})
|
|
261
270
|
else:
|
|
262
271
|
_field_truncated[field_info.full_key] = true
|
|
263
272
|
|
|
264
273
|
|
|
265
274
|
func collect() -> Dictionary:
|
|
266
|
-
var elapsed
|
|
267
|
-
if _stop_time > 0:
|
|
268
|
-
elapsed = _stop_time - _start_time
|
|
269
|
-
elif _start_time > 0:
|
|
270
|
-
elapsed = Time.get_ticks_msec() - _start_time
|
|
271
|
-
else:
|
|
272
|
-
elapsed = 0 # never started
|
|
275
|
+
var elapsed := int(_elapsed_ms)
|
|
273
276
|
var total_samples := 0
|
|
274
277
|
for key in _samples:
|
|
275
278
|
total_samples += (_samples[key] as Array).size()
|
|
@@ -288,11 +291,9 @@ func collect() -> Dictionary:
|
|
|
288
291
|
|
|
289
292
|
|
|
290
293
|
func stop() -> Dictionary:
|
|
294
|
+
# _elapsed_ms already holds the game-time window and freezes once _active is
|
|
295
|
+
# false, so a late manual stop can't inflate window_ms past the real window end.
|
|
291
296
|
_active = false
|
|
292
|
-
# Don't clobber an auto-stop's timestamp: a late manual stop would inflate
|
|
293
|
-
# window_ms to the call time instead of the actual window end.
|
|
294
|
-
if _stop_time == 0:
|
|
295
|
-
_stop_time = Time.get_ticks_msec()
|
|
296
297
|
set_process(false)
|
|
297
298
|
_disconnect_all()
|
|
298
299
|
return collect()
|
package/addon/plugin.cfg
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@satelliteoflove/godot-mcp",
|
|
3
|
-
"version": "3.23.
|
|
3
|
+
"version": "3.23.1",
|
|
4
4
|
"description": "MCP server that gives AI assistants eyes and hands in the Godot editor: scene editing, input injection, deterministic game-time control, and live runtime state for agent-driven playtesting",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|