Improve command discovery and welcome messages

This commit is contained in:
2026-10-19 14:37:24 -04:00
parent e93182cd02
commit a457cbe539
12 changed files with 378 additions and 26 deletions
+31 -1
View File
@@ -34,7 +34,7 @@ function functions(source) {
let body = protectedSource.slice(headers.lastIndex, end - 1);
// Each GSC function has its own implicit locals, including recursion.
const locals = new Set([...body.matchAll(/(?<![\w.])(\w+)\s*(?:[+*/%-]?=(?!=)|\+\+|--)/g)].map((x) => x[1]));
const coroutine = ['lb_round_watcher', 'lb_get'].includes(match[1]);
const coroutine = ['lb_round_watcher', 'lb_get', 'lb_welcome'].includes(match[1]);
body = body.replace(/player getGuid\(/g, 'player.getGuid(').replace(/\btypeof\(/g, 'gscType(').replace(/\.size\b/g, '.length')
.replace(/\bwait\s+([\d.]+);/g, coroutine ? 'yield { milliseconds: $1 * 1000 };' : ';');
if (coroutine)
@@ -172,6 +172,36 @@ for (const game of ['t5', 't6']) {
if (game === 't6') assert(!all.get('lb_welcome').includes('self.hasSpawned'));
assert(source.includes('register_connect_handler(::lb_welcome)'));
});
check(`${game} welcome sends one private native hint before waiting for record data`, () => {
const e = engine(game), c = e.context, replies = [], requests = [];
const other = { replies: [] };
c.self = { iPrintLn: message => replies.push(message) };
c.level.tws_lb_attempt = 0;
c.welcomeRecord = manual => requests.push(manual);
const code = e.all.get('lb_welcome')
.replace(/\b(?:self|level) endon\([^;]*\);/g, '')
.replace(/self iPrintLn\(/g, 'self.iPrintLn(')
.replace(/self lb_request\(/g, 'welcomeRecord(');
vm.runInContext(code, c);
const welcome = c.lb_welcome();
assert.equal(welcome.next().value.milliseconds, 250, 'wait for readiness');
assert.deepEqual(replies, []);
c.self.sessionstate = 'spectator';
assert.equal(welcome.next().value.milliseconds, 5000, 'retain the welcome delay');
assert.deepEqual(replies, []);
assert.equal(welcome.next().value.milliseconds, 250, 'record fetch can still be pending');
assert.deepEqual(replies, ['Type .help for commands']);
assert.deepEqual(other.replies, []);
assert.deepEqual(requests, []);
assert.equal(welcome.next().value.milliseconds, 250);
assert.equal(replies.length, 1, 'waiting does not repeat the hint');
c.level.tws_lb_attempt = 1;
assert.equal(welcome.next().done, true);
assert.deepEqual(requests, [false]);
assert.equal(c.lb_welcome().next().done, true, 'duplicate connection dispatch stays silent');
assert.equal(replies.length, 1);
assert(!e.all.get('lb_request').includes('iPrintLn'), 'manual replay does not print the hint');
});
check(`${game} record replay uses only a fresh complete cache`, () => {
assert.equal(c.lb_cached_record(), undefined);
const snapshot = { saved_at: 1000000, round: 52, lines: ['Alice'] };