gdharness 0.13.39 → 0.13.41

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.
@@ -31,6 +31,11 @@ const ELSEWHERE_CLOSE_CODE: int = 4001
31
31
  const LSP_SETTING: String = "network/language_server/remote_port"
32
32
  const DAP_SETTING: String = "network/debug_adapter/remote_port"
33
33
  const DEBUGGER_SETTING: String = "network/debug/remote_port"
34
+ ## Whether the editor shares its breakpoints with a debug adapter session as it opens, or clears
35
+ ## them. Off by default in Godot, and off means every breakpoint in the editor is removed the
36
+ ## moment any session sends `initialize`, which a server does for its first breakpoint or its
37
+ ## first stack read. Turned on by [method _keep_breakpoints_through_sessions] when this loads.
38
+ const SYNC_BREAKPOINTS_SETTING: String = "network/debug_adapter/sync_breakpoints"
34
39
  ## What a server that opened this editor put in the environment, matching the ports it named on
35
40
  ## the command line. Kept in step with `editorArguments` in src/launch.ts.
36
41
  const LSP_ASKED: String = "GDHARNESS_LSP_PORT"
@@ -38,6 +43,12 @@ const DAP_ASKED: String = "GDHARNESS_DAP_PORT"
38
43
  ## What a server that opened this editor says about itself, so that being opened by one is stated
39
44
  ## rather than guessed at from the ports. Kept in step with `OPENED_BY_A_SERVER` in src/launch.ts.
40
45
  const OPENED_BY_A_SERVER: String = "GDHARNESS_OPENED_BY_A_SERVER"
46
+ ## Put into this editor's own environment as it loads, so every game it plays inherits it and
47
+ ## the runtime addon can announce which editor played it. A game of the same project that some
48
+ ## other server started has no such variable, or another editor's number, and that is what
49
+ ## tells the two apart when both are announced at once. Kept in step with `EDITOR_PID_VARIABLE`
50
+ ## in the runtime autoload.
51
+ const EDITOR_PID_VARIABLE: String = "GDHARNESS_EDITOR_PID"
41
52
 
42
53
  ## How long one attempt is given before the address is called a bad one.
43
54
  ##
@@ -87,6 +98,8 @@ var _connecting_for: float = 0.0
87
98
  func _ready() -> void:
88
99
  _project_path = ProjectSettings.globalize_path("res://")
89
100
  version_at_load = _loaded_version()
101
+ _keep_breakpoints_through_sessions()
102
+ OS.set_environment(EDITOR_PID_VARIABLE, str(OS.get_process_id()))
90
103
 
91
104
  _reconnect_timer = Timer.new()
92
105
  _reconnect_timer.one_shot = true
@@ -281,6 +294,9 @@ func _handle_connect() -> void:
281
294
  "lsp_port": _serves(LSP_ASKED, LSP_SETTING),
282
295
  "dap_port": _serves(DAP_ASKED, DAP_SETTING),
283
296
  "debug_port": _serving(DEBUGGER_SETTING),
297
+ # Whether a session opening on the adapter is told this editor's breakpoints or takes
298
+ # them away, so a server can say which before it sets one.
299
+ "syncs_breakpoints": _syncs_breakpoints(),
284
300
  # Whether a server started this editor, which is the only kind that can be started again
285
301
  # as itself: the engine hands back none of the arguments it was given, so the arguments
286
302
  # have to come from whoever wrote them. See `restart_editor` in play_tools.gd.
@@ -329,6 +345,50 @@ static func _asked_for(variable: String) -> int:
329
345
  return port if port >= 1 and port <= 65535 else 0
330
346
 
331
347
 
