chore: initial commit
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user