Compare commits

...

30 Commits

Author SHA1 Message Date
jay
feb0b0927a feat(informer): add villager profession details
Provide names in place of numbers for villager profession.
Implementation uses an enum
2021-05-15 18:37:24 +05:00
jay
8276e68489 feat(informer): add block info based on relative position for players 2021-05-11 17:59:26 +05:00
jay
2b7163bef3 feat(informer): add block info based on other players
Now should work for position of players other than the callee.

Alternative implementation could use `entity` function.
This allows to also work for named entities as a bonus.
2021-05-11 17:54:19 +05:00
jay
a0b4641f37 feat(informer): add entity detail by type and kind for some entities
So far:
- mobs
 - villager
- object
 - drops
2021-05-11 17:42:31 +05:00
jay
186d6ac3d2 feat(informer): 🚸 add more detail to entity info 2021-05-11 17:36:41 +05:00
jay
984c9490c3 fix(informer): 🥅 make player undefined if nul
This allows to fallback to defaults in functions
2021-05-11 17:34:25 +05:00
jay
3379f75ab9 feat(informer): 🚸 make entity search case insensitive 2021-05-11 17:32:38 +05:00
jay
69b1ab0b0b feat(informer): add reverse mcdata lookup by block or item id 2021-05-11 14:44:22 +05:00
jay
3c9d62441f feat(informer): add block info based on player's relative position
Relative position includes simple words like "feet", "standing", "head", etc
2021-05-11 10:39:53 +05:00
jay
665102e19c fix(informer): 🚑 remove extra let cfg declaration left over from refactor 2021-05-11 09:46:07 +05:00
jay
db459f52e6 fix(informer): ✏️ fix typo in isVec3 check function 2021-05-11 09:32:54 +05:00
jay
360eeff02f feat(informer): add item info based on player
Change the parameter order of `item` function to accomodate this
2021-05-11 07:32:05 +05:00
jay
25faac2f4c feat(informer): add command me for block info based on player position 2021-05-10 18:37:48 +05:00
jay
ccf0c598e8 feat(informer): add block info based on player position or manually provided coords
This paves the way to get block info based on positions relative from the player
2021-05-07 16:27:44 +05:00
jay
050e2b3bd9 feat(informer): 🚸 add sub-command for module level quiet 2021-05-07 12:53:24 +05:00
jay
e879d1f4ad refactor(informer): ♻️ reorder config loading 2021-05-07 12:40:17 +05:00
jay
df193da779 feat(command): 🚧 add stub for bot._client events
Not sure how these are different:
- `bot._client.on("chat")`
- `bot.on("message(str)")`
2021-05-06 13:15:38 +05:00
jay
269d258763 feat(command): use a simpler text based system for receiving server commands 2021-05-06 13:11:31 +05:00
jay
cbb105fe49 fix(command): 🚸 make bot address regex (prefix) more lenient
Should also accept server commands now on address
2021-05-03 07:20:33 +05:00
jay
356f83e39b fix(command): 👽 fix whisper event api change with an extra parameter
Whisper also now includes who it was whispered to
2021-05-03 07:14:45 +05:00
jay
5b3804893b feat(command): fix to show web (including extra [MODE]) messages
There's no way to remove `bot.addChatPattern`, so only does once on load
2021-04-29 08:37:46 +05:00
jay
9f6fea2423 feat(command): 🔊 add system (non-chat) messages to console log
This includes anything that doesn't return a `.text`.
Such as an `extra`, `translate` or other `json` formatted message.
Ex:
- afk
- errors
- player leave and joins
2021-04-29 08:24:51 +05:00
jay
3686bab167 fix: 🐛 fix plugin reload for multi-file/dir based plugins
Now, it also (un-)loads the whole plugin (sub and master).

