gdharness 1.1.25 → 1.1.27
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
|
@@ -17556,7 +17556,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17556
17556
|
requires: ["projectPath"]
|
|
17557
17557
|
},
|
|
17558
17558
|
stop: {
|
|
17559
|
-
summary: "end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, and false with the number under stillGoing when what was told to end was still running ten seconds later",
|
|
17559
|
+
summary: "end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause",
|
|
17560
17560
|
requires: []
|
|
17561
17561
|
},
|
|
17562
17562
|
check: {
|
|
@@ -17724,7 +17724,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17724
17724
|
namePattern: {
|
|
17725
17725
|
type: "string",
|
|
17726
17726
|
ops: ["find"],
|
|
17727
|
-
description: 'find: a case-insensitive glob on the node name, such as "Enemy*". Matched against the whole name, so a bare word finds only a node called exactly that; an answer of none says how many names contain it and what glob would have found them.'
|
|
17727
|
+
description: 'find: a case-insensitive glob on the node name, such as "Enemy*", or several separated by | the way says takes them, such as "Enemy*|Boss", with \\| for a bar in a name. Matched against the whole name, so a bare word finds only a node called exactly that; an answer of none says how many names contain it and what glob would have found them.'
|
|
17728
17728
|
},
|
|
17729
17729
|
group: { type: "string", ops: ["find"], description: "find: a group the node is in." },
|
|
17730
17730
|
says: {
|
|
@@ -39767,7 +39767,7 @@ function stopVerdict(stop) {
|
|
|
39767
39767
|
return {
|
|
39768
39768
|
stopped: false,
|
|
39769
39769
|
notSignalled: stop.endedPid,
|
|
39770
|
-
note: `Nothing was ended: pid ${stop.endedPid ?? "unknown"} no longer answers as the process this run was started as (its engine, its project and when it started), so it was not signalled. If that process is still the game, end it yourself, for instance with runtime_invoke calling get_tree().quit() when the game has the runtime addon; if it is not, it belongs to something else.`,
|
|
39770
|
+
note: `Nothing was ended: pid ${stop.endedPid ?? "unknown"} no longer answers as the process this run was started as (${stop.notEndedBecause ?? "its engine, its project and when it started"}), so it was not signalled. If that process is still the game, end it yourself, for instance with runtime_invoke calling get_tree().quit() when the game has the runtime addon; if it is not, it belongs to something else.`,
|
|
39771
39771
|
withChildren: false
|
|
39772
39772
|
};
|
|
39773
39773
|
}
|
|
@@ -39788,6 +39788,15 @@ function stopVerdict(stop) {
|
|
|
39788
39788
|
withChildren: true
|
|
39789
39789
|
};
|
|
39790
39790
|
}
|
|
39791
|
+
function previousRunHow(ended) {
|
|
39792
|
+
if (ended === null) {
|
|
39793
|
+
return {};
|
|
39794
|
+
}
|
|
39795
|
+
return {
|
|
39796
|
+
...ended.through === undefined || ended.ending === "refused" ? {} : { endedPreviousRunThrough: ended.through },
|
|
39797
|
+
...ended.ending === "refused" && ended.notEndedBecause !== undefined ? { previousRunLeftBecause: ended.notEndedBecause } : {}
|
|
39798
|
+
};
|
|
39799
|
+
}
|
|
39791
39800
|
function endedPreviousRun(ended) {
|
|
39792
39801
|
if (ended?.ending !== "gone") {
|
|
39793
39802
|
return;
|
|
@@ -42512,9 +42521,12 @@ class GodotServer {
|
|
|
42512
42521
|
if (before !== null && await this.runStillGoing(before)) {
|
|
42513
42522
|
this.logDebug("Ending the running game before starting another");
|
|
42514
42523
|
const endingFrom = Date.now();
|
|
42524
|
+
const ending = await this.endActiveGame("editor_run start, which ends the run that was going before it starts another", true);
|
|
42515
42525
|
ended = {
|
|
42516
42526
|
pid: before.pid ?? this.announcedPidOf(before) ?? null,
|
|
42517
|
-
ending
|
|
42527
|
+
ending,
|
|
42528
|
+
...before.endedThrough === undefined ? {} : { through: before.endedThrough },
|
|
42529
|
+
...before.notEndedBecause === undefined ? {} : { notEndedBecause: before.notEndedBecause }
|
|
42518
42530
|
};
|
|
42519
42531
|
this.logDebug(`The running game was ${ended.ending} after ${Date.now() - endingFrom}ms`);
|
|
42520
42532
|
}
|
|
@@ -42562,6 +42574,7 @@ class GodotServer {
|
|
|
42562
42574
|
savesNote: asked.value.savesIn !== undefined && savesStayPut() ? SAVES_NOT_MOVED_NOTE : undefined,
|
|
42563
42575
|
endedPreviousRun: endedPreviousRun(ended),
|
|
42564
42576
|
previousRunLeft: previousRunLeft(ended),
|
|
42577
|
+
...previousRunHow(ended),
|
|
42565
42578
|
runtime: await this.runtimeUp(project.value.path, alreadyPlaying, runtimeWaitMs, given.value.length > 0),
|
|
42566
42579
|
message: `Use editor_output for what it prints and editor_run stop to end it.${spawnedInstead}${endedForThis}${alsoRunning}`
|
|
42567
42580
|
});
|
|
@@ -42714,6 +42727,7 @@ class GodotServer {
|
|
|
42714
42727
|
debugPort: readNumber(playAnswer, "debugPort"),
|
|
42715
42728
|
endedPreviousRun: endedPreviousRun(ended),
|
|
42716
42729
|
previousRunLeft: previousRunLeft(ended),
|
|
42730
|
+
...previousRunHow(ended),
|
|
42717
42731
|
breakpoints: breakpoints.applied.length === 0 ? undefined : this.breakpointsAsProjectSpells(projectPath, breakpoints.applied),
|
|
42718
42732
|
breakpointsRefused: breakpoints.refused.length === 0 ? undefined : breakpoints.refused.map((one) => ({
|
|
42719
42733
|
scriptPath: projectSpelling(projectPath, one.scriptPath),
|
|
@@ -42868,6 +42882,7 @@ class GodotServer {
|
|
|
42868
42882
|
running.log.record("warning", `This run was not ended here: the editor could not be asked to stop it (${asked.content[0]?.text ?? "no reason given"}).`);
|
|
42869
42883
|
return "unreached";
|
|
42870
42884
|
}
|
|
42885
|
+
running.endedThrough = "editor";
|
|
42871
42886
|
if (announced === undefined) {
|
|
42872
42887
|
return await this.untilTheEditorStopsPlaying() ? "gone" : "lingering";
|
|
42873
42888
|
}
|
|
@@ -42896,6 +42911,7 @@ class GodotServer {
|
|
|
42896
42911
|
if (why !== null) {
|
|
42897
42912
|
clearTheRecord();
|
|
42898
42913
|
running.endedHere = null;
|
|
42914
|
+
running.notEndedBecause = why;
|
|
42899
42915
|
running.log.record("warning", `This run was not ended here: pid ${running.pid} no longer answers as the process the run was started as (${why}), so nothing was signalled. If that process is still the game, end it yourself; if it is not, it belongs to something else.`);
|
|
42900
42916
|
return "refused";
|
|
42901
42917
|
}
|
|
@@ -42920,6 +42936,9 @@ class GodotServer {
|
|
|
42920
42936
|
running.exitCode = null;
|
|
42921
42937
|
running.exitSignal = "SIGTERM";
|
|
42922
42938
|
}
|
|
42939
|
+
if (landed) {
|
|
42940
|
+
running.endedThrough = "signal";
|
|
42941
|
+
}
|
|
42923
42942
|
clearTheRecord();
|
|
42924
42943
|
running.log.record(gone ? "info" : "warning", declined !== null ? `gdharness could not signal pid ${running.pid}: the operating system refused it (${declined}). It had described that process as ${described}.` : !gone ? `gdharness signalled pid ${running.pid}, which the operating system described as ${described}, and it was still running ${STOP_WAIT_MS / 1000} seconds later.` : landed ? `gdharness ended pid ${running.pid}, which the operating system described as ${described}.` : `gdharness signalled pid ${running.pid} and it was already gone; the operating system had described it as ${described}.`);
|
|
42925
42944
|
return gone ? "gone" : "lingering";
|
|
@@ -42930,6 +42949,9 @@ class GodotServer {
|
|
|
42930
42949
|
this.logDebug(`The keeper of pid ${pid} could not be asked to end it (${asked.unreached})`);
|
|
42931
42950
|
return null;
|
|
42932
42951
|
}
|
|
42952
|
+
if (asked === "signalled") {
|
|
42953
|
+
run.endedThrough = "keeper";
|
|
42954
|
+
}
|
|
42933
42955
|
const gone = await untilGone([pid]);
|
|
42934
42956
|
if (gone) {
|
|
42935
42957
|
await this.untilTheExitIsRecorded(run);
|
|
@@ -43655,11 +43677,13 @@ class GodotServer {
|
|
|
43655
43677
|
endedPid,
|
|
43656
43678
|
throughEditor: stopped.throughEditor,
|
|
43657
43679
|
exitSignal: stopped.exitSignal,
|
|
43658
|
-
clean: cleanVerdict(stopped)
|
|
43680
|
+
clean: cleanVerdict(stopped),
|
|
43681
|
+
...stopped.notEndedBecause === undefined ? {} : { notEndedBecause: stopped.notEndedBecause }
|
|
43659
43682
|
});
|
|
43660
43683
|
return this.jsonTextResponse({
|
|
43661
43684
|
stopped: verdict.stopped,
|
|
43662
43685
|
through: stopped.throughEditor ? "editor" : "gdharness",
|
|
43686
|
+
endedThrough: wasRunning ? stopped.endedThrough : undefined,
|
|
43663
43687
|
endedPid: verdict.endedPid,
|
|
43664
43688
|
notSignalled: verdict.notSignalled,
|
|
43665
43689
|
stillGoing: verdict.stillGoing,
|
|
@@ -43724,6 +43748,7 @@ class GodotServer {
|
|
|
43724
43748
|
through: "gdharness",
|
|
43725
43749
|
endedPid: gone ? pid : undefined,
|
|
43726
43750
|
stillGoing: gone ? undefined : pid,
|
|
43751
|
+
...landed ? { endedThrough: "signal" } : {},
|
|
43727
43752
|
...childrenFields(ended),
|
|
43728
43753
|
exitedBeforeStop: !landed,
|
|
43729
43754
|
note: `${!gone ? `pid ${pid} was told to end and was still running ${STOP_WAIT_MS / 1000} seconds later, so it has not stopped: a game of this project that no server here was holding,` : landed ? `pid ${pid} was ended: a game of this project that no server here was holding,` : `pid ${pid} had already gone when it was signalled: a game of this project that no server here was holding,`} identified by its announcement and by its command line, ${read.executable} run with --path ${read.projectPath ?? ""}. Nothing here read what it printed or holds its exit code, so neither is in this answer.${gone ? "" : " editor_status says when it has gone."} ${aboutTheChildren(ended, false)}`
|
|
@@ -70,7 +70,8 @@ func get_tree(params: Dictionary) -> Dictionary:
|
|
|
70
70
|
|
|
71
71
|
## Nodes matching every filter given, as paths, so a caller can name what it wants without
|
|
72
72
|
## reading the whole tree to find it. `class` matches native classes and their subclasses, and
|
|
73
|
-
## the global name of a script class; `name` is a case-insensitive glob
|
|
73
|
+
## the global name of a script class; `name` is a case-insensitive glob, or several separated by `|`
|
|
74
|
+
## the way `says` takes them, with `\|` for a bar in a name; `script` is a path;
|
|
74
75
|
## `says` is what the node has written on it.
|
|
75
76
|
##
|
|
76
77
|
## `property` names one to read off each of them, which is the difference between one question and
|
|
@@ -103,6 +104,12 @@ func find_nodes(params: Dictionary) -> Dictionary:
|
|
|
103
104
|
if not wanted["script"].is_empty() and not wanted["script"].begins_with("res://"):
|
|
104
105
|
wanted["script"] = "res://" + wanted["script"]
|
|
105
106
|
|
|
107
|
+
# Split once here rather than for each node the walk passes.
|
|
108
|
+
var names: Array[String] = Says.split_at_bars(wanted["name"])
|
|
109
|
+
if not wanted["name"].is_empty() and names.is_empty():
|
|
110
|
+
var bars_only: String = 'name "%s" is only bars, so it names no node; write the names between them'
|
|
111
|
+
return {"type": "error", "message": bars_only % wanted["name"]}
|
|
112
|
+
|
|
106
113
|
var reached: Dictionary = Values.node_at(_host.get_tree().root, root_path)
|
|
107
114
|
if reached.has("message"):
|
|
108
115
|
return reached
|
|
@@ -113,7 +120,10 @@ func find_nodes(params: Dictionary) -> Dictionary:
|
|
|
113
120
|
var truncated: bool = false
|
|
114
121
|
# Counted while the tree is already being walked, for the answer below: how many nodes this
|
|
115
122
|
# find would have matched if the name had been read the way it was probably meant.
|
|
116
|
-
var
|
|
123
|
+
var plain_names: Array[String] = []
|
|
124
|
+
for named: String in names:
|
|
125
|
+
if Says.is_plain(named):
|
|
126
|
+
plain_names.append(named)
|
|
117
127
|
var nearly: int = 0
|
|
118
128
|
# The same for the words: a glob is matched against the whole text, and one written as a prefix
|
|
119
129
|
# by habit, "Taken together*", missed the label whose sentence those words are in the middle of.
|
|
@@ -132,7 +142,7 @@ func find_nodes(params: Dictionary) -> Dictionary:
|
|
|
132
142
|
while not pending.is_empty():
|
|
133
143
|
var node: Node = pending.pop_front()
|
|
134
144
|
var rest: bool = _matches_apart_from_name(node, wanted)
|
|
135
|
-
if rest and _named(node,
|
|
145
|
+
if rest and _named(node, names):
|
|
136
146
|
if not include_hidden and not shown(node):
|
|
137
147
|
hidden += 1
|
|
138
148
|
elif found.size() >= limit:
|
|
@@ -140,16 +150,12 @@ func find_nodes(params: Dictionary) -> Dictionary:
|
|
|
140
150
|
break
|
|
141
151
|
else:
|
|
142
152
|
found.append(_found(node, wanted_property))
|
|
143
|
-
elif rest and
|
|
153
|
+
elif rest and _contains_any(str(node.name), plain_names):
|
|
144
154
|
if include_hidden or shown(node):
|
|
145
155
|
nearly += 1
|
|
146
156
|
else:
|
|
147
157
|
nearly_hidden += 1
|
|
148
|
-
elif (
|
|
149
|
-
not widened_says.is_empty()
|
|
150
|
-
and _named(node, wanted["name"])
|
|
151
|
-
and _matches_apart_from_name(node, widened)
|
|
152
|
-
):
|
|
158
|
+
elif not widened_says.is_empty() and _named(node, names) and _matches_apart_from_name(node, widened):
|
|
153
159
|
if include_hidden or shown(node):
|
|
154
160
|
nearly_said += 1
|
|
155
161
|
else:
|
|
@@ -172,17 +178,23 @@ func find_nodes(params: Dictionary) -> Dictionary:
|
|
|
172
178
|
if found.is_empty() and nearly + nearly_hidden > 0:
|
|
173
179
|
var named_near: int = nearly if nearly > 0 else nearly_hidden
|
|
174
180
|
var holding: String = "names contain" if named_near > 1 else "name contains"
|
|
181
|
+
var near_words: Array[String] = []
|
|
182
|
+
for named: String in plain_names:
|
|
183
|
+
near_words.append('"%s"' % named)
|
|
184
|
+
var opened: Array[String] = []
|
|
185
|
+
for named: String in names:
|
|
186
|
+
opened.append("*%s*" % named if Says.is_plain(named) else named)
|
|
175
187
|
(
|
|
176
188
|
notes
|
|
177
189
|
. append(
|
|
178
190
|
(
|
|
179
|
-
'name is matched as a glob against the whole name; %d %snode %s
|
|
191
|
+
'name is matched as a glob against the whole name; %d %snode %s %s, which "%s"%s would find'
|
|
180
192
|
% [
|
|
181
193
|
named_near,
|
|
182
194
|
"" if nearly > 0 else "hidden ",
|
|
183
195
|
holding,
|
|
184
|
-
|
|
185
|
-
|
|
196
|
+
" or ".join(PackedStringArray(near_words)),
|
|
197
|
+
Says.joined_at_bars(opened),
|
|
186
198
|
"" if nearly > 0 else " with includeHidden true",
|
|
187
199
|
]
|
|
188
200
|
)
|
|
@@ -308,10 +320,23 @@ static func _anything_asked(wanted: Dictionary[String, String]) -> bool:
|
|
|
308
320
|
return false
|
|
309
321
|
|
|
310
322
|
|
|
311
|
-
## Whether [param node] carries the
|
|
312
|
-
## makes leaving a filter out the same as not having one.
|
|
313
|
-
static func _named(node: Node,
|
|
314
|
-
|
|
323
|
+
## Whether [param node] carries one of the names a find asked for. None matches everything, which is
|
|
324
|
+
## what makes leaving a filter out the same as not having one.
|
|
325
|
+
static func _named(node: Node, names: Array[String]) -> bool:
|
|
326
|
+
if names.is_empty():
|
|
327
|
+
return true
|
|
328
|
+
var called: String = str(node.name)
|
|
329
|
+
for pattern: String in names:
|
|
330
|
+
if called.matchn(pattern):
|
|
331
|
+
return true
|
|
332
|
+
return false
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
static func _contains_any(called: String, words: Array[String]) -> bool:
|
|
336
|
+
for word: String in words:
|
|
337
|
+
if called.containsn(word):
|
|
338
|
+
return true
|
|
339
|
+
return false
|
|
315
340
|
|
|
316
341
|
|
|
317
342
|
## Every filter but the name, so a find that came back empty can say how many nodes the name was
|
|
@@ -54,18 +54,34 @@ static func alternatives(wanted: String) -> Array[String]:
|
|
|
54
54
|
if kept != null:
|
|
55
55
|
var split: Array[String] = kept
|
|
56
56
|
return split
|
|
57
|
-
var
|
|
57
|
+
var found: Array[String] = split_at_bars(as_said(wanted))
|
|
58
|
+
if _splits.size() >= SPLITS_KEPT:
|
|
59
|
+
_splits.clear()
|
|
60
|
+
_splits[wanted] = found
|
|
61
|
+
return found
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## [param written] split at every `|` not written as `\|`, empty alternatives dropped. Shared with a
|
|
65
|
+
## find's name, which takes alternatives by the same rule so the two arguments beside each other do
|
|
66
|
+
## not read a bar two ways: written in a name, `A|B` was one glob no node matched, and the empty
|
|
67
|
+
## answer came with no note.
|
|
68
|
+
static func split_at_bars(written: String) -> Array[String]:
|
|
58
69
|
var found: Array[String] = []
|
|
59
|
-
for part: String in
|
|
70
|
+
for part: String in written.replace("\\|", ESCAPED_BAR).split("|"):
|
|
60
71
|
var words: String = part.replace(ESCAPED_BAR, "|")
|
|
61
72
|
if not words.is_empty():
|
|
62
73
|
found.append(words)
|
|
63
|
-
if _splits.size() >= SPLITS_KEPT:
|
|
64
|
-
_splits.clear()
|
|
65
|
-
_splits[wanted] = found
|
|
66
74
|
return found
|
|
67
75
|
|
|
68
76
|
|
|
77
|
+
## [param parts] joined back into one pattern that [method split_at_bars] reads as them.
|
|
78
|
+
static func joined_at_bars(parts: Array[String]) -> String:
|
|
79
|
+
var escaped: Array[String] = []
|
|
80
|
+
for words: String in parts:
|
|
81
|
+
escaped.append(words.replace("|", "\\|"))
|
|
82
|
+
return "|".join(PackedStringArray(escaped))
|
|
83
|
+
|
|
84
|
+
|
|
69
85
|
## Whether [param pattern] is words written whole rather than as a glob, which is how a caller
|
|
70
86
|
## writes them when they mean "contains". [method String.matchn] answers nothing to it, and nothing
|
|
71
87
|
## is also what words that are simply not there answer.
|
package/build/index.js
CHANGED
|
@@ -31540,7 +31540,7 @@ var TOOL_SPECS = [
|
|
|
31540
31540
|
requires: ["projectPath"]
|
|
31541
31541
|
},
|
|
31542
31542
|
stop: {
|
|
31543
|
-
summary: "end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, and false with the number under stillGoing when what was told to end was still running ten seconds later",
|
|
31543
|
+
summary: "end the run and answer with what it printed last, naming the process ended under endedPid: for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. A game that started processes of its own keeps them unless andChildren says to end those too. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause",
|
|
31544
31544
|
requires: []
|
|
31545
31545
|
},
|
|
31546
31546
|
check: {
|
|
@@ -31708,7 +31708,7 @@ var TOOL_SPECS = [
|
|
|
31708
31708
|
namePattern: {
|
|
31709
31709
|
type: "string",
|
|
31710
31710
|
ops: ["find"],
|
|
31711
|
-
description: 'find: a case-insensitive glob on the node name, such as "Enemy*". Matched against the whole name, so a bare word finds only a node called exactly that; an answer of none says how many names contain it and what glob would have found them.'
|
|
31711
|
+
description: 'find: a case-insensitive glob on the node name, such as "Enemy*", or several separated by | the way says takes them, such as "Enemy*|Boss", with \\| for a bar in a name. Matched against the whole name, so a bare word finds only a node called exactly that; an answer of none says how many names contain it and what glob would have found them.'
|
|
31712
31712
|
},
|
|
31713
31713
|
group: { type: "string", ops: ["find"], description: "find: a group the node is in." },
|
|
31714
31714
|
says: {
|
|
@@ -32287,7 +32287,7 @@ function stopVerdict(stop) {
|
|
|
32287
32287
|
return {
|
|
32288
32288
|
stopped: false,
|
|
32289
32289
|
notSignalled: stop.endedPid,
|
|
32290
|
-
note: `Nothing was ended: pid ${stop.endedPid ?? "unknown"} no longer answers as the process this run was started as (its engine, its project and when it started), so it was not signalled. If that process is still the game, end it yourself, for instance with runtime_invoke calling get_tree().quit() when the game has the runtime addon; if it is not, it belongs to something else.`,
|
|
32290
|
+
note: `Nothing was ended: pid ${stop.endedPid ?? "unknown"} no longer answers as the process this run was started as (${stop.notEndedBecause ?? "its engine, its project and when it started"}), so it was not signalled. If that process is still the game, end it yourself, for instance with runtime_invoke calling get_tree().quit() when the game has the runtime addon; if it is not, it belongs to something else.`,
|
|
32291
32291
|
withChildren: false
|
|
32292
32292
|
};
|
|
32293
32293
|
}
|
|
@@ -32308,6 +32308,15 @@ function stopVerdict(stop) {
|
|
|
32308
32308
|
withChildren: true
|
|
32309
32309
|
};
|
|
32310
32310
|
}
|
|
32311
|
+
function previousRunHow(ended) {
|
|
32312
|
+
if (ended === null) {
|
|
32313
|
+
return {};
|
|
32314
|
+
}
|
|
32315
|
+
return {
|
|
32316
|
+
...ended.through === undefined || ended.ending === "refused" ? {} : { endedPreviousRunThrough: ended.through },
|
|
32317
|
+
...ended.ending === "refused" && ended.notEndedBecause !== undefined ? { previousRunLeftBecause: ended.notEndedBecause } : {}
|
|
32318
|
+
};
|
|
32319
|
+
}
|
|
32311
32320
|
function endedPreviousRun(ended) {
|
|
32312
32321
|
if (ended?.ending !== "gone") {
|
|
32313
32322
|
return;
|
|
@@ -35092,9 +35101,12 @@ class GodotServer {
|
|
|
35092
35101
|
if (before !== null && await this.runStillGoing(before)) {
|
|
35093
35102
|
this.logDebug("Ending the running game before starting another");
|
|
35094
35103
|
const endingFrom = Date.now();
|
|
35104
|
+
const ending = await this.endActiveGame("editor_run start, which ends the run that was going before it starts another", true);
|
|
35095
35105
|
ended = {
|
|
35096
35106
|
pid: before.pid ?? this.announcedPidOf(before) ?? null,
|
|
35097
|
-
ending
|
|
35107
|
+
ending,
|
|
35108
|
+
...before.endedThrough === undefined ? {} : { through: before.endedThrough },
|
|
35109
|
+
...before.notEndedBecause === undefined ? {} : { notEndedBecause: before.notEndedBecause }
|
|
35098
35110
|
};
|
|
35099
35111
|
this.logDebug(`The running game was ${ended.ending} after ${Date.now() - endingFrom}ms`);
|
|
35100
35112
|
}
|
|
@@ -35142,6 +35154,7 @@ class GodotServer {
|
|
|
35142
35154
|
savesNote: asked.value.savesIn !== undefined && savesStayPut() ? SAVES_NOT_MOVED_NOTE : undefined,
|
|
35143
35155
|
endedPreviousRun: endedPreviousRun(ended),
|
|
35144
35156
|
previousRunLeft: previousRunLeft(ended),
|
|
35157
|
+
...previousRunHow(ended),
|
|
35145
35158
|
runtime: await this.runtimeUp(project.value.path, alreadyPlaying, runtimeWaitMs, given.value.length > 0),
|
|
35146
35159
|
message: `Use editor_output for what it prints and editor_run stop to end it.${spawnedInstead}${endedForThis}${alsoRunning}`
|
|
35147
35160
|
});
|
|
@@ -35294,6 +35307,7 @@ class GodotServer {
|
|
|
35294
35307
|
debugPort: readNumber(playAnswer, "debugPort"),
|
|
35295
35308
|
endedPreviousRun: endedPreviousRun(ended),
|
|
35296
35309
|
previousRunLeft: previousRunLeft(ended),
|
|
35310
|
+
...previousRunHow(ended),
|
|
35297
35311
|
breakpoints: breakpoints.applied.length === 0 ? undefined : this.breakpointsAsProjectSpells(projectPath, breakpoints.applied),
|
|
35298
35312
|
breakpointsRefused: breakpoints.refused.length === 0 ? undefined : breakpoints.refused.map((one) => ({
|
|
35299
35313
|
scriptPath: projectSpelling(projectPath, one.scriptPath),
|
|
@@ -35448,6 +35462,7 @@ class GodotServer {
|
|
|
35448
35462
|
running.log.record("warning", `This run was not ended here: the editor could not be asked to stop it (${asked.content[0]?.text ?? "no reason given"}).`);
|
|
35449
35463
|
return "unreached";
|
|
35450
35464
|
}
|
|
35465
|
+
running.endedThrough = "editor";
|
|
35451
35466
|
if (announced === undefined) {
|
|
35452
35467
|
return await this.untilTheEditorStopsPlaying() ? "gone" : "lingering";
|
|
35453
35468
|
}
|
|
@@ -35476,6 +35491,7 @@ class GodotServer {
|
|
|
35476
35491
|
if (why !== null) {
|
|
35477
35492
|
clearTheRecord();
|
|
35478
35493
|
running.endedHere = null;
|
|
35494
|
+
running.notEndedBecause = why;
|
|
35479
35495
|
running.log.record("warning", `This run was not ended here: pid ${running.pid} no longer answers as the process the run was started as (${why}), so nothing was signalled. If that process is still the game, end it yourself; if it is not, it belongs to something else.`);
|
|
35480
35496
|
return "refused";
|
|
35481
35497
|
}
|
|
@@ -35500,6 +35516,9 @@ class GodotServer {
|
|
|
35500
35516
|
running.exitCode = null;
|
|
35501
35517
|
running.exitSignal = "SIGTERM";
|
|
35502
35518
|
}
|
|
35519
|
+
if (landed) {
|
|
35520
|
+
running.endedThrough = "signal";
|
|
35521
|
+
}
|
|
35503
35522
|
clearTheRecord();
|
|
35504
35523
|
running.log.record(gone ? "info" : "warning", declined !== null ? `gdharness could not signal pid ${running.pid}: the operating system refused it (${declined}). It had described that process as ${described}.` : !gone ? `gdharness signalled pid ${running.pid}, which the operating system described as ${described}, and it was still running ${STOP_WAIT_MS / 1000} seconds later.` : landed ? `gdharness ended pid ${running.pid}, which the operating system described as ${described}.` : `gdharness signalled pid ${running.pid} and it was already gone; the operating system had described it as ${described}.`);
|
|
35505
35524
|
return gone ? "gone" : "lingering";
|
|
@@ -35510,6 +35529,9 @@ class GodotServer {
|
|
|
35510
35529
|
this.logDebug(`The keeper of pid ${pid} could not be asked to end it (${asked.unreached})`);
|
|
35511
35530
|
return null;
|
|
35512
35531
|
}
|
|
35532
|
+
if (asked === "signalled") {
|
|
35533
|
+
run.endedThrough = "keeper";
|
|
35534
|
+
}
|
|
35513
35535
|
const gone = await untilGone([pid]);
|
|
35514
35536
|
if (gone) {
|
|
35515
35537
|
await this.untilTheExitIsRecorded(run);
|
|
@@ -36235,11 +36257,13 @@ class GodotServer {
|
|
|
36235
36257
|
endedPid,
|
|
36236
36258
|
throughEditor: stopped.throughEditor,
|
|
36237
36259
|
exitSignal: stopped.exitSignal,
|
|
36238
|
-
clean: cleanVerdict(stopped)
|
|
36260
|
+
clean: cleanVerdict(stopped),
|
|
36261
|
+
...stopped.notEndedBecause === undefined ? {} : { notEndedBecause: stopped.notEndedBecause }
|
|
36239
36262
|
});
|
|
36240
36263
|
return this.jsonTextResponse({
|
|
36241
36264
|
stopped: verdict.stopped,
|
|
36242
36265
|
through: stopped.throughEditor ? "editor" : "gdharness",
|
|
36266
|
+
endedThrough: wasRunning ? stopped.endedThrough : undefined,
|
|
36243
36267
|
endedPid: verdict.endedPid,
|
|
36244
36268
|
notSignalled: verdict.notSignalled,
|
|
36245
36269
|
stillGoing: verdict.stillGoing,
|
|
@@ -36304,6 +36328,7 @@ class GodotServer {
|
|
|
36304
36328
|
through: "gdharness",
|
|
36305
36329
|
endedPid: gone ? pid : undefined,
|
|
36306
36330
|
stillGoing: gone ? undefined : pid,
|
|
36331
|
+
...landed ? { endedThrough: "signal" } : {},
|
|
36307
36332
|
...childrenFields(ended),
|
|
36308
36333
|
exitedBeforeStop: !landed,
|
|
36309
36334
|
note: `${!gone ? `pid ${pid} was told to end and was still running ${STOP_WAIT_MS / 1000} seconds later, so it has not stopped: a game of this project that no server here was holding,` : landed ? `pid ${pid} was ended: a game of this project that no server here was holding,` : `pid ${pid} had already gone when it was signalled: a game of this project that no server here was holding,`} identified by its announcement and by its command line, ${read.executable} run with --path ${read.projectPath ?? ""}. Nothing here read what it printed or holds its exit code, so neither is in this answer.${gone ? "" : " editor_status says when it has gone."} ${aboutTheChildren(ended, false)}`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.27",
|
|
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",
|