101 lines
5.3 KiB
JavaScript
101 lines
5.3 KiB
JavaScript
// Exercise the actual common/leaderboard chat listeners with private reply shims.
|
|
// This does not emulate Plutonium's VM or native chat delivery.
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import vm from 'node:vm';
|
|
|
|
const root = new URL('../', import.meta.url);
|
|
let checks = 0;
|
|
function translated(source, name) {
|
|
const match = new RegExp(`^${name}\\(([^\\n]*)\\)\\s*\\n\\{`, 'm').exec(source);
|
|
assert(match, name);
|
|
const start = match.index + match[0].length;
|
|
let end = start, depth = 1;
|
|
while (depth) {
|
|
const ch = source[end++];
|
|
if (ch === '{') depth++;
|
|
if (ch === '}') depth--;
|
|
}
|
|
let body = source.slice(start, end - 1);
|
|
const locals = new Set([...body.matchAll(/(?<![\w.])(\w+)\s*=(?!=)/g)].map(m => m[1]));
|
|
const listener = name.endsWith('_listener');
|
|
if (listener) { locals.add('message'); locals.add('player'); }
|
|
for (const param of match[1].split(',').map(s => s.trim())) locals.delete(param);
|
|
body = body.replace(/level endon\([^;]*\);/g, '')
|
|
.replace(/level waittill\("say", message, player\);/g, '[message, player] = yield "say";')
|
|
.replace(/player show_help\(\);/g, 'help.push(player);')
|
|
.replace(/player thread lb_request\(true\);/g, 'records.push(player);')
|
|
.replace(/player iPrintLn\(/g, 'player.replies.push(')
|
|
.replace(/\.size\b/g, '.length');
|
|
return `function${listener ? '*' : ''} ${name}(${match[1]}) { var ${[...locals].join(', ') || 'unused'}; ${body} }`;
|
|
}
|
|
|
|
for (const game of ['t5', 't6']) {
|
|
const common = readFileSync(new URL(`${game}/scripts/tws_${game}_common.gsc`, root), 'utf8');
|
|
const leaderboard = readFileSync(new URL(`${game}/scripts/tws_${game}_leaderboard.gsc`, root), 'utf8');
|
|
const help = [], records = [], sender = { replies: [] }, other = { replies: [] };
|
|
const c = vm.createContext({
|
|
level: {}, help, records,
|
|
isDefined: value => value !== undefined && value !== null,
|
|
toLower: value => value.toLowerCase(),
|
|
strTok: value => value.split(/[ \t\r\n]+/).filter(Boolean),
|
|
getSubStr: (value, start, end) => value.slice(start, end),
|
|
spawnStruct: () => ({}),
|
|
});
|
|
for (const name of ['register_help_command', 'help_first_word', 'help_unknown_command', 'help_chat_listener'])
|
|
vm.runInContext(translated(common, name), c);
|
|
vm.runInContext(translated(leaderboard, 'lb_chat_listener'), c);
|
|
// Missing/disabled optional modules must not suppress unknown-command replies.
|
|
assert.equal(c.help_unknown_command('.record'), true);
|
|
assert.equal(c.help_unknown_command('.w 2000'), true);
|
|
c.register_help_command('.record', 'Custom record command');
|
|
assert.equal(c.help_unknown_command('.record'), false);
|
|
c.level.tws_help_commands = [];
|
|
checks += 3;
|
|
const chat = c.help_chat_listener(), lb = c.lb_chat_listener();
|
|
assert.equal(chat.next().value, 'say');
|
|
assert.equal(lb.next().value, 'say');
|
|
for (const file of ['common', 'afk', 'bank', 'hud', 'join', 'lock', 'leaderboard']) {
|
|
const source = readFileSync(new URL(`${game}/scripts/tws_${game}_${file}.gsc`, root), 'utf8');
|
|
for (const [, command, description] of source.matchAll(/register_help_command\("([^"]+)", "([^"]+)"\)/g))
|
|
c.register_help_command(command, description);
|
|
}
|
|
c.register_help_command('!custom <value>', 'Custom bang command');
|
|
const send = (message, player = sender) => {
|
|
sender.replies.length = 0; other.replies.length = 0; help.length = 0; records.length = 0;
|
|
assert.equal(chat.next([message, player]).value, 'say');
|
|
assert.equal(lb.next([message, player]).value, 'say');
|
|
};
|
|
for (const message of ['.unknown', '!unknown', '.', '!', '.return', '.records', '.helpful', '!help']) {
|
|
send(message);
|
|
assert.deepEqual(sender.replies, ['Unknown command. Type .help for the list'], `${game} ${message}`);
|
|
assert.deepEqual(other.replies, []);
|
|
assert.deepEqual(help, []); assert.deepEqual(records, []);
|
|
checks++;
|
|
}
|
|
for (const message of ['', 'hello', 'info', 'hello .unknown', ' .unknown', undefined,
|
|
'.w 2000', '.W ALL', '.d 500', '.w\t2000', '.afk', '.afk buy 10', '.lock', '.unlock', '!custom 5',
|
|
'.help extra', '.record extra']) {
|
|
send(message);
|
|
assert.deepEqual(sender.replies, [], `${game} ${message}`);
|
|
assert.deepEqual(help, []); assert.deepEqual(records, []);
|
|
checks++;
|
|
}
|
|
for (const message of ['.help', '.HELP', '.commands', '.info', '.INFO', '.command', '.COMMAND']) {
|
|
send(message, other);
|
|
assert.deepEqual(help, [other]); assert.deepEqual(records, []);
|
|
assert.deepEqual(sender.replies, []); assert.deepEqual(other.replies, []);
|
|
checks++;
|
|
}
|
|
for (const message of ['.leaders', '.LEADERS', '.record', '.RECORD']) {
|
|
send(message, other);
|
|
assert.deepEqual(records, [other]); assert.deepEqual(help, []);
|
|
assert.deepEqual(sender.replies, []); assert.deepEqual(other.replies, []);
|
|
checks++;
|
|
}
|
|
send('.unknown', null);
|
|
assert.deepEqual(sender.replies, []); assert.deepEqual(help, []); assert.deepEqual(records, []);
|
|
checks++;
|
|
}
|
|
console.log(`PASS ${checks} command discovery checks (both games). Native chat delivery still requires Plutonium.`);
|