This appears to be the most stable option.
Loading behaviour is unchanged from before (load only top level plugin).
However as a precaution, loading tries to unload bad plugins on error.
This helps to not end up in a bad state during reload.
2021-04-29 08:14:40 +05:00
jay
e0c477a46f feat: 🔨 make fs.watch recursive, so it reloadplugin works also on directories 2021-04-27 11:47:25 +05:00
jay
6046123074 build: ⬆️ update deps 2021-04-25 07:13:55 +05:00
jay
ae1f7cf269 feat: 🔧 extract the hardcoded admin and mod list out into environment variables 2021-04-23 06:07:29 +05:00
jay
afd2e002df feat(mover): add new pathfinder Movements
- safeMove without parkour
- aggresiveMove that digs
2021-04-19 13:34:22 +05:00
jay
3c5ec6b360 fix(mover): ✏️ fix !go up moving left instead 2021-04-19 13:31:36 +05:00
jay
b71728e503 feat(mover): add Mule to ridable mobs 2021-04-19 13:25:04 +05:00
jay
d001280383 fix: 🥅 fix a plugin loading error
While reloading, plugin cache may not be deleted.
This happens when a loaded plugin encounters an error during reload.
This causes a deadlock in the unloading code.

So make sure cache is always deleted:
- ignore when an `unload()` function doesn't exist
- delete cache even if there are no `exports`
2021-04-19 13:15:25 +05:00
5 changed files with 310 additions and 60 deletions

View File

@@ -1,8 +1,8 @@
const env = require("dotenv-packed").parseEnv().parsed const env = require("dotenv-packed").parseEnv().parsed
const fs = require('fs'); const fs = require('fs');
let cfg = { const cfg = {
admin: "Applezaus", admin: process.env.MINECRAFT_PLAYER_ADMIN || env.MINECRAFT_PLAYER_ADMIN || console.warn("main: bot admin user not provided"),
mods: ["Applezaus", "tanner6", "Angram42", "[WEB] Angram42", "[WEB] Applezaus"], mods: process.env.MINECRAFT_PLAYER_MODS || env.MINECRAFT_PLAYER_MODS || [], // json array,
stateMachines: {} stateMachines: {}
} }
@@ -29,25 +29,28 @@ cfg.botOptions = options
let plugins = {} let plugins = {}
function loadplugin(pluginname, pluginpath) { function loadplugin(pluginname, pluginpath = './plugins/' + pluginname) {
try { try {
plugins[pluginname] = require(pluginpath) plugins[pluginname] = require(pluginpath)
plugins[pluginname].load(cfg) plugins[pluginname].load(cfg)
} catch (error) { } catch (error) {
if (error.code == 'MODULE_NOT_FOUND') { if (error.code == 'MODULE_NOT_FOUND') {
console.warn('plugin not used:', pluginpath) console.warn('plugin not used:', pluginpath)
} else if (plugins[pluginname] && !plugins[pluginname].load) {
unloadplugin(pluginname, pluginpath)
} else { } else {
console.error(error) console.error(error)
} }
} }
} }
function unloadplugin(pluginname, pluginpath) { function unloadplugin(pluginname, pluginpath = './plugins/' + pluginname) {
pluginpath = pluginpath ? pluginpath : './plugins/' + pluginname
const plugin = require.resolve(pluginpath) const plugin = require.resolve(pluginpath)
try { try {
if (plugin && require.cache[plugin]) { if (plugin && require.cache[plugin]) {
require.cache[plugin].exports.unload() // `unload()` isn't exported sometimes,
// when plugin isn't properly loaded
require.cache[plugin].exports?.unload?.()
delete plugins[pluginname] delete plugins[pluginname]
delete require.cache[plugin] delete require.cache[plugin]
} }
@@ -56,20 +59,28 @@ function unloadplugin(pluginname, pluginpath) {
} }
} }
reloadplugin = (event, filename, pluginpath) => { function reloadplugin(event, filename, pluginpath = './plugins/') {
if (!/\.js$/.test(filename)) { return } if (!/\.js$/.test(filename)) { return }
filename = filename.replace("\\", "/") // windows
if (!cfg.fsTimeout) { if (!cfg.fsTimeout) {
console.info(event, filename) console.info(event, filename)
pluginpath = (pluginpath ? pluginpath : './plugins/') + filename const fullpluginpath = pluginpath + filename
const pluginname = (filename.split(".js")[0]).replace(/\/.+/, "")
pluginpath = pluginpath + pluginname
const hassubplugin = fullpluginpath.replace(/\.js$/, "") !== pluginpath
const check = Object.keys(cfg.plugins) const check = Object.keys(cfg.plugins)
console.info(`reload file: ./lib/${pluginpath}`) console.info(`reload file: ./lib/${fullpluginpath}`)
const plugin = require.resolve(pluginpath) const plugin = require.resolve(fullpluginpath)
if (plugin && require.cache[plugin]) { if (plugin && require.cache[plugin]) {
// console.debug(Object.keys(cfg.plugins)) // console.debug(Object.keys(cfg.plugins))
unloadplugin(filename.split(".js")[0], pluginpath) unloadplugin(pluginname)
console.assert(Object.keys(cfg.plugins).length == check.length - 1, "plugin not removed, potential memory leak") console.assert(Object.keys(cfg.plugins).length == check.length - 1, "plugin not removed, potential memory leak")
if (hassubplugin) {
console.info("reload: also unloading sub")
unloadplugin(pluginname, fullpluginpath)
}
} }
loadplugin(filename.split(".js")[0], pluginpath) loadplugin(pluginname)
if (Object.keys(cfg.plugins).length != check.length) { if (Object.keys(cfg.plugins).length != check.length) {
// If left < right : // If left < right :
// - new plugin that's not registered in cfg.plugins, so added // - new plugin that's not registered in cfg.plugins, so added
@@ -83,7 +94,7 @@ reloadplugin = (event, filename, pluginpath) => {
// console.log('file.js %s event', event) // console.log('file.js %s event', event)
} }
fs.watch('./lib/plugins', reloadplugin) fs.watch('./lib/plugins', { recursive: true }, reloadplugin)
cfg.bot = bot cfg.bot = bot
// TODO better name, or switch to array // TODO better name, or switch to array
@@ -109,7 +120,10 @@ bot.once("spawn", () => {
} }
cfg.plugins = plugins cfg.plugins = plugins
cfg.botAddressRegex = new RegExp(`^${bot.username} (${cfg.botAddressPrefix}.+)`) // cfg.botAddressPrefix = ${bot.username.substr(-2,2)}
cfg.botAddressRegex = new RegExp(`^${bot.username}:? (/|${cfg.botAddressPrefix}.+)`)
// FIXME leaks every load, so adding here instead of command.js to load only once
bot.addChatPattern("web", /\[WEB\] (\[.+\])?\s*([\w.]+): (.+)/, { parse: true })
for (const plugin of Object.values(plugins)) { for (const plugin of Object.values(plugins)) {
try { try {

View File

@@ -27,12 +27,12 @@ function checkItemExists(name) {
} }
const events = { const events = {
whisper: function command_whisper(username, message) { whisper: function command_whisper(username, _botusername, message, ...history) {
if ([bot.username, "me"].includes(username)) return if ([bot.username, "me"].includes(username)) return // whisper back from server (afk msges, etc)
if (/^gossip/.test(message)) return if (/^gossip/.test(message)) return
if (username === cfg.admin) { if (username === cfg.admin) {
console.info("whispered command", _botusername, message)
message = message.replace("\\", "@") message = message.replace("\\", "@")
console.info("whispered command", message)
if (message.startsWith(cfg.botAddressPrefix)) { if (message.startsWith(cfg.botAddressPrefix)) {
command(username, message) command(username, message)
} else { } else {
@@ -41,7 +41,7 @@ const events = {
} }
} else { } else {
bot.whisper(cfg.admin, `gossip ${username}: ${message}`) bot.whisper(cfg.admin, `gossip ${username}: ${message}`)
console.info(username, "whispered", message) console.info(username, "whispered", _botusername, message)
} }
} }
, chat: command , chat: command
@@ -56,6 +56,30 @@ const events = {
}, 15 * 60 * 1000, bot, cfg); }, 15 * 60 * 1000, bot, cfg);
} }
} }
// , message: systemMessage
, messagestr: function systemMessageStr(...args) { console.log("cmd msgstr:", ...args) }
, "chat:web": commandWeb
}
function systemMessage(...args) {
if (args[0]?.text) return
const metadata = (args[0]?.extra || args[0]?.with)?.map(v => v.text)
console.log(
"cmd msg:",
args[0]?.text || args[0]?.translate,
args[0]?.extra?.length || args[0]?.with?.length || Object.keys(args[0]?.json).length - 1,
metadata.length > 0 && metadata || args[0],
args.slice(1)
)
}
function commandWeb([[mode, username, message]]) {
// console.log("web msg:", mode, username, message)
message && command(username, message)
}
function _clientSystemMessage(...args) {
console.log("cmd chat:", args[0]?.message, args[0]?.extra?.length, args[0]?.extra, args[0], args.slice(1))
} }
const events_registered = [] const events_registered = []
@@ -521,6 +545,8 @@ const load = (config) => {
bot.on(key, fn) bot.on(key, fn)
) )
} }
// bot._client.on("chat", _clientSystemMessage)
} }
const unload = () => { const unload = () => {
@@ -530,6 +556,8 @@ const unload = () => {
events_registered.shift() events_registered.shift()
} }
console.log("events_registered:", bot._eventsCount) console.log("events_registered:", bot._eventsCount)
// bot._client.off("chat", _clientSystemMessage)
} }
module.exports = { load, unload, events_registered, command } module.exports = { load, unload, events_registered, command }