348
+ ## Turns breakpoint syncing on, so a debug session opening does not clear the editor's breakpoints.
349
+ ##
350
+ ## With the setting off, Godot's adapter answers `initialize` by clearing every breakpoint in the
351
+ ## script editor, open and cached, and telling every session so; with it on, it tells the new
352
+ ## session what is set and clears nothing. A server opens a session for its first breakpoint or
353
+ ## its first stack read, so off meant a harness session took the user's breakpoints away on its
354
+ ## first debug call, silently. The adapter reads the setting again the moment it changes, so this
355
+ ## holds for the editor that is running and not only the next one.
356
+ ##
357
+ ## An editor setting rather than a project one, the way the debugger port this addon moves is,
358
+ ## and left alone when it already reads true or does not exist.
359
+ func _keep_breakpoints_through_sessions() -> void:
360
+ if not Engine.is_editor_hint():
361
+ return
362
+ var settings: EditorSettings = EditorInterface.get_editor_settings()
363
+ if settings == null or not settings.has_setting(SYNC_BREAKPOINTS_SETTING):
364
+ return
365
+ if Read.as_bool(settings.get_setting(SYNC_BREAKPOINTS_SETTING)):
366
+ return
367
+ settings.set_setting(SYNC_BREAKPOINTS_SETTING, true)
368
+ # Stored is not the same as noticed. The adapter reads this setting again on the editor-wide
369
+ # settings notification, which the settings dialog sends after Apply and set_setting does not
370
+ # send at all, so without this the running adapter went on clearing breakpoints until the
371
+ # editor was restarted. Sent from the editor's root, which is where the dialog sends it from.
372
+ var editor_root: Node = get_tree().root.get_child(0)
373
+ editor_root.propagate_notification(EditorSettings.NOTIFICATION_EDITOR_SETTINGS_CHANGED)
374
+ print(
375
+ (
376
+ "[gdharness] turned on %s, so a debug session opening keeps the editor's breakpoints"
377
+ % SYNC_BREAKPOINTS_SETTING
378
+ )
379
+ )
380
+
381
+
382
+ ## Whether the editor shares its breakpoints with a session as it opens. See the setting.
383
+ func _syncs_breakpoints() -> bool:
384
+ if not Engine.is_editor_hint():
385
+ return false
386
+ var settings: EditorSettings = EditorInterface.get_editor_settings()
387
+ if settings == null or not settings.has_setting(SYNC_BREAKPOINTS_SETTING):
388
+ return false
389
+ return Read.as_bool(settings.get_setting(SYNC_BREAKPOINTS_SETTING))
390
+
391
+
332
392
  ## What the settings say this editor serves on, or 0 when there is nothing to ask.
333
393
  func _serving(setting: String) -> int:
334
394
  if not Engine.is_editor_hint():
@@ -30,6 +30,12 @@ const SCRIPT_FLAGS: PackedStringArray = ["-s", "--script"]
30
30
  ## Serve a script run anyway, for somebody driving a `-s` script rather than a game.
31
31
  const SCRIPT_RUNS_SETTING: String = "gdharness/runtime/serve_script_runs"
32
32
 
33
+ ## The variable the editor addon puts into its own environment, which every game the editor plays
34
+ ## inherits: it says which editor played this game, so a server can tell the editor's game from
35
+ ## another of the same project that some other server started. Kept in step with
36
+ ## `EDITOR_PID_VARIABLE` in the editor addon's bridge client.
37
+ const EDITOR_PID_VARIABLE: String = "GDHARNESS_EDITOR_PID"
38
+
33
39
  var values: Values = Values.new()
34
40
 
35
41
  # The modules are members and not locals of _init, because a Callable holds its object by id
@@ -250,7 +256,7 @@ func _announce(bind_address: String) -> void:
250
256
  ## What the announcement file and the welcome both carry: enough to pick this game out of
251
257
  ## several and to know whether the server speaks its protocol.
252
258
  func _identity(bind_address: String) -> Dictionary:
253
- return {
259
+ var identity: Dictionary = {
254
260
  "protocol": PROTOCOL,
255
261
  "pid": OS.get_process_id(),
256
262
  "port": _port,
@@ -262,6 +268,10 @@ func _identity(bind_address: String) -> Dictionary:
262
268
  },
263
269
  "godot": Engine.get_version_info().get("string", ""),
264
270
  }
271
+ var played_by: String = OS.get_environment(EDITOR_PID_VARIABLE)
272
+ if played_by.is_valid_int():
273
+ identity["editor_pid"] = played_by.to_int()
274
+ return identity
265
275
 
266
276
 
267
277
  func _send_welcome(client: StreamPeerTCP) -> void: