gdharness 1.3.56 → 1.3.57
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 +103 -19
- package/build/keeper.js +30 -6
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -1784,12 +1784,16 @@ function slotsDrawnOn(count, taker, queue) {
|
|
|
1784
1784
|
function inAGate(env = process4.env) {
|
|
1785
1785
|
return nonEmpty(env[SHARE_VARIABLE]) !== undefined && nonEmpty(env[SHARE_COUNT_VARIABLE]) !== undefined;
|
|
1786
1786
|
}
|
|
1787
|
-
function inheritsSlot(tree, pid, holder, executable) {
|
|
1787
|
+
function inheritsSlot(tree, pid, holder, executable, engineStarted) {
|
|
1788
1788
|
const program = (name) => name.toLowerCase().replace(/\.(?:exe|cmd|bat|com)$/, "");
|
|
1789
1789
|
const own = program(executable);
|
|
1790
|
-
for (const above of ancestorsIn(tree, pid, holder)) {
|
|
1790
|
+
for (const above of ancestorsIn(tree, pid, holder.pid)) {
|
|
1791
1791
|
if (program(readCommandLine(tree.get(above)?.command ?? "").executable) !== own) {
|
|
1792
|
-
|
|
1792
|
+
if (above === holder.pid) {
|
|
1793
|
+
return true;
|
|
1794
|
+
}
|
|
1795
|
+
const started = tree.get(above)?.startedAt ?? engineStarted;
|
|
1796
|
+
return above === holder.engine?.pid && started !== undefined && Math.abs(started - holder.engine.started) <= START_TOLERANCE_MS;
|
|
1793
1797
|
}
|
|
1794
1798
|
}
|
|
1795
1799
|
return false;
|
|
@@ -1854,20 +1858,29 @@ function holderOf(content) {
|
|
|
1854
1858
|
if (typeof parsed !== "object" || parsed === null) {
|
|
1855
1859
|
return null;
|
|
1856
1860
|
}
|
|
1857
|
-
const {
|
|
1858
|
-
|
|
1861
|
+
const { by, game, engine } = parsed;
|
|
1862
|
+
const named = namedProcess(parsed);
|
|
1863
|
+
if (named === null) {
|
|
1859
1864
|
return null;
|
|
1860
1865
|
}
|
|
1866
|
+
const runs = namedProcess(engine);
|
|
1861
1867
|
return {
|
|
1862
|
-
|
|
1863
|
-
started,
|
|
1868
|
+
...named,
|
|
1864
1869
|
...typeof by === "string" && by !== "" ? { by } : {},
|
|
1865
|
-
...game === true ? { game } : {}
|
|
1870
|
+
...game === true ? { game } : {},
|
|
1871
|
+
...runs === null ? {} : { engine: runs }
|
|
1866
1872
|
};
|
|
1867
1873
|
} catch {
|
|
1868
1874
|
return null;
|
|
1869
1875
|
}
|
|
1870
1876
|
}
|
|
1877
|
+
function namedProcess(value) {
|
|
1878
|
+
if (typeof value !== "object" || value === null) {
|
|
1879
|
+
return null;
|
|
1880
|
+
}
|
|
1881
|
+
const { pid, started } = value;
|
|
1882
|
+
return typeof pid === "number" && Number.isInteger(pid) && pid > 0 && typeof started === "number" ? { pid, started } : null;
|
|
1883
|
+
}
|
|
1871
1884
|
function lockIsFree(content, writtenAt, now, isAlive, startOf) {
|
|
1872
1885
|
const holder = holderOf(content);
|
|
1873
1886
|
if (holder === null) {
|
|
@@ -1981,7 +1994,9 @@ class EngineSlots {
|
|
|
1981
1994
|
return null;
|
|
1982
1995
|
}
|
|
1983
1996
|
const tree = await processTree();
|
|
1984
|
-
|
|
1997
|
+
const engine = holder.engine?.pid;
|
|
1998
|
+
const engineStarted = engine === undefined || tree?.get(engine)?.startedAt !== undefined ? undefined : (await startTimesOf([engine])).get(engine);
|
|
1999
|
+
return tree !== undefined && inheritsSlot(tree, process4.pid, holder, basename3(process4.execPath), engineStarted) ? lock : null;
|
|
1985
2000
|
}
|
|
1986
2001
|
markWaiting() {
|
|
1987
2002
|
const mark = this.waitMarked();
|
|
@@ -2172,6 +2187,45 @@ function handSlot(lock, from, to) {
|
|
|
2172
2187
|
const holder = content === null ? null : holderOf(content);
|
|
2173
2188
|
return holder?.pid === from && writeWhole(lock, JSON.stringify(holder.game === true ? { ...to, game: true } : to));
|
|
2174
2189
|
}
|
|
2190
|
+
function nameSlotEngine(lock, holder, engine) {
|
|
2191
|
+
let content;
|
|
2192
|
+
try {
|
|
2193
|
+
content = readWhole(lock);
|
|
2194
|
+
} catch {
|
|
2195
|
+
return false;
|
|
2196
|
+
}
|
|
2197
|
+
const named = content === null ? null : holderOf(content);
|
|
2198
|
+
return named?.pid === holder && writeWhole(lock, JSON.stringify({ ...named, engine }));
|
|
2199
|
+
}
|
|
2200
|
+
async function nameSlotEngineOf(lock, holder, pid) {
|
|
2201
|
+
const started = (await startTimesOf([pid])).get(pid);
|
|
2202
|
+
if (started !== undefined) {
|
|
2203
|
+
nameSlotEngine(lock, holder, { pid, started });
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
async function slotEngineNamed(lock, run, pidFile) {
|
|
2207
|
+
if (pidFile === undefined) {
|
|
2208
|
+
if (run.pid !== null) {
|
|
2209
|
+
await nameSlotEngineOf(lock, process4.pid, run.pid);
|
|
2210
|
+
}
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2213
|
+
const state = { settled: false };
|
|
2214
|
+
run.settled.finally(() => {
|
|
2215
|
+
state.settled = true;
|
|
2216
|
+
});
|
|
2217
|
+
while (!state.settled) {
|
|
2218
|
+
let written = Number.NaN;
|
|
2219
|
+
try {
|
|
2220
|
+
written = Number(readFileSync6(pidFile, "utf8").trim());
|
|
2221
|
+
} catch {}
|
|
2222
|
+
if (Number.isInteger(written) && written > 0) {
|
|
2223
|
+
await nameSlotEngineOf(lock, process4.pid, written);
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
await delay2(ENGINE_PID_LOOK_MS);
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2175
2229
|
function giveBackSlot(lock, pid) {
|
|
2176
2230
|
try {
|
|
2177
2231
|
if (holderOf(readFileSync6(lock, "utf8"))?.pid === pid) {
|
|
@@ -2290,7 +2344,7 @@ async function inASlot(body) {
|
|
|
2290
2344
|
held.release();
|
|
2291
2345
|
}
|
|
2292
2346
|
}
|
|
2293
|
-
var HELD_VARIABLE = "GODOT_ENGINE_HELD", SLOTS_VARIABLE = "GODOT_ENGINE_SLOTS", SLOT_COUNT_VARIABLE = "GODOT_ENGINE_SLOT_COUNT", PROCESSORS_A_SLOT = 2, START_TOLERANCE_MS = 2000, UNWRITTEN_MS = 1e4, SLOT_LOOK_MS = 1000, SLOT_SETTLE_MS = 50, WAIT_SAID_MS = 20000, COMMAND_WAIT_SAID_MS = 2000, START_READ_MS = 1e4, WAITING_FOLDER = "waiting", QUEUED_FOLDER = "queued", SHARE_VARIABLE = "GODOT_ENGINE_SHARE", SHARE_COUNT_VARIABLE = "GODOT_ENGINE_SHARE_COUNT", GATE_KEPT = 1, TIMED_VARIABLE = "GODOT_ENGINE_TIMED", TIER_VARIABLE = "GODOT_ENGINE_TIER", SEATED_SUFFIX = ".seated", waitsMarked = 0, ours, saidTo, takenBy;
|
|
2347
|
+
var HELD_VARIABLE = "GODOT_ENGINE_HELD", SLOTS_VARIABLE = "GODOT_ENGINE_SLOTS", SLOT_COUNT_VARIABLE = "GODOT_ENGINE_SLOT_COUNT", PROCESSORS_A_SLOT = 2, START_TOLERANCE_MS = 2000, UNWRITTEN_MS = 1e4, SLOT_LOOK_MS = 1000, SLOT_SETTLE_MS = 50, WAIT_SAID_MS = 20000, COMMAND_WAIT_SAID_MS = 2000, START_READ_MS = 1e4, WAITING_FOLDER = "waiting", QUEUED_FOLDER = "queued", SHARE_VARIABLE = "GODOT_ENGINE_SHARE", SHARE_COUNT_VARIABLE = "GODOT_ENGINE_SHARE_COUNT", GATE_KEPT = 1, TIMED_VARIABLE = "GODOT_ENGINE_TIMED", TIER_VARIABLE = "GODOT_ENGINE_TIER", SEATED_SUFFIX = ".seated", waitsMarked = 0, ENGINE_PID_LOOK_MS = 100, ours, saidTo, takenBy;
|
|
2294
2348
|
var init_engine_slots = __esm(() => {
|
|
2295
2349
|
init_alive();
|
|
2296
2350
|
init_cache_home();
|
|
@@ -13189,9 +13243,13 @@ async function watchedEngine(launch, lock, args, directory, log, silenceSeconds,
|
|
|
13189
13243
|
return;
|
|
13190
13244
|
}
|
|
13191
13245
|
};
|
|
13246
|
+
const found = (engine) => {
|
|
13247
|
+
watching(engine, true);
|
|
13248
|
+
nameSlotEngineOf(lock, process7.pid, engine);
|
|
13249
|
+
};
|
|
13192
13250
|
let pid = engineAt();
|
|
13193
13251
|
if (pid !== undefined) {
|
|
13194
|
-
|
|
13252
|
+
found(pid);
|
|
13195
13253
|
}
|
|
13196
13254
|
try {
|
|
13197
13255
|
let size = 0;
|
|
@@ -13206,7 +13264,7 @@ async function watchedEngine(launch, lock, args, directory, log, silenceSeconds,
|
|
|
13206
13264
|
if (pid === undefined) {
|
|
13207
13265
|
pid = engineAt();
|
|
13208
13266
|
if (pid !== undefined) {
|
|
13209
|
-
|
|
13267
|
+
found(pid);
|
|
13210
13268
|
}
|
|
13211
13269
|
}
|
|
13212
13270
|
const taken = await sample();
|
|
@@ -14023,6 +14081,9 @@ function runStarted(executable, args, lock, options, signal) {
|
|
|
14023
14081
|
env: onTheSlot(process.env, lock),
|
|
14024
14082
|
...signal === undefined ? {} : { signal }
|
|
14025
14083
|
});
|
|
14084
|
+
if (child.pid !== undefined) {
|
|
14085
|
+
nameSlotEngineOf(lock, process.pid, child.pid);
|
|
14086
|
+
}
|
|
14026
14087
|
child.stdout.on("data", (chunk) => {
|
|
14027
14088
|
log.append("stdout", chunk);
|
|
14028
14089
|
});
|
|
@@ -19119,7 +19180,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
19119
19180
|
},
|
|
19120
19181
|
wait: {
|
|
19121
19182
|
effect: "read",
|
|
19122
|
-
summary: "wait for the run to end, then answer as editor_output does",
|
|
19183
|
+
summary: "wait for the run to end, or to be held by its debugger at a breakpoint, a pause or a step, then answer as editor_output does",
|
|
19123
19184
|
requires: []
|
|
19124
19185
|
},
|
|
19125
19186
|
profile: {
|
|
@@ -56713,6 +56774,7 @@ ${error.text}`;
|
|
|
56713
56774
|
try {
|
|
56714
56775
|
const run = this.spawnHeld(godotPath, cmdArgs, onTheSlot(env ?? process.env, held.lock), onDesktop);
|
|
56715
56776
|
run.settled.finally(held.release);
|
|
56777
|
+
slotEngineNamed(held.lock, run, onDesktop?.files.pid);
|
|
56716
56778
|
return run;
|
|
56717
56779
|
} catch (error) {
|
|
56718
56780
|
held.release();
|
|
@@ -57455,14 +57517,33 @@ ${error.text}`;
|
|
|
57455
57517
|
return elsewhere;
|
|
57456
57518
|
}
|
|
57457
57519
|
const budgetMs = readPositiveNumber(args, "timeoutMs") ?? WAIT_FOR_RUN_MS;
|
|
57458
|
-
const
|
|
57520
|
+
const began = Date.now();
|
|
57521
|
+
const until = began + budgetMs;
|
|
57459
57522
|
const cancelled = callSignal();
|
|
57523
|
+
let held = false;
|
|
57524
|
+
let holdAsked = 0;
|
|
57460
57525
|
while (cancelled?.aborted !== true && await this.runStillGoing(run) && Date.now() < until) {
|
|
57461
57526
|
this.drainEditorOutput(run);
|
|
57462
57527
|
this.drainTranscript(run);
|
|
57528
|
+
if (Date.now() - holdAsked >= HOLD_ASKED_EVERY_MS) {
|
|
57529
|
+
holdAsked = Date.now();
|
|
57530
|
+
if (await this.heldByItsDebugger(run)) {
|
|
57531
|
+
held = true;
|
|
57532
|
+
break;
|
|
57533
|
+
}
|
|
57534
|
+
}
|
|
57463
57535
|
await delay8(RUN_POLL_MS);
|
|
57464
57536
|
}
|
|
57465
|
-
|
|
57537
|
+
if (!await this.runStillGoing(run)) {
|
|
57538
|
+
return await this.handleGetDebugOutput(args);
|
|
57539
|
+
}
|
|
57540
|
+
return await this.handleGetDebugOutput(args, held ? { heldAfterMs: Date.now() - began } : { ranOutMs: budgetMs });
|
|
57541
|
+
}
|
|
57542
|
+
async heldByItsDebugger(run) {
|
|
57543
|
+
if (!run.throughEditor) {
|
|
57544
|
+
return run.keeper !== undefined && await this.keptHold(run.keeper) !== null;
|
|
57545
|
+
}
|
|
57546
|
+
return this.dapClient?.holdIsKnown() === true && this.dapClient.whereItStopped() !== null;
|
|
57466
57547
|
}
|
|
57467
57548
|
async runStillGoing(run, editorSays) {
|
|
57468
57549
|
if (!run.throughEditor) {
|
|
@@ -57611,7 +57692,7 @@ ${error.text}`;
|
|
|
57611
57692
|
"editor_status names every game running on the machine under runtimes"
|
|
57612
57693
|
]);
|
|
57613
57694
|
}
|
|
57614
|
-
async handleGetDebugOutput(args,
|
|
57695
|
+
async handleGetDebugOutput(args, waited) {
|
|
57615
57696
|
if (readString(args, "op") === "editor") {
|
|
57616
57697
|
return this.handleEditorConsole(args);
|
|
57617
57698
|
}
|
|
@@ -57648,8 +57729,11 @@ ${error.text}`;
|
|
|
57648
57729
|
if (wanted && cpuSeconds === undefined && stillRunning(run)) {
|
|
57649
57730
|
notes.push(processToAsk === null ? "cpu was asked for and there is no process to ask: the editor is playing this run and holds it, and the game announced no runtime, so nothing here has its process id. A game with the runtime addon announces one; editor_run start without a connected editor, or with args, starts the game here instead and that run answers cpuSeconds either way." : "cpu was asked for and this platform would not say what the run has used.");
|
|
57650
57731
|
}
|
|
57651
|
-
if (
|
|
57652
|
-
notes.push(`The wait of ${
|
|
57732
|
+
if (waited !== undefined && "ranOutMs" in waited) {
|
|
57733
|
+
notes.push(`The wait of ${waited.ranOutMs}ms ran out and the run is still going, so this is what it had printed by then rather than everything it will print. editor_run wait again to keep waiting.`);
|
|
57734
|
+
}
|
|
57735
|
+
if (waited !== undefined && "heldAfterMs" in waited) {
|
|
57736
|
+
notes.push(`The wait answered after ${waited.heldAfterMs}ms because the game is held by its debugger, where heldAt says, and a held game does not end: debug_control continue lets it go, and editor_run wait then waits on.`);
|
|
57653
57737
|
}
|
|
57654
57738
|
const restarts = run.restartedFrom ?? [];
|
|
57655
57739
|
if (restarts.length > 0) {
|
|
@@ -59507,7 +59591,7 @@ async function runGodotServer() {
|
|
|
59507
59591
|
const server = new GodotServer;
|
|
59508
59592
|
await server.run();
|
|
59509
59593
|
}
|
|
59510
|
-
var TYPE_ARGUMENTS, UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, FAILED_BUDGET = 1e6, ANSWER_BUDGET = 4000000, __dirname2, PARENT_ASKED_AFTER_MS = 5000, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, ALONGSIDE_NOTE = "Another call on this game was running while this one was: calls sent together run together and are answered as each finishes, so this answer is not from after the other. Send a call meant to come after another once that one has answered.", OWN_DEBUGGER = " Its debugger is the keeper holding it rather than the editor, so the debug_* tools answer for it all the same: breakpoints stop it, and debug_control pause breaks into it wherever it is, which the editor offers no way to do.", PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, KEEPER_DEBUG_MS, EXIT_REPORTED_WITHIN_MS = 2000, SUCCESSOR_SOUGHT_WITHIN_MS = 20000, CLOSE_ASK_MS = 3000, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_SETTLED_MS = 500, SLOW_ANSWER_MS = 1000, REIMPORT_TIMEOUT_MS = 300000, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_TIMEOUT_MS = 600000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", LOOPING_AFTER = 100, WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, DESTINATION_ARGUMENTS, OUTPUT_PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, MOST_PRINTED_LINES = 50, MOST_UNDECLARED_ERRORS = 20, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", RAISED_WARNING = " (Warning treated as error.)", PARSE_ERROR2 = "Parse Error: ", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
59594
|
+
var TYPE_ARGUMENTS, UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, FAILED_BUDGET = 1e6, ANSWER_BUDGET = 4000000, __dirname2, PARENT_ASKED_AFTER_MS = 5000, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, ALONGSIDE_NOTE = "Another call on this game was running while this one was: calls sent together run together and are answered as each finishes, so this answer is not from after the other. Send a call meant to come after another once that one has answered.", OWN_DEBUGGER = " Its debugger is the keeper holding it rather than the editor, so the debug_* tools answer for it all the same: breakpoints stop it, and debug_control pause breaks into it wherever it is, which the editor offers no way to do.", PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, KEEPER_DEBUG_MS, EXIT_REPORTED_WITHIN_MS = 2000, SUCCESSOR_SOUGHT_WITHIN_MS = 20000, CLOSE_ASK_MS = 3000, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_SETTLED_MS = 500, SLOW_ANSWER_MS = 1000, REIMPORT_TIMEOUT_MS = 300000, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_TIMEOUT_MS = 600000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", LOOPING_AFTER = 100, WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, HOLD_ASKED_EVERY_MS = 1000, PATH_SOLUTIONS, DESTINATION_ARGUMENTS, OUTPUT_PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, MOST_PRINTED_LINES = 50, MOST_UNDECLARED_ERRORS = 20, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", RAISED_WARNING = " (Warning treated as error.)", PARSE_ERROR2 = "Parse Error: ", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
59511
59595
|
var init_server = __esm(() => {
|
|
59512
59596
|
init_dist2();
|
|
59513
59597
|
init_stdio();
|
package/build/keeper.js
CHANGED
|
@@ -8833,20 +8833,29 @@ function holderOf(content) {
|
|
|
8833
8833
|
if (typeof parsed !== "object" || parsed === null) {
|
|
8834
8834
|
return null;
|
|
8835
8835
|
}
|
|
8836
|
-
const {
|
|
8837
|
-
|
|
8836
|
+
const { by, game, engine } = parsed;
|
|
8837
|
+
const named = namedProcess(parsed);
|
|
8838
|
+
if (named === null) {
|
|
8838
8839
|
return null;
|
|
8839
8840
|
}
|
|
8841
|
+
const runs = namedProcess(engine);
|
|
8840
8842
|
return {
|
|
8841
|
-
|
|
8842
|
-
started,
|
|
8843
|
+
...named,
|
|
8843
8844
|
...typeof by === "string" && by !== "" ? { by } : {},
|
|
8844
|
-
...game === true ? { game } : {}
|
|
8845
|
+
...game === true ? { game } : {},
|
|
8846
|
+
...runs === null ? {} : { engine: runs }
|
|
8845
8847
|
};
|
|
8846
8848
|
} catch {
|
|
8847
8849
|
return null;
|
|
8848
8850
|
}
|
|
8849
8851
|
}
|
|
8852
|
+
function namedProcess(value) {
|
|
8853
|
+
if (typeof value !== "object" || value === null) {
|
|
8854
|
+
return null;
|
|
8855
|
+
}
|
|
8856
|
+
const { pid, started } = value;
|
|
8857
|
+
return typeof pid === "number" && Number.isInteger(pid) && pid > 0 && typeof started === "number" ? { pid, started } : null;
|
|
8858
|
+
}
|
|
8850
8859
|
async function ownStart() {
|
|
8851
8860
|
const said = (await startTimesOf([process4.pid])).get(process4.pid);
|
|
8852
8861
|
return said ?? Math.round(Date.now() - process4.uptime() * 1000);
|
|
@@ -8861,6 +8870,16 @@ function handSlot(lock, from, to) {
|
|
|
8861
8870
|
const holder = content === null ? null : holderOf(content);
|
|
8862
8871
|
return holder?.pid === from && writeWhole(lock, JSON.stringify(holder.game === true ? { ...to, game: true } : to));
|
|
8863
8872
|
}
|
|
8873
|
+
function nameSlotEngine(lock, holder, engine) {
|
|
8874
|
+
let content;
|
|
8875
|
+
try {
|
|
8876
|
+
content = readWhole(lock);
|
|
8877
|
+
} catch {
|
|
8878
|
+
return false;
|
|
8879
|
+
}
|
|
8880
|
+
const named = content === null ? null : holderOf(content);
|
|
8881
|
+
return named?.pid === holder && writeWhole(lock, JSON.stringify({ ...named, engine }));
|
|
8882
|
+
}
|
|
8864
8883
|
function giveBackSlot(lock, pid) {
|
|
8865
8884
|
try {
|
|
8866
8885
|
if (holderOf(readFileSync5(lock, "utf8"))?.pid === pid) {
|
|
@@ -11067,10 +11086,12 @@ async function keep() {
|
|
|
11067
11086
|
});
|
|
11068
11087
|
const slot = run?.slot;
|
|
11069
11088
|
if (slot !== undefined && keeperStarted !== undefined) {
|
|
11089
|
+
const gameStarted = (await startTimesOf([pid])).get(pid);
|
|
11070
11090
|
handSlot(slot.lock, slot.from, {
|
|
11071
11091
|
pid: process.pid,
|
|
11072
11092
|
started: await keeperStarted,
|
|
11073
|
-
by: `${slot.by}, through the keeper process that runs it
|
|
11093
|
+
by: `${slot.by}, through the keeper process that runs it`,
|
|
11094
|
+
...gameStarted === undefined ? {} : { engine: { pid, started: gameStarted } }
|
|
11074
11095
|
});
|
|
11075
11096
|
}
|
|
11076
11097
|
let { code, signal } = await exited;
|
|
@@ -11106,6 +11127,9 @@ async function keep() {
|
|
|
11106
11127
|
restartedFrom: [...note.restartedFrom ?? [], before]
|
|
11107
11128
|
};
|
|
11108
11129
|
writeNoteIfItCan(note);
|
|
11130
|
+
if (slot !== undefined) {
|
|
11131
|
+
nameSlotEngine(slot.lock, process.pid, { pid: next.pid, started: next.startedAt });
|
|
11132
|
+
}
|
|
11109
11133
|
end = () => {
|
|
11110
11134
|
try {
|
|
11111
11135
|
process.kill(next.pid);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.57",
|
|
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",
|