View File

@@ -1,39 +1,104 @@
let cfg
let bot let bot
let mcData let mcData
let quiet
// import v from 'vec3' // import v from 'vec3'
const v = require('vec3') const v = require('vec3')
function block(pos) { let cfg = {
const block = pos ? bot.blockAt(v(pos)) : bot.blockAtCursor() info: {
console.log(block, block && block.getProperties()) quiet: quiet,
recentCommand: null,
},
// to satisfy typescript
quiet: null,
bot: bot
}
var RelativePosEnum
(function (RelativePosEnum) {
RelativePosEnum[RelativePosEnum["feet"] = 0] = "feet"
RelativePosEnum[RelativePosEnum["standing"] = 1] = "standing"
RelativePosEnum[RelativePosEnum["head"] = 2] = "head"
RelativePosEnum[RelativePosEnum["looking"] = 3] = "looking"
RelativePosEnum[RelativePosEnum["infront"] = 4] = "infront"
RelativePosEnum[RelativePosEnum["behind"] = 5] = "behind"
})(RelativePosEnum || (RelativePosEnum = {}))
function relPosToBlock(entity, relPos) {
let pos
if (!(relPos in RelativePosEnum)) return console.warn("info: not a relative position:", relPos)
relPos = typeof relPos === "number" ? RelativePosEnum[relPos] : relPos
switch (relPos) {
case "feet":
pos = entity.position;
break
case "standing":
pos = entity.position.offset(0, -1, 0)
break
// todo use CARDINAL directions api from pathfinder
// case "behind":
// entity.yaw
// pos = entity.position
// break
// case "front":
// case "infront":
// pos = entity.position
// break
case "head":
pos = entity.position.offset(0, 1.85, 0)
break
case "looking":
if (entity === bot.entity) {
return bot.blockAtCursor()
// return bot.blockInSight(128, 128)
}
default:
quiet || bot.chat(`info: pos '${relPos}' not implemented`)
break
}
if (pos) {
// nearest block
return bot.blockAt(pos)
}
}
function isVec3(vec) {
return vec?.length === 3 || vec.x && vec.y && vec.z
}
function block(entity = bot.entity, pos = entity?.position?.offset(0, -1, 0)) {
console.assert(pos || entity)
const block = isVec3(pos) ? bot.blockAt(v(pos))
: typeof pos === "string" ? relPosToBlock(entity, RelativePosEnum[pos]) : pos
console.log(block, block?.getProperties && block.getProperties())
if (!block) { if (!block) {
cfg.quiet || bot.chat("empty block") quiet || bot.chat("empty block")
return block return block
} }
bot.lookAt(block?.position)
let info = [block.type, block.name] let info = [block.type, block.name]
if (block.metadata) info.push(Object.entries(block.getProperties())) if (block.metadata) info.push(Object.entries(block.getProperties()))
cfg.quiet || bot.chat(info.join(": ")) quiet || bot.chat(info.join(": "))
} }
function item( function item(
slot, entity = bot.entity,
entity = bot.entity slot = entity.heldItem
) { ) {
const item = slot ? const item = typeof slot === "number" ?
bot.inventory.slots[parseInt(slot) + bot.QUICK_BAR_START] : bot.inventory.slots[slot + bot.QUICK_BAR_START] :
entity.heldItem slot
console.log(item) console.log("info item:", item)
if (!item) { if (!item) {
cfg.quiet || bot.chat("no item") quiet || bot.chat("no item")
return item return item
} }
let info = [item.type, item.name] let info = [item.type, item.name, item.count]
if (item.metadata) info.push("meta: " + item.metadata.length) if (item.metadata) info.push("meta: " + item.metadata.length)
if (item.nbt) { if (item.nbt) {
info.push(compound_value(item.nbt)) info.push(compound_value(item.nbt))
} }
cfg.quiet || bot.chat(info.join("; ")) quiet || bot.chat(info.join("; "))
function compound_value(obj) { function compound_value(obj) {
if (typeof obj.value == "object") { if (typeof obj.value == "object") {
return compound_value(obj.value) return compound_value(obj.value)
@@ -51,27 +116,110 @@ function item(
return item return item
} }
function entity(name) { var VillagerProfession
const entity = typeof name === "string" ? bot.nearestEntity((entity) => { (function (VillagerProfession) {
const ename = entity.name || entity.username VillagerProfession[VillagerProfession["Unemployed"] = 0] = "Unemployed"
return name && ename ? ename == name : true VillagerProfession[VillagerProfession["Armourer"] = 1] = "Armourer"
}) : entity VillagerProfession[VillagerProfession["Butcher"] = 2] = "Butcher"
console.log(entity) VillagerProfession[VillagerProfession["Cartographer"] = 3] = "Cartographer"
VillagerProfession[VillagerProfession["Cleric"] = 4] = "Cleric"
VillagerProfession[VillagerProfession["Farmer"] = 5] = "Farmer"
VillagerProfession[VillagerProfession["Fisherman"] = 6] = "Fisherman"
VillagerProfession[VillagerProfession["Fletcher"] = 7] = "Fletcher"
VillagerProfession[VillagerProfession["Leatherworker"] = 8] = "Leatherworker"
VillagerProfession[VillagerProfession["Librarian"] = 9] = "Librarian"
VillagerProfession[VillagerProfession["Mason"] = 10] = "Mason"
VillagerProfession[VillagerProfession["Nitwit"] = 11] = "Nitwit"
VillagerProfession[VillagerProfession["Shepherd"] = 12] = "Shepherd"
VillagerProfession[VillagerProfession["Toolsmith"] = 13] = "Toolsmith"
VillagerProfession[VillagerProfession["Weaponsmith"] = 14] = "Weaponsmith"
})(VillagerProfession || (VillagerProfession = {}))
function entity(name = bot.nearestEntity()) {
const entity = typeof name === "string" ? (name = name.toLowerCase()) && bot.nearestEntity((entity) => {
const enames = [entity.username?.toLowerCase(), entity.name, entity.displayName?.toLowerCase()]
return enames.includes(name)
}) : name
console.log("info entity:", entity)
if (!entity) { if (!entity) {
cfg.quiet || bot.chat("no entity") quiet || bot.chat("no entity")
return entity return entity
} }
let info = [entity.type, entity.username || entity.name] entity?.position && bot.lookAt(entity.position)
let info = [(entity.entityType && entity.entityType + ":" || "") + (entity.type === "object" ? entity.kind : entity.type)]
info.push(entity.username || entity.name)
// TODO various info depending on the type of entity; player, villager, etc // TODO various info depending on the type of entity; player, villager, etc
// TODO refactor and combine with compound_value()
switch (entity.type) {
case "object":
switch (entity.kind) {
case "Drops":
const item = entity.metadata[7];
entity.metadata;
if (item.present) {
info.push("item: " + item.itemId + ": "
+ mcData.items[item.itemId].name
+ `x${item.itemCount}`);
}
else {
console.warn("info entity: metadata expected item, got:", entity.metadata[7]);
}
break;
default:
}
break;
case "mob":
switch (name) {
case 'villager':
const { villagerProfession, ...otherProps } = entity.metadata[17]
info.push(VillagerProfession[villagerProfession])
info.push(Object.entries(otherProps).toString())
break
default:
break
}
entity.metadata[8] && info.push("health:" + entity.metadata[8])
entity.metadata[15] && info.push("baby")
if (entity.heldItem) {
info.push("holding")
item(entity)
}
case "global":
case "orb":
case "player":
break;
case "other":
info.push("kind:" + entity.kind)
default:
}
if (entity.metadata) info.push("len: " + entity.metadata.length) if (entity.metadata) info.push("len: " + entity.metadata.length)
cfg.quiet || bot.chat(info.join("; ")) quiet || bot.chat(info.join("; "))
return entity return entity
} }
function blockOrItemFromId(num, quiet = cfg.info.quiet) {
const block = mcData?.blocks[num]
const item = mcData?.items[num]
// const entity = mcData?.entities[num]
if (block || item) {
quiet || bot.chat(
(block && `block: ${block.name}, ` || "")
+ (item && `item: ${item.name}, ` || "")
// + (entity && `entity: ${entity.name}, ` || "")
)
} else {
quiet || bot.chat("info: nonexistent block or item")
}
return { block, item }
}
function command(message_parts, player) { function command(message_parts, player) {
if (message_parts.length > 0) { if (message_parts.length > 0) {
cfg.info.recentCommand = message_parts cfg.info.recentCommand = message_parts
} }
if (player === null)
player = void 0
switch (message_parts.length) { switch (message_parts.length) {
case 0: case 0:
if (cfg.info.recentCommand) { if (cfg.info.recentCommand) {
@@ -79,11 +227,15 @@ function command(message_parts, player) {
} else { } else {
// TODO dispatch on instance of entity, block, etc.. // TODO dispatch on instance of entity, block, etc..
// TODO have the logic inside the function or with a utility function // TODO have the logic inside the function or with a utility function
block(player.position || player?.entity.position || null) block()
} }
break; break;
case 1: case 1:
switch (message_parts[0]) { switch (message_parts[0]) {
case "quiet":
cfg.info.quiet = quiet = !quiet;
quiet || bot.chat(`info: ${quiet ? "" : "not "}being quiet`);
break;
case "i": case "i":
case "item": case "item":
item() item()
@@ -92,10 +244,20 @@ function command(message_parts, player) {
case "entity": case "entity":
entity() entity()
break break
case "me":
block(player)
break
case "b": case "b":
case "block": case "block":
default:
block() block()
break
default:
const num = parseInt(message_parts[0])
if (isFinite(num)) {
blockOrItemFromId(num)
} else {
quiet || bot.chat("info usage: `!info [me|i|e|b|<num>|quiet]`")
}
break; break;
} }
@@ -105,7 +267,43 @@ function command(message_parts, player) {
switch (message_parts[0]) { switch (message_parts[0]) {
case "i": case "i":
case "item": case "item":
item(message_parts[1]) switch (message_parts[1]) {
case "me":
item(player)
break
default:
const slot = parseInt(message_parts[1])
slot && item(undefined, slot)
break
}
break
case "b":
case "block":
switch (message_parts[1]) {
case "me":
block(player)
break
default:
const aPlayer = bot.players[message_parts[1]]
if (message_parts[1] in RelativePosEnum) {
block(undefined, message_parts[1])
} else if (aPlayer) {
if (aPlayer.entity) {
block(aPlayer.entity)
} else {
quiet || bot.chat(`info: player ${aPlayer.username} too far!`)
}
} else {
// or entity(message_parts[1]).position
console.log(bot.players[message_parts[1]])
quiet || bot.chat("info: not yet implemented")
}
}
break
case "me":
if (message_parts[1] in RelativePosEnum) {
block(player, message_parts[1])
}
break break
case "e": case "e":
case "entity": case "entity":
@@ -120,7 +318,7 @@ function command(message_parts, player) {
case "b": case "b":
case "block": case "block":
default: default:
block(message_parts.slice(1)) block(undefined, message_parts.slice(1))
break; break;
} }
@@ -133,12 +331,9 @@ function command(message_parts, player) {
} }
const load = (config) => { const load = (config) => {
config.info = cfg.info
cfg = config cfg = config
bot = cfg.bot bot = cfg.bot
cfg.info = {
quiet: cfg.quiet,
recentCommand: null,
}
mcData = bot.mcData || (bot.mcData = require('minecraft-data')(bot.version)) mcData = bot.mcData || (bot.mcData = require('minecraft-data')(bot.version))
} }

View File

@@ -23,6 +23,19 @@ function initMoves(bot = bot, mcData = bot.mcData) {
movements.push(normalMove) movements.push(normalMove)
movements.defaultMove = movements[0] movements.defaultMove = movements[0]
const aggresiveMove = new Movements(bot, mcData)
//Object.create or assign?
Object.assign(aggresiveMove, normalMove)
aggresiveMove.canDig = true
movements.push(aggresiveMove)
const safeMove = new Movements(bot, mcData)
Object.assign(safeMove, normalMove)
safeMove.allowParkour = false
safeMove.canDig = false
movements.push(safeMove)
// console.info("go init: moves:", movements)
bot.pathfinder.setMovements(normalMove) bot.pathfinder.setMovements(normalMove)
} }
@@ -143,7 +156,7 @@ function away(entity = bot.nearestEntity(), invertInvert = true, dynamic = true,
function ride(entity) { function ride(entity) {
entity = entity?.entity || entity entity = entity?.entity || entity
const ridableMobs = ["Horse", "Donkey", "Pig", "Strider"] const ridableMobs = ["Horse", "Donkey", "Pig", "Strider", "Mule"]
const vehicle = entity && typeof entity !== "string" ? entity : bot.nearestEntity(e => { const vehicle = entity && typeof entity !== "string" ? entity : bot.nearestEntity(e => {
if (typeof entity === "string") return e.name === entity if (typeof entity === "string") return e.name === entity
const maybeRidableMob = e.mobType?.split(" ") const maybeRidableMob = e.mobType?.split(" ")
@@ -314,7 +327,7 @@ function command(message_parts, player) {
case "up": case "up":
case "u": case "u":
case "j": case "j":
moveOrRide(1, 1, "left", message_parts2) moveOrRide(1, 1, "jump", message_parts2)
break break
case "back": case "back":
case "forward": case "forward":

View File

@@ -30,16 +30,16 @@
}, },
"homepage": "https://github.com/PrismarineJS/prismarine-template#readme", "homepage": "https://github.com/PrismarineJS/prismarine-template#readme",
"devDependencies": { "devDependencies": {
"@types/node": "^14.14.35", "@types/node": "^14.14.41",
"jest": "^26.6.3", "jest": "^26.6.3",
"require-self": "^0.2.3", "require-self": "^0.2.3",
"typescript": "^4.2.3" "typescript": "^4.2.4"
}, },
"dependencies": { "dependencies": {
"dotenv-packed": "^1.2.1", "dotenv-packed": "^1.2.2",
"minecraft-data": "^2.80.0", "minecraft-data": "^2.84.0",
"mineflayer": "^3.4.0", "mineflayer": "^3.6.0",
"mineflayer-armor-manager": "^1.4.0", "mineflayer-armor-manager": "^1.4.1",
"mineflayer-pathfinder": "^1.6.1", "mineflayer-pathfinder": "^1.6.1",
"mineflayer-pvp": "^1.0.2", "mineflayer-pvp": "^1.0.2",
"prismarine-block": "^1.8.0", "prismarine-block": "^1.8.0",
@@ -49,7 +49,7 @@
"prismarine-nbt": "^1.5.0", "prismarine-nbt": "^1.5.0",
"prismarine-recipe": "^1.1.0", "prismarine-recipe": "^1.1.0",
"vec3": "^0.1.7", "vec3": "^0.1.7",
"xstate": "^4.17.0" "xstate": "^4.18.0"
}, },
"files": [ "files": [
"lib/**/*" "lib/**/*"