gdharness 0.13.41 → 0.13.43

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.
@@ -0,0 +1,62 @@
1
+ extends Logger
2
+
3
+ ## Every error the engine reports inside this game, written to a file beside the announcement,
4
+ ## in the lines the engine itself prints to stderr.
5
+ ##
6
+ ## A game the editor plays prints to the editor's own stderr, which nobody reads: a `push_error`
7
+ ## raised in one reached neither `editor_output` nor the run's transcript, and the run was
8
+ ## answered clean while the game had just refused something out loud. The debug adapter relays
9
+ ## what the game prints and not what it reports, so the report is taken where it is made, in
10
+ ## the game, and left where the server already looks. The lines are the engine's own so the
11
+ ## server reads them the way it reads a run it started itself.
12
+
13
+ var _path: String
14
+
15
+
16
+ func _init(path: String) -> void:
17
+ _path = path
18
+
19
+
20
+ # The engine's own signature for a Logger, eight parameters and not this project's to shape.
21
+ # gdlint:ignore = function-arguments-number
22
+ func _log_error(
23
+ function: String,
24
+ file: String,
25
+ line: int,
26
+ code: String,
27
+ rationale: String,
28
+ _editor_notify: bool,
29
+ error_type: int,
30
+ script_backtraces: Array[ScriptBacktrace],
31
+ ) -> void:
32
+ var kind: String
33
+ match error_type:
34
+ ERROR_TYPE_WARNING:
35
+ kind = "WARNING"
36
+ ERROR_TYPE_SCRIPT:
37
+ kind = "SCRIPT ERROR"
38
+ ERROR_TYPE_SHADER:
39
+ kind = "SHADER ERROR"
40
+ _:
41
+ kind = "ERROR"
42
+ var lines: Array[String] = [
43
+ "%s: %s" % [kind, rationale if not rationale.is_empty() else code],
44
+ " at: %s (%s:%d)" % [function, file, line],
45
+ ]
46
+ for backtrace: ScriptBacktrace in script_backtraces:
47
+ lines.append(backtrace.format(3))
48
+ # Opened for each report rather than held, so the file is whole on disk after every one:
49
+ # the last error a game reports is the one a caller most needs, and the game may be gone the
50
+ # moment after. Nothing here may report an error of its own, which would come straight back.
51
+ var report: FileAccess = (
52
+ FileAccess.open(_path, FileAccess.READ_WRITE)
53
+ if FileAccess.file_exists(_path)
54
+ else FileAccess.open(_path, FileAccess.WRITE)
55
+ )
56
+ if report == null:
57
+ return
58
+ report.seek_end()
59
+ for each: String in lines:
60
+ # A line the disk would not take is a report lost, and nothing here may report that.
61
+ var _stored: bool = report.store_line(each)
62
+ report.close()
@@ -0,0 +1 @@
1
+ uid://bhvig6uq5tvey
@@ -11,6 +11,7 @@ extends Node
11
11
  ## id and the reply carries it back.
12
12
 
13
13
  const Capture = preload("runtime_capture.gd")
14
+ const ErrorReport = preload("error_report.gd")
14
15
  const InputCommands = preload("runtime_input.gd")
15
16
  const Queries = preload("runtime_queries.gd")
16
17
  const Read = preload("reading.gd")
@@ -36,6 +37,11 @@ const SCRIPT_RUNS_SETTING: String = "gdharness/runtime/serve_script_runs"
36
37
  ## `EDITOR_PID_VARIABLE` in the editor addon's bridge client.
37
38
  const EDITOR_PID_VARIABLE: String = "GDHARNESS_EDITOR_PID"
38
39
 
40
+ ## The file the engine's error reports go to, beside the announcement and named the same way,
41
+ ## which is where the server reads them for a run the editor plays. Kept in step with
42
+ ## `errorReportPath` in src/runtime-client.ts.
43
+ const ERROR_REPORT_NAME: String = "runtime-%d.log"
44
+
39
45
  var values: Values = Values.new()
40
46
 
41
47
  # The modules are members and not locals of _init, because a Callable holds its object by id
@@ -61,6 +67,9 @@ var _outgoing: Dictionary = {}
61
67
  var _port: int = 0
62
68
  var _enabled: bool = true
63
69
  var _announcement: String = ""
70
+ ## Held for as long as the game runs: a Logger the engine has is kept by reference, and one
71
+ ## that goes away mid-run takes the report with it.
72
+ var _errors: ErrorReport
64
73
  ## Every command, by the name a request uses, as the module method that answers it.
65
74
  var _commands: Dictionary = {}
66
75
 
@@ -98,13 +107,35 @@ func _ready() -> void:
98
107
  # even be un-paused over the socket. A debug server has to stay responsive while the game
99
108
  # is frozen, to inspect, capture, inject or resume it.
100
109
  process_mode = Node.PROCESS_MODE_ALWAYS
110
+ _report_errors()
101
111
  _start_server()
102
112
 
103
113
 
104
114
  func _exit_tree() -> void:
115
+ if _errors != null:
116
+ OS.remove_logger(_errors)
117
+ _errors = null
105
118
  _cleanup()
106
119
 
107
120
 
121
+ ## The engine's error reports, written to a file for the server, for a game the editor plays.
122
+ ##
123
+ ## Only for one the editor plays: a game the server starts itself has its stderr in the run's
124
+ ## transcript already, and a game somebody runs from a terminal has it on the terminal. The
125
+ ## editor's game prints to the editor's stderr, which nobody reads, and the editor's debug
126
+ ## adapter relays what the game prints and not what it reports. Before the server starts, so
127
+ ## a port that cannot be bound is reported there too, and whether or not the server comes up.
128
+ func _report_errors() -> void:
129
+ if not OS.get_environment(EDITOR_PID_VARIABLE).is_valid_int():
130
+ return
131
+ var directory: String = _announcement_directory()
132
+ var made: Error = DirAccess.make_dir_recursive_absolute(directory)
133
+ if made != OK and made != ERR_ALREADY_EXISTS:
134
+ return
135
+ _errors = ErrorReport.new(directory.path_join(ERROR_REPORT_NAME % OS.get_process_id()))
136
+ OS.add_logger(_errors)
137
+
138
+
108
139
  func _process(_delta: float) -> void:
109
140
  if not _enabled or _server == null:
110
141
  return