178 lines
7.5 KiB
JavaScript
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);
|
|
});
|