Files
node_server/tests/stop_current_function.test.mjs
2026-10-22 17:07:08 +08:00

178 lines
7.5 KiB
JavaScript

import assert from 'node:assert/strict';
import {EventEmitter} from 'node:events';
import {createRequire} from 'node:module';
import test from 'node:test';
import {tsImport} from 'tsx/esm/api';
const {create_function_manager} = await tsImport('../bot/function/init.ts', import.meta.url);
const {init: Stop} = await tsImport('../bot/function/bot/stop_current_function.ts', import.meta.url);
const require = createRequire(import.meta.url);
const {Vec3} = createRequire(require.resolve('@wp2508/mineflayer'))('vec3');
const turn = () => new Promise(resolve => setImmediate(resolve));
async function fixture(t) {
const bot = new EventEmitter();
bot.entity = {id: 1, position: new Vec3(0, 0, 0), height: 1.8, eyeHeight: 1.62, onGround: true};
bot._client = Object.assign(new EventEmitter(), {state: 'play'});
bot.controlState = {};
bot.getControlState = key => bot.controlState[key] === true;
bot.setControlState = (key, value) => { bot.controlState[key] = value; };
bot.stopDigging = () => { bot.targetDigBlock = null; };
bot.deactivateItem = () => {};
bot.clearControlStates = () => { for (const key of Object.keys(bot.controlState)) bot.setControlState(key, false); };
const manager = await create_function_manager({bot_instance: bot, bot_status: 'online'});
t.after(() => manager.dispose());
return {bot, manager};
}
function clean(bot) {
assert.deepEqual(bot.eventNames(), []);
assert.deepEqual(bot._client.eventNames(), []);
}
test('stop bypasses a full queue, releases direction and persistent sneak, and never restarts pending keys', async t => {
const {bot, manager} = await fixture(t);
await manager.run('bot', 'squat_continue_switch', [true]);
const running = manager.run('bot', 'use_direction_key', ['forward', 5000]);
await turn();
assert.equal(bot.controlState.forward, true);
const queued = Array.from({length: 64}, () => manager.run('bot', 'use_direction_key', ['right', 5000]));
let queuedChecks = 0;
// Queue capacity applies to normal calls, never to the stop command.
await assert.rejects(manager.run('bot', 'jump', [], () => queuedChecks++), /队列已满/);
assert.equal(await manager.run('bot', 'stop_current_function', []), true);
assert.equal(await running, false);
assert.deepEqual(await Promise.all(queued), Array(64).fill(false));
await turn();
assert.equal(queuedChecks, 0);
assert.deepEqual(bot.controlState, {sneak: false, forward: false});
clean(bot);
const fresh = manager.run('bot', 'jump', []);
await turn();
bot.emit('physicsTick');
assert.equal(await fresh, true);
clean(bot);
});
test('stop validates no arguments and attempts every stop API even when earlier calls throw', async () => {
const stop = new Stop();
assert.deepEqual(stop.args, []);
for (const failing of ['stopDigging', 'deactivateItem', 'clearControlStates', null]) {
const calls = [];
const bot = {};
for (const name of ['stopDigging', 'deactivateItem', 'clearControlStates']) bot[name] = () => {
calls.push(name);
if (name === failing) throw Error(name);
};
assert.equal(await stop.run(bot, [1]), false);
assert.deepEqual(calls, []);
assert.equal(await stop.run(bot, []), failing === null);
assert.deepEqual(calls, ['stopDigging', 'deactivateItem', 'clearControlStates']);
}
});
test('stop settles pending look immediately and late entity look cannot attack or restart queued work', async t => {
const {bot, manager} = await fixture(t);
const target = {id: 2, name: 'cow', position: new Vec3(2, 0, 0), width: 1, height: 2};
bot.entities = {2: target};
let finishLook;
bot.lookAt = () => new Promise(resolve => { finishLook = resolve; });
let attacks = 0;
bot.attack = () => { attacks++; };
const running = manager.run('entity', 'attack_nearest_entity', ['cow', 4, false, true]);
await turn();
let before = 0;
const queued = manager.run('bot', 'jump', [], () => before++);
assert.equal(await manager.run('bot', 'stop_current_function', []), true);
assert.equal(await running, false);
assert.equal(await queued, false);
clean(bot);
finishLook();
await turn();
assert.equal(attacks, 0);
assert.equal(before, 0);
clean(bot);
});
test('stop cancels container waits and block groups without fake disconnects or later group actions', async t => {
const {bot, manager} = await fixture(t);
let ends = 0;
bot.on('end', () => ends++);
bot.entities = {2: {id: 2, name: 'cow', position: new Vec3(2, 0, 0), width: 1, height: 2}};
bot.interactEntity = () => {};
const open = manager.run('entity', 'open_nearest_entity_container', ['cow', 4, false]);
await turn();
assert.equal(bot.listenerCount('windowOpen'), 1);
await manager.run('bot', 'stop_current_function', []);
assert.equal(await open, false);
assert.equal(bot.listenerCount('windowOpen'), 0);
const block = {name: 'stone', type: 1, stateId: 1, position: new Vec3(2, 1, 0)};
bot.blockAt = () => block;
let finishUse;
let uses = 0;
bot.activateBlock = () => { uses++; return new Promise(resolve => { finishUse = resolve; }); };
const group = manager.run('block', 'use_position_group_block', [[2, 1, 0, 1, 2, 1, 0, 1]]);
await turn();
await manager.run('bot', 'stop_current_function', []);
assert.equal(await group, false);
finishUse();
await turn();
assert.equal(uses, 1);
assert.equal(ends, 0);
bot.removeAllListeners('end');
clean(bot);
});
test('stop prevents remaining inventory clicks after an already submitted click settles', async t => {
const {bot, manager} = await fixture(t);
for (const name of ['slot_to_slot', 'slot_switch_slot']) {
bot.inventory = {slots: [{name: 'stone'}, null], selectedItem: null};
let finishClick;
const clicks = [];
bot.clickWindow = slot => { clicks.push(slot); return new Promise(resolve => { finishClick = resolve; }); };
const running = manager.run('inventory', name, [0, 1]);
await turn();
assert.deepEqual(clicks, [0]);
await manager.run('bot', 'stop_current_function', []);
assert.equal(await running, false);
finishClick();
await turn();
assert.deepEqual(clicks, [0]);
}
clean(bot);
});
test('stop releases dig ownership during pending aim and stops an already active dig', async t => {
const {bot, manager} = await fixture(t);
const block = {name: 'stone', type: 1, stateId: 1, position: new Vec3(2, 1, 0)};
bot.blockAt = () => block;
bot.canDigBlock = () => true;
let finishLook;
bot.lookAt = () => new Promise(resolve => { finishLook = resolve; });
let digs = 0;
let finishDig;
bot.dig = () => {
digs++;
bot.targetDigBlock = block;
return new Promise(resolve => { finishDig = resolve; });
};
const aiming = manager.run('block', 'destroy_position_block', [2, 1, 0, 1, true]);
await turn();
await manager.run('bot', 'stop_current_function', []);
assert.equal(await aiming, false);
clean(bot);
finishLook();
await turn();
assert.equal(digs, 0);
const digging = manager.run('block', 'destroy_position_block', [2, 1, 0, 1]);
await turn();
assert.equal(digs, 1);
assert.equal(bot.targetDigBlock, block);
await manager.run('bot', 'stop_current_function', []);
assert.equal(await digging, false);
assert.equal(bot.targetDigBlock, null);
finishDig();
await turn();
clean(bot);
});