Compare commits
2 Commits
73a667eeb4
...
e785aaa6a2
Author | SHA1 | Date | |
---|---|---|---|
e785aaa6a2 | |||
2fa279e3c1 |
@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import { readJsonFile, writeJsonFile } from '../utils/index.mjs';
|
||||
import { readJsonFile, writeJsonFile } from 'compass-utils';
|
||||
|
||||
export class NoCredentialError extends Error {
|
||||
constructor(profile) {
|
||||
@ -46,6 +46,10 @@ export class Account {
|
||||
return new SessionCache(auth_info.accessToken, auth_info.clientToken
|
||||
, auth_info.selectedProfile, auth_info.availableProfiles);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.handle;
|
||||
}
|
||||
};
|
||||
|
||||
export class Credentials {
|
||||
@ -177,8 +181,3 @@ export class SessionCache {
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
NoCredentialError, ProfileNotSelectedError, ProfileAlreadySelectedError
|
||||
, YggdrasilEndpoint, Account, Credentials, SessionCache
|
||||
};
|
64
behavior-tree/index.mjs
Normal file
64
behavior-tree/index.mjs
Normal file
@ -0,0 +1,64 @@
|
||||
|
||||
export class BehaviorTree {
|
||||
constructor(root) {
|
||||
this.root = root;
|
||||
}
|
||||
};
|
||||
|
||||
export class Node {
|
||||
constructor() {}
|
||||
};
|
||||
|
||||
export class ExecutionNode extends Node {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
isLeaf() { return true; }
|
||||
};
|
||||
|
||||
export class ControlNode extends Node {
|
||||
constructor() {
|
||||
super();
|
||||
this.children = [];
|
||||
}
|
||||
|
||||
isLeaf() { return false; }
|
||||
appendChild(child) {
|
||||
this.children.push(child);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
export class SequenceNode extends ControlNode {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async tick(blackboard) {
|
||||
for (let child of this.children) {
|
||||
await child.tick(blackboard);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export class FallbackNode extends ControlNode {
|
||||
constructor() { super(); }
|
||||
|
||||
async tick(blackboard) {
|
||||
for (let i = 0; i < this.children.length; i += 1) {
|
||||
try {
|
||||
await child.tick(blackboard);
|
||||
break;
|
||||
} catch(err) {
|
||||
if (i == this.children.length - 1) { throw err; }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export class ParallelNode extends ControlNode {
|
||||
constructor() { super(); }
|
||||
|
||||
async tick(blackboard) {}
|
||||
}
|
10
behavior-tree/package.json
Normal file
10
behavior-tree/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "compass-behavior-tree",
|
||||
"description": "Behavior Tree Library",
|
||||
"type": "module",
|
||||
"main": "index.mjs",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"compass-utils": "file:../utils"
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import authlib from './auth/authlib.mjs';
|
||||
import * as authlib from './auth/authlib.mjs';
|
||||
import mineflayer from 'mineflayer';
|
||||
import yargs from 'yargs';
|
||||
import { parseLogin, waitEvent } from './utils/index.mjs';
|
||||
import { parseLogin, waitEvent } from 'compass-utils';
|
||||
import repl from 'node:repl';
|
||||
import vm from 'node:vm';
|
||||
|
||||
@ -37,9 +37,12 @@ async function main() {
|
||||
host, port, version: args.protocal,
|
||||
...session.mineflayer(credential_info.endpoint)
|
||||
});
|
||||
bot.on('error', console.error);
|
||||
bot.on('kicked', console.log);
|
||||
|
||||
await waitEvent(bot, 'inject_allowed');
|
||||
bot.loadPlugin((await import('mineflayer-event-promise')).default);
|
||||
bot.loadPlugin((await import('mineflayer-control')).default);
|
||||
await bot.waitEvent('spawn');
|
||||
|
||||
let context = vm.createContext();
|
||||
|
@ -12,6 +12,8 @@
|
||||
"debug": "^4.3.4",
|
||||
"mineflayer": "^4.14.0",
|
||||
"mineflayer-event-promise": "file:plugin/event-promise",
|
||||
"compass-utils": "file:utils",
|
||||
"mineflayer-control": "file:plugin/control",
|
||||
"mineflayer-pathfinder": "^2.4.5",
|
||||
"prismarine-viewer": "^1.25.0",
|
||||
"yargs": "^17.7.2"
|
||||
|
180
plugin/control/index.mjs
Normal file
180
plugin/control/index.mjs
Normal file
@ -0,0 +1,180 @@
|
||||
import debug from 'debug';
|
||||
import { Task } from 'compass-utils';
|
||||
import { Vec3 } from 'vec3';
|
||||
const logger = debug('mineflayer-control');
|
||||
|
||||
// yaw = axis * Math.PI / 2
|
||||
// name = "ZX"[axis % 2]
|
||||
export const AXIS = {
|
||||
'-Z': 0,
|
||||
'-X': 1,
|
||||
'+Z': 2,
|
||||
'+X': 3,
|
||||
NORTH: 0,
|
||||
WEST: 1,
|
||||
SOUTH: 2,
|
||||
EAST: 3,
|
||||
0: 0,
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
};
|
||||
|
||||
export const AXIS_UNIT = {
|
||||
0: new Vec3(0, 0, -1),
|
||||
1: new Vec3(-1, 0, 0),
|
||||
2: new Vec3(0, 0, 1),
|
||||
3: new Vec3(1, 0, 0),
|
||||
};
|
||||
|
||||
AXIS_UNIT['-Z'] = AXIS_UNIT['NORTH'] = AXIS_UNIT[0];
|
||||
AXIS_UNIT['-X'] = AXIS_UNIT['WEST'] = AXIS_UNIT[1];
|
||||
AXIS_UNIT['+Z'] = AXIS_UNIT['SOUTH'] = AXIS_UNIT[2];
|
||||
AXIS_UNIT['+X'] = AXIS_UNIT['EAST'] = AXIS_UNIT[3];
|
||||
|
||||
export const MOVE_LEVEL = {
|
||||
WALK: 1,
|
||||
SPRINT: 2,
|
||||
// TODO: SPRINT_JUMP
|
||||
};
|
||||
|
||||
export class ControlState {
|
||||
constructor() {
|
||||
for (let key of ControlState.CONTROLS) {
|
||||
this[key] = false;
|
||||
}
|
||||
}
|
||||
|
||||
set(cs) {
|
||||
for (let key of ControlState.CONTROLS) {
|
||||
this[key] = cs[key] || false;
|
||||
}
|
||||
}
|
||||
|
||||
static from(cs) {
|
||||
let res = new ControlState();
|
||||
res.set(cs);
|
||||
return res;
|
||||
}
|
||||
|
||||
apply(bot) {
|
||||
for (let key of ControlState.CONTROLS) {
|
||||
bot.setControlState(key, this[key]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ControlState.CONTROLS = ['forward', 'back', 'left', 'right', 'jump', 'sprint', 'sneak'];
|
||||
|
||||
function adjust05(x) {
|
||||
return Math.floor(x) + .5;
|
||||
}
|
||||
|
||||
function adjustXZ(vec) {
|
||||
vec.x = adjust05(vec.x);
|
||||
vec.z = adjust05(vec.z);
|
||||
}
|
||||
|
||||
export class MoveInterferedError extends Error {
|
||||
constructor() { super('Move task has been interfered by an external force.'); }
|
||||
};
|
||||
|
||||
async function moveAxisTask(bot, task, axis_raw, target_raw, level) {
|
||||
const axis = AXIS[axis_raw];
|
||||
const stable_axis = "xz"[axis % 2];
|
||||
const target = target_raw.clone();
|
||||
adjustXZ(target);
|
||||
|
||||
bot.clearControlStates();
|
||||
bot.control.adjustXZ();
|
||||
|
||||
let pos = bot.entity.position;
|
||||
const delta = target.minus(pos);
|
||||
let remaining_dis = delta.dot(AXIS_UNIT[axis]);
|
||||
|
||||
logger(`moveAxisTask() source: ${pos}.`);
|
||||
logger(`moveAxisTask() target: ${target}.`);
|
||||
logger(`moveAxisTask() delta: ${delta}.`);
|
||||
logger(`moveAxisTask() distance: ${remaining_dis}.`);
|
||||
logger(`moveAxisTask() stable_axis: ${stable_axis}.`);
|
||||
logger(`moveAxisTask() Condition: ${delta[stable_axis]} ${delta.y}.`);
|
||||
if (Math.abs(delta.y) > Number.EPSILON || Math.abs(delta[stable_axis]) > Number.EPSILON) {
|
||||
throw new Error('Invalid Argument: target');
|
||||
}
|
||||
|
||||
if (remaining_dis < 0) {
|
||||
throw new Error('Invalid Argument: axis argument should reverse its sign.');
|
||||
}
|
||||
|
||||
const stable_axis_value = target[stable_axis];
|
||||
|
||||
logger('moveAxisTask() pre adjust look angle');
|
||||
await bot.look(axis * Math.PI / 2, 0);
|
||||
logger('moveAxisTask() post adjust look angle');
|
||||
task._interuptableHere();
|
||||
|
||||
const controls = new ControlState();
|
||||
controls.forward = true;
|
||||
if (level >= MOVE_LEVEL.SPRINT) { controls.sprint = true; }
|
||||
logger('moveAxisTask() control', controls);
|
||||
controls.apply(bot);
|
||||
logger('moveAxisTask() started.');
|
||||
|
||||
let time_used = 0;
|
||||
do {
|
||||
await bot.waitForTicks(1);
|
||||
task._interuptableHere();
|
||||
|
||||
controls.apply(bot);
|
||||
time_used += 1;
|
||||
pos = bot.entity.position;
|
||||
if (Math.abs(pos[stable_axis] - stable_axis_value) > 1.2) {
|
||||
logger('moveAxisTask() stable axis changed.');
|
||||
logger(`moveAxisTask() target.${stable_axis}: ${stable_axis_value}.`);
|
||||
logger(`moveAxisTask() pos.${stable_axis}: ${pos[stable_axis]}.`);
|
||||
throw new MoveInterferedError();
|
||||
}
|
||||
pos[stable_axis] = stable_axis_value;
|
||||
|
||||
delta.update(target.minus(pos));
|
||||
remaining_dis = delta.dot(AXIS_UNIT[axis]);
|
||||
if (Math.abs(remaining_dis) <= 0.5) {
|
||||
logger('moveAxisTask() very close to target now.');
|
||||
pos.update(target);
|
||||
bot.entity.velocity.x = 0;
|
||||
bot.entity.velocity.z = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (remaining_dis < -0.5) {
|
||||
logger('moveAxisTask() went past target.');
|
||||
throw new MoveInterferedError();
|
||||
}
|
||||
} while (true);
|
||||
bot.clearControlStates();
|
||||
task._ready(time_used);
|
||||
}
|
||||
|
||||
export default function inject(bot) {
|
||||
bot.control = {};
|
||||
bot.control.getState = () => { return ControlState.from(bot.controlState); };
|
||||
bot.control.adjustXZ = () => { adjustXZ(bot.entity.position); };
|
||||
|
||||
bot.control.moveAxis = (axis, target, level = MOVE_LEVEL.SPRINT) => {
|
||||
let task = new Task();
|
||||
queueMicrotask(() => {
|
||||
task._start();
|
||||
moveAxisTask(bot, task, axis, target, level).catch(err => {
|
||||
bot.clearControlStates();
|
||||
task._fail(err);
|
||||
});
|
||||
});
|
||||
return task;
|
||||
};
|
||||
|
||||
bot.control.jump = async () => {
|
||||
bot.setControlState('jump', true);
|
||||
await bot.waitForTicks(1);
|
||||
bot.setControlState('jump', false);
|
||||
};
|
||||
}
|
11
plugin/control/package.json
Normal file
11
plugin/control/package.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "mineflayer-control",
|
||||
"description": "High-level & handy API for mineflayer bot movement control.",
|
||||
"type": "module",
|
||||
"main": "index.mjs",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"vec3": "^0.1.8",
|
||||
"compass-utils": "file:../../utils"
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
export class TaskInteruptedError {
|
||||
export class TaskInteruptedError extends Error {
|
||||
constructor() { super('Task has been interupted.'); }
|
||||
};
|
||||
|
||||
@ -45,6 +45,13 @@ export class Task {
|
||||
this.#notifyFailure();
|
||||
}
|
||||
|
||||
_interuptableHere() {
|
||||
if (this._shouldInterupt()) {
|
||||
this._confirmInterupt();
|
||||
throw this.error;
|
||||
}
|
||||
}
|
||||
|
||||
get() {
|
||||
if (this.status == Task.STATUS.ready) { return Promise.resolve(this.result); }
|
||||
if (this.status == Task.STATUS.failed || this.status == Task.STATUS.interupted) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user