gdharness 1.1.2 → 1.1.3

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.
package/build/cli.js CHANGED
@@ -41842,13 +41842,23 @@ class GodotServer {
41842
41842
  if (!declined || Date.now() - started >= timeoutMs) {
41843
41843
  break;
41844
41844
  }
41845
+ await new Promise((settle) => setTimeout(settle, 100));
41845
41846
  }
41846
41847
  let busy = true;
41847
- while (busy && Date.now() - started < timeoutMs) {
41848
+ let idleSince = null;
41849
+ while (Date.now() - started < timeoutMs) {
41848
41850
  await new Promise((settle) => setTimeout(settle, 100));
41849
41851
  const status = asParams(await this.godotBridge.invokeTool("rescan_filesystem", { ...args, statusOnly: true }));
41850
41852
  const pending = Boolean(status["pending"]) && Date.now() - started < SCAN_START_MS;
41851
41853
  busy = Boolean(status["scanning"]) || Boolean(status["importing"]) || pending;
41854
+ if (busy) {
41855
+ idleSince = null;
41856
+ } else {
41857
+ idleSince ??= Date.now();
41858
+ if (Date.now() - idleSince >= SCAN_SETTLED_MS) {
41859
+ break;
41860
+ }
41861
+ }
41852
41862
  }
41853
41863
  if (!busy && projectPath !== "" && (atRisk.length > 0 || ghosts.length > 0 || blind.unchecked !== undefined)) {
41854
41864
  const until = Date.now() + CACHE_WRITE_MS;
@@ -42318,7 +42328,7 @@ async function runGodotServer() {
42318
42328
  const server = new GodotServer;
42319
42329
  await server.run();
42320
42330
  }
42321
- var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run7, __dirname2, EDITOR_RESTART_TIMEOUT_MS = 90000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, PLAY_STARTS_WITHIN_MS = 30000, STOP_WAIT_MS = 1e4, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, MOST_GROUPS = 20, VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, SCAN_WAIT_MS = 30000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/";
42331
+ var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run7, __dirname2, EDITOR_RESTART_TIMEOUT_MS = 90000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, PLAY_STARTS_WITHIN_MS = 30000, STOP_WAIT_MS = 1e4, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, MOST_GROUPS = 20, VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, SCAN_WAIT_MS = 30000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/";
42322
42332
  var init_server3 = __esm(() => {
42323
42333
  init_mcp();
42324
42334
  init_stdio2();
@@ -11,12 +11,10 @@ var _editor_plugin: EditorPlugin = null
11
11
  var _values: Serialisation = Serialisation.new()
12
12
  var _properties: PropertyValues = PropertyValues.new()
13
13
 
14
- ## How many scans the editor has finished, counted off its own signal, and what the count was when
15
- ## a scan was last asked for. A queued scan has not started, so the flags the engine offers read
16
- ## the same as one that is over; the count is what tells the two apart.
14
+ ## How many times the editor has said its filesystem changed, which a scan run on the main thread
15
+ ## does before `scan()` returns: counted so a rescan can tell a scan that ran there from one the
16
+ ## editor declined.
17
17
  var _scans_finished: int = 0
18
- var _scans_finished_at_request: int = 0
19
- var _scan_pending: bool = false
20
18
 
21
19
  ## When the last scan finished, on the engine's clock, or -1 before the first. The editor writes
22
20
  ## the class cache a frame after it stops reporting a scan, measured on 4.7.2, so a game started
@@ -36,7 +34,6 @@ func set_editor_plugin(plugin: EditorPlugin) -> void:
36
34
 
37
35
  func _scan_finished() -> void:
38
36
  _scans_finished += 1
39
- _scan_pending = false
40
37
  _scan_finished_at_msec = Time.get_ticks_msec()
41
38
 
42
39
 
@@ -662,31 +659,24 @@ func rescan_filesystem(args: Dictionary) -> Dictionary:
662
659
  var busy: bool = filesystem.is_scanning() or filesystem.is_importing()
663
660
  var started: bool = false
664
661
  if not Read.as_bool(args.get("statusOnly", false)) and not busy:
665
- _scans_finished_at_request = _scans_finished
666
- _scan_pending = true
662
+ var finished_before: int = _scans_finished
667
663
  filesystem.scan()
668
- started = true
664
+ # Whether the scan ran, read off the editor rather than assumed. `scan()` returns without
665
+ # a word while the thread of the scan before is still to be joined, which is a frame or
666
+ # more after that scan stops reporting itself, and that scan then signals as it is joined.
667
+ # Taken as started, the answer came from a scan begun before the files were written:
668
+ # downstream, a rescan after 698 files were rewritten answered finished in 1.5s and the
669
+ # one changed file stayed on its old import. A threaded scan reports itself from inside
670
+ # the call, and one run on this thread has finished and signalled before it returns.
671
+ started = filesystem.is_scanning() or _scans_finished != finished_before
669
672
 
670
673
  # Importing is reported separately from scanning, and a class is not registered until
671
674
  # both are done, so a caller watching only one of them can look too early.
672
- #
673
- # `pending` is the third state, and it is the one that was missing. A scan is queued rather
674
- # than run, so for the first frames after asking, both flags are false and the editor has not
675
- # started: a caller polling them reads "finished" off a scan that has not begun. Measured, a
676
- # headless editor of 400 scripts takes 1619ms and answered honestly, while a downstream editor
677
- # of 376 classes answered finished 296ms in and then wrote its own stale class list over the
678
- # cache that had just been corrected. Whoever reads this is waiting for the scan to have
679
- # landed, so "not yet" and "over" must not be spelled the same.
680
- var scanning: bool = filesystem.is_scanning()
681
- var importing: bool = filesystem.is_importing()
682
- if scanning or importing or _scans_finished != _scans_finished_at_request:
683
- _scan_pending = false
684
675
  return {
685
676
  "ok": true,
686
677
  "started": started,
687
- "scanning": scanning,
688
- "importing": importing,
689
- "pending": _scan_pending,
678
+ "scanning": filesystem.is_scanning(),
679
+ "importing": filesystem.is_importing(),
690
680
  "scansFinished": _scans_finished,
691
681
  }
692
682
 
@@ -704,7 +694,6 @@ func scan_status(_args: Dictionary) -> Dictionary:
704
694
  "ok": true,
705
695
  "scanning": filesystem.is_scanning(),
706
696
  "importing": filesystem.is_importing(),
707
- "pending": _scan_pending,
708
697
  "sinceScanFinishedMs":
709
698
  -1 if _scan_finished_at_msec < 0 else Time.get_ticks_msec() - _scan_finished_at_msec,
710
699
  }
package/build/index.js CHANGED
@@ -31475,6 +31475,7 @@ function scanWaitAnswer(scanned) {
31475
31475
  }
31476
31476
  var PLAYING_STATUS_MS = 1000;
31477
31477
  var SCAN_START_MS = 2000;
31478
+ var SCAN_SETTLED_MS = 500;
31478
31479
  var WAIT_FOR_RUN_MS = 600000;
31479
31480
  var RUN_POLL_MS = 250;
31480
31481
  var PATH_SOLUTIONS = [
@@ -34579,13 +34580,23 @@ class GodotServer {
34579
34580
  if (!declined || Date.now() - started >= timeoutMs) {
34580
34581
  break;
34581
34582
  }
34583
+ await new Promise((settle) => setTimeout(settle, 100));
34582
34584
  }
34583
34585
  let busy = true;
34584
- while (busy && Date.now() - started < timeoutMs) {
34586
+ let idleSince = null;
34587
+ while (Date.now() - started < timeoutMs) {
34585
34588
  await new Promise((settle) => setTimeout(settle, 100));
34586
34589
  const status = asParams(await this.godotBridge.invokeTool("rescan_filesystem", { ...args, statusOnly: true }));
34587
34590
  const pending = Boolean(status["pending"]) && Date.now() - started < SCAN_START_MS;
34588
34591
  busy = Boolean(status["scanning"]) || Boolean(status["importing"]) || pending;
34592
+ if (busy) {
34593
+ idleSince = null;
34594
+ } else {
34595
+ idleSince ??= Date.now();
34596
+ if (Date.now() - idleSince >= SCAN_SETTLED_MS) {
34597
+ break;
34598
+ }
34599
+ }
34589
34600
  }
34590
34601
  if (!busy && projectPath !== "" && (atRisk.length > 0 || ghosts.length > 0 || blind.unchecked !== undefined)) {
34591
34602
  const until = Date.now() + CACHE_WRITE_MS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "mcpName": "io.github.Aureliolo/gdharness",
5
5
  "description": "A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.",
6
6
  "type": "module",