feat: 🎉 init new repo
Dump of current working bot. Warning: somewhat messy code! Lints haven't been run, no tests, etc.
This commit is contained in:
commit
4f1e510386
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
/lib/**/*.old
|
||||||
|
/lib/**/*.bak
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Editor
|
||||||
|
*.swp
|
||||||
|
*.swo
|
29
.vscode/launch.json
vendored
Normal file
29
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "protospace",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"program": "${workspaceFolder}/lib/index.js",
|
||||||
|
"args": ["games.protospace.ca"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Program",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"program": "${workspaceFolder}/lib/index.js",
|
||||||
|
// port may need to be changed for each session
|
||||||
|
"args": ["localhost", "56901"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
123
lib/index.js
Normal file
123
lib/index.js
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
// TODO reload
|
||||||
|
const fs = require('fs');
|
||||||
|
let cfg = {
|
||||||
|
admin: "Applezaus",
|
||||||
|
mods: ["Applezaus", "tanner6", "Angram42", "[WEB] Angram42", "[WEB] Applezaus"],
|
||||||
|
stateMachines: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mineflayer = require("mineflayer");
|
||||||
|
// const { createGetAccessor } = require('typescript');
|
||||||
|
|
||||||
|
const bot =
|
||||||
|
!isNaN(parseInt(process.argv[3])) && parseInt(process.argv[3]) > 1e2 ?
|
||||||
|
mineflayer.createBot({
|
||||||
|
host: process.argv[2] || process.env.MINECRAFT_HOST || 'localhost', // Change this to the ip you want.
|
||||||
|
port: parseInt(process.argv[3]) || process.env.MINECRAFT_PORT // || 58471,
|
||||||
|
})
|
||||||
|
:
|
||||||
|
mineflayer.createBot({
|
||||||
|
host: process.argv[2] || process.env.MINECRAFT_HOST || 'localhost', // Change this to the ip you want.
|
||||||
|
username: process.argv[3] || process.env.MINECRAFT_USER,
|
||||||
|
password: process.argv[4] || process.env.MINECRAFT_PASS,
|
||||||
|
// port: process.argv[5] || process.env.MINECRAFT_PORT || 58471,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
let plugins = {}
|
||||||
|
|
||||||
|
function loadplugin(pluginname, pluginpath) {
|
||||||
|
try {
|
||||||
|
plugins[pluginname] = require(pluginpath)
|
||||||
|
plugins[pluginname]?.load(cfg)
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code == 'MODULE_NOT_FOUND') {
|
||||||
|
console.warn('plugin not used:', pluginpath)
|
||||||
|
} else {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unloadplugin(pluginname, pluginpath) {
|
||||||
|
pluginpath = pluginpath ? pluginpath : './plugins/' + pluginname
|
||||||
|
const plugin = require.resolve(pluginpath)
|
||||||
|
try {
|
||||||
|
if (plugin && require.cache[plugin]) {
|
||||||
|
require.cache[plugin].exports?.unload()
|
||||||
|
delete plugins[pluginname]
|
||||||
|
delete require.cache[plugin]
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadplugin = (event, filename, pluginpath) => {
|
||||||
|
if (!/\.js$/.test(filename)) { return }
|
||||||
|
if (!cfg.fsTimeout) {
|
||||||
|
console.info(event, filename)
|
||||||
|
pluginpath = (pluginpath ? pluginpath : './plugins/') + filename
|
||||||
|
const check = Object.keys(cfg.plugins)
|
||||||
|
console.info(`reload file:`, pluginpath)
|
||||||
|
const plugin = require.resolve(pluginpath)
|
||||||
|
if (plugin && require.cache[plugin]) {
|
||||||
|
// console.debug(Object.keys(cfg.plugins))
|
||||||
|
unloadplugin(filename.split(".js")[0], pluginpath)
|
||||||
|
console.assert(Object.keys(cfg.plugins).length == check.length - 1, "plugin not removed, potential memory leak")
|
||||||
|
}
|
||||||
|
loadplugin(filename.split(".js")[0], pluginpath)
|
||||||
|
if (Object.keys(cfg.plugins).length != check.length) {
|
||||||
|
// If left < right :
|
||||||
|
// - new plugin that's not registered in cfg.plugins, so added
|
||||||
|
// - rename, so old wasn't removed
|
||||||
|
// If left > right :
|
||||||
|
// - error in file (syntax, missing load()), so was not reloaded
|
||||||
|
console.warn("plugin count mismatch", check.length, Object.keys(cfg.plugins).length, Object.keys(cfg.plugins))
|
||||||
|
}
|
||||||
|
cfg.fsTimeout = setTimeout(function () { cfg.fsTimeout = null }, 2000) // give 2 seconds for multiple events
|
||||||
|
}
|
||||||
|
// console.log('file.js %s event', event)
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.watch('./lib/plugins', reloadplugin)
|
||||||
|
|
||||||
|
cfg.bot = bot
|
||||||
|
cfg.botAddress = new RegExp(`^${bot.username} (!.+)`)
|
||||||
|
cfg.quiet = true
|
||||||
|
|
||||||
|
|
||||||
|
// == actually do stuff
|
||||||
|
|
||||||
|
bot.once("spawn", () => {
|
||||||
|
plugins = {
|
||||||
|
command: require('./plugins/command'),
|
||||||
|
sleeper: require('./plugins/sleeper'),
|
||||||
|
armor: require('./plugins/armor'),
|
||||||
|
// mover: require('./plugins/mover'),
|
||||||
|
guard: require('./plugins/guard'),
|
||||||
|
inventory: require('./plugins/inventory'),
|
||||||
|
// eater: require('./plugins/eater'),
|
||||||
|
finder: require('./plugins/finder'),
|
||||||
|
miner: require('./plugins/miner'),
|
||||||
|
// statemachine: require('./plugins/statemachine'),
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.plugins = plugins
|
||||||
|
|
||||||
|
for (const plugin of Object.values(plugins)) {
|
||||||
|
try {
|
||||||
|
plugin.load(cfg)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
bot.on("death", () => {
|
||||||
|
bot.pathfinder && bot.pathfinder.setGoal(null)
|
||||||
|
// plugins.guard.unload()
|
||||||
|
})
|
||||||
|
// bot.on("respawn", () => {
|
||||||
|
// // setTimeout(plugins.guard.load, 2000)
|
||||||
|
// })
|
16
lib/plugins/armor.js
Normal file
16
lib/plugins/armor.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
const armorManager = require('mineflayer-armor-manager')
|
||||||
|
const mineflayer = require('mineflayer');
|
||||||
|
|
||||||
|
let cfg = {}
|
||||||
|
|
||||||
|
|
||||||
|
const load = (config) => {
|
||||||
|
cfg = config
|
||||||
|
bot = cfg.bot
|
||||||
|
|
||||||
|
bot.loadPlugin(armorManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
const unload = () => { console.warn("armour: may not properly unload") }
|
||||||
|
|
||||||
|
module.exports = { load, unload }
|
323
lib/plugins/command.js
Normal file
323
lib/plugins/command.js
Normal file
|
@ -0,0 +1,323 @@
|
||||||
|
const v = require('vec3')
|
||||||
|
let mcData = require('minecraft-data')
|
||||||
|
|
||||||
|
let cfg = {}
|
||||||
|
let bot = {}
|
||||||
|
// bot.chatAddPattern(new RegExp(`^hi|hello ${bot.username}`, "i"), 'greet', "General greeting")
|
||||||
|
|
||||||
|
function todo() {
|
||||||
|
bot.chat("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkBlockExists(name) {
|
||||||
|
if (mcData.blocksByName[name] === undefined) {
|
||||||
|
bot.chat(`${name} is not a block name`)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function checkItemExists(name) {
|
||||||
|
if (mcData.itemsByName[name] === undefined) {
|
||||||
|
bot.chat(`${name} is not an item name`)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const events = {
|
||||||
|
whisper: function command_whisper(username, message) {
|
||||||
|
if ([bot.username, "me"].includes(username)) return
|
||||||
|
if (/^gossip/.test(message)) return
|
||||||
|
// if (/^!/.test(message) && username === cfg.admin){
|
||||||
|
if (username === cfg.admin) {
|
||||||
|
message = message.replace("\\", "@")
|
||||||
|
console.info("whispered command", message)
|
||||||
|
bot.chat("/" + message)
|
||||||
|
} else {
|
||||||
|
bot.whisper(cfg.admin, `gossip ${username}: ${message}`)
|
||||||
|
console.info(username, "whispered", message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, chat: command
|
||||||
|
, kicked: (reason, loggedIn) => console.warn(reason, loggedIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
const events_registered = []
|
||||||
|
|
||||||
|
function command(username, message) {
|
||||||
|
|
||||||
|
if (username === bot.username && !message.startsWith("!")) return
|
||||||
|
|
||||||
|
const player = bot.players[username] ? bot.players[username].entity : null
|
||||||
|
|
||||||
|
if (message.startsWith("zzz")) {
|
||||||
|
bot.chat("/afk")
|
||||||
|
cfg.plugins.sleeper.sleep()
|
||||||
|
// if(cfg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (message) {
|
||||||
|
case "hi":
|
||||||
|
case "hello":
|
||||||
|
case "howdy":
|
||||||
|
case "yo":
|
||||||
|
bot.swingArm()
|
||||||
|
return
|
||||||
|
case "69":
|
||||||
|
Math.random() > 0.5 && bot.chat("nice!")
|
||||||
|
return
|
||||||
|
case "awesome":
|
||||||
|
case "cool":
|
||||||
|
case "superb":
|
||||||
|
case "nice":
|
||||||
|
Math.random() > 0.1 && setTimeout(() => bot.chat("very nice!"), 1000)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.startsWith("!") || cfg.botAddress.test(message)) {
|
||||||
|
message = cfg.botAddress.test(message) ? cfg.botAddress.exec(message)[1] : message
|
||||||
|
|
||||||
|
console.log(message)
|
||||||
|
message = message.slice(1)
|
||||||
|
message_parts = message.split(/\s+/)
|
||||||
|
switch (message_parts[0]) {
|
||||||
|
// case "follow":
|
||||||
|
// // if(username === cfg.admin)
|
||||||
|
// cfg.stateMachines.follow.transitions[4].trigger()
|
||||||
|
// // cfg.stateMachines.follow.transitions[1].trigger()
|
||||||
|
// break;
|
||||||
|
// case "stay":
|
||||||
|
// // if(username === cfg.admin)
|
||||||
|
// cfg.stateMachines.follow.transitions[3].trigger()
|
||||||
|
// break;
|
||||||
|
case "echo":
|
||||||
|
// bot.chat(message_parts[1])
|
||||||
|
// bot.chat(message.slice(1))
|
||||||
|
bot.chat(message)
|
||||||
|
break;
|
||||||
|
case "autosleep":
|
||||||
|
case "sleep":
|
||||||
|
message_parts[1] = message_parts[0] == "autosleep" ? "auto" : message_parts[1]
|
||||||
|
switch (message_parts[1]) {
|
||||||
|
case "auto":
|
||||||
|
cfg.sleep.auto = !cfg.sleep.auto
|
||||||
|
bot.chat(`ok, ${cfg.sleep.auto ? "" : "not "}auto sleeping `)
|
||||||
|
bot.chat("/afk")
|
||||||
|
break;
|
||||||
|
case "silent":
|
||||||
|
case "quiet":
|
||||||
|
cfg.sleep.quiet = !cfg.sleep.quiet
|
||||||
|
bot.chat(`ok, ${cfg.sleep.quiet ? "" : "not "}sleeping quietly`)
|
||||||
|
break;
|
||||||
|
case "timeout":
|
||||||
|
if (!message_parts[2]) {
|
||||||
|
bot.chat(`sleeping every ${Math.round(cfg.sleep.timeout / 60 / 1000)}mins`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const timeout = parseFloat(message_parts[2], 10)
|
||||||
|
if (timeout) {
|
||||||
|
cfg.sleep.timeout = timeout * 60 * 1000
|
||||||
|
cfg.sleep.timeoutFn = null
|
||||||
|
cfg.plugins.sleeper.sleep(true) //reset timer
|
||||||
|
bot.chat(`ok, next sleep attempt in ${timeout}mins`)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
bot.chat(`usage: !sleep [auto | quiet | timeout <mins>], or zzz for manual sleep`)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break; //todo; needed?
|
||||||
|
case "zzz":
|
||||||
|
cfg.plugins.sleeper.sleep()
|
||||||
|
case "afk":
|
||||||
|
bot.chat("/afk")
|
||||||
|
break;
|
||||||
|
case "awaken":
|
||||||
|
case "wake":
|
||||||
|
case "wakeup":
|
||||||
|
cfg.plugins.sleeper.wake()
|
||||||
|
break;
|
||||||
|
case "attack":
|
||||||
|
case "rage":
|
||||||
|
case "ragemode":
|
||||||
|
case "guard":
|
||||||
|
switch (message_parts.length) {
|
||||||
|
case 1:
|
||||||
|
if (!player) {
|
||||||
|
bot.chat("can't see you.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bot.chat('for glory!')
|
||||||
|
cfg.plugins.guard.guardArea(player.position)
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
switch (message_parts[1]) {
|
||||||
|
case "self":
|
||||||
|
cfg.guard.self = !cfg.guard.self
|
||||||
|
bot.chat(`ok, ${cfg.guard.self?"no longer being altruistic":"self sacrifice!"}`)
|
||||||
|
return;
|
||||||
|
case "stop":
|
||||||
|
cfg.plugins.guard.stopGuarding()
|
||||||
|
bot.chat('no longer guarding this area.')
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
bot.chat("don't know wym")
|
||||||
|
}
|
||||||
|
// entity = new BehaviorGetClosestEntity(bot, bot.nearestEntity(), a => EntityFilters().MobsOnly(a))
|
||||||
|
// if (entity) {
|
||||||
|
// bot.attack(entity, true)
|
||||||
|
// } else {
|
||||||
|
// bot.chat('no nearby entities')
|
||||||
|
// }
|
||||||
|
// // bot.attack(new BehaviorGetClosestEntity(bot, {}, EntityFilters().MobsOnly()))
|
||||||
|
break;
|
||||||
|
case "find":
|
||||||
|
switch (message_parts.length) {
|
||||||
|
case 2:
|
||||||
|
cfg.plugins.finder.findBlocks(message_parts[1])
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "lookat":
|
||||||
|
// const coords = v(message_parts.splice(1))
|
||||||
|
switch (message_parts.length) {
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
todo()
|
||||||
|
// bot.lookAt({})
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
bot.lookAt(bot.nearestEntity().position)
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
bot.lookAt(v(message_parts.splice(1)))
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "ride":
|
||||||
|
case "mount":
|
||||||
|
bot.mount(bot.nearestEntity())
|
||||||
|
break
|
||||||
|
case "getoff":
|
||||||
|
case "unmount":
|
||||||
|
case "dismount":
|
||||||
|
bot.dismount()
|
||||||
|
break
|
||||||
|
case "go":
|
||||||
|
bot.moveVehicle(0, 10)
|
||||||
|
break
|
||||||
|
// case "use":
|
||||||
|
// bot.useOn(bot.nearestEntity())
|
||||||
|
// break;
|
||||||
|
|
||||||
|
// // TODO move all inventory related tasks into own module
|
||||||
|
// case "give":
|
||||||
|
// // switch (message_parts[1]) {
|
||||||
|
// // case "hand":
|
||||||
|
// // case "right":
|
||||||
|
// // break;
|
||||||
|
// // case "left":
|
||||||
|
// // break;
|
||||||
|
// // // case "slot":
|
||||||
|
// // default:
|
||||||
|
// // let slot = parseInt(message_parts[1])
|
||||||
|
// // if (!isNaN(slot)) {
|
||||||
|
|
||||||
|
// // } else {
|
||||||
|
|
||||||
|
// // }
|
||||||
|
// // break;
|
||||||
|
// // }
|
||||||
|
// // break;
|
||||||
|
// case "take":
|
||||||
|
// // TODO take only what's requested, then throw all the rest
|
||||||
|
// // TODO take all
|
||||||
|
// case "toss":
|
||||||
|
// case "drop":
|
||||||
|
// if (!message_parts[1]) { return false } // FIXME, works but ugly
|
||||||
|
// if (!checkItemExists(message_parts[1])) { return false }
|
||||||
|
// switch (message_parts.length) {
|
||||||
|
// case 2:
|
||||||
|
// bot.toss(mcData.blocksByName[message_parts[1]].id)
|
||||||
|
// break
|
||||||
|
// case 3:
|
||||||
|
// bot.tossStack(
|
||||||
|
// mcData.itemsByName[message_parts[1]].id,
|
||||||
|
// (err) => {
|
||||||
|
// if (err) {
|
||||||
|
// console.log(err)
|
||||||
|
// bot.chat(err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// break
|
||||||
|
// default:
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
case "location":
|
||||||
|
switch (message_parts[1]) {
|
||||||
|
case "add":
|
||||||
|
case "record":
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "where":
|
||||||
|
case "where?":
|
||||||
|
// TODO put in /lib/location
|
||||||
|
console.log(bot.entity.position)
|
||||||
|
bot.chat(bot.entity.position.floored().toString())
|
||||||
|
break;
|
||||||
|
case "warp":
|
||||||
|
// if(message_parts[1] == "spawn")
|
||||||
|
bot.chat("/" + message)
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (cfg.mods.includes(username))
|
||||||
|
bot.chat("/" + message)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const load = (config) => {
|
||||||
|
cfg = config
|
||||||
|
bot = cfg.bot
|
||||||
|
|
||||||
|
mcData = mcData(bot.version)
|
||||||
|
for (const [key, fn] of Object.entries(events)) {
|
||||||
|
events_registered.push(
|
||||||
|
bot.on(key, fn)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mcData = require('minecraft-data')(bot.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
const unload = () => {
|
||||||
|
console.log("events_registered:", events_registered.length, "of", bot._eventsCount)
|
||||||
|
for (const [key, fn] of Object.entries(events)) {
|
||||||
|
bot.off(key, fn)
|
||||||
|
events_registered.shift()
|
||||||
|
}
|
||||||
|
console.log("events_registered:", bot._eventsCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { load, unload, events_registered, command }
|
53
lib/plugins/finder.js
Normal file
53
lib/plugins/finder.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// const performance = require('performance-now')
|
||||||
|
const {Vec3} = require('vec3')
|
||||||
|
let mcData = require('minecraft-data')
|
||||||
|
|
||||||
|
let cfg = {}
|
||||||
|
let bot = {}
|
||||||
|
|
||||||
|
function findBlocks(name){
|
||||||
|
// const name = message.split(' ')[1]
|
||||||
|
if (mcData.blocksByName[name] === undefined) {
|
||||||
|
bot.chat(`${name} is not a block name`)
|
||||||
|
return
|
||||||
|
} else if (["ancient_debris", "diamond_ore", "emerald_ore"].includes(name)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const ids = [mcData.blocksByName[name].id]
|
||||||
|
|
||||||
|
// const startTime = performance.now()
|
||||||
|
const blocks = bot.findBlocks({ matching: ids, maxDistance: 128, count: 10 })
|
||||||
|
// const time = (performance.now() - startTime).toFixed(2)
|
||||||
|
|
||||||
|
// TODO check if toString() is really needed
|
||||||
|
if (blocks.length === 0){
|
||||||
|
bot.chat("not here (around 128 blocks)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const pos = minmax(blocks).map(x => x.toString())
|
||||||
|
|
||||||
|
|
||||||
|
// bot.chat(`I found ${blocks.length} ${name} blocks in ${time} ms`)
|
||||||
|
bot.chat(`I found ${blocks.length} ${name} blocks at ${pos}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function minmax(coordsArray){
|
||||||
|
const min = coordsArray.reduce((x,y) => x.min(y))
|
||||||
|
const max = coordsArray.reduce((x,y) => x.max(y))
|
||||||
|
|
||||||
|
return [min, max]
|
||||||
|
}
|
||||||
|
|
||||||
|
const load = (config) => {
|
||||||
|
cfg = config
|
||||||
|
bot = cfg.bot
|
||||||
|
|
||||||
|
mcData = mcData(bot.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
const unload = () => {
|
||||||
|
// bot.off("time", autoSleep)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { load, unload, findBlocks}
|
147
lib/plugins/guard.js
Normal file
147
lib/plugins/guard.js
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
const mineflayer = require('mineflayer')
|
||||||
|
const { pathfinder, Movements, goals } = require('mineflayer-pathfinder')
|
||||||
|
const pvp = require('mineflayer-pvp').plugin
|
||||||
|
|
||||||
|
let cfg = {}
|
||||||
|
let bot = {}
|
||||||
|
let guardPos
|
||||||
|
|
||||||
|
const filterallMobs = e => e.type === 'mob'
|
||||||
|
// const filterPlayers = e => e.type === 'player' && e.username !== 'Applezaus'
|
||||||
|
// const filterPassiveMobs = e => e.kind === 'Passive mobs'
|
||||||
|
const filterCreeper = e => e.mobType === 'Creeper'
|
||||||
|
const filterHostileMobs = e => e.kind === 'Hostile mobs'
|
||||||
|
// // not needed if detecting by kind
|
||||||
|
// && e.mobType !== 'Armor Stand' // Mojang classifies armor stands as mobs for some reason?
|
||||||
|
|
||||||
|
// Assign the given location to be guarded
|
||||||
|
function guardArea(pos) {
|
||||||
|
if (pos) {
|
||||||
|
cfg.guard.pos = guardPos = pos
|
||||||
|
// Check for new enemies to attack
|
||||||
|
// bot.off('physicTick', lookForMobs)
|
||||||
|
bot.off('time', lookForMobs)
|
||||||
|
bot.on('time', lookForMobs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are not currently in combat, move to the guard pos
|
||||||
|
if (!bot.pvp.target && guardPos) {
|
||||||
|
moveToGuardPos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel all pathfinder and combat
|
||||||
|
function stopGuarding() {
|
||||||
|
guardPos = cfg.guard.pos = null
|
||||||
|
bot.pvp.stop()
|
||||||
|
bot.pathfinder.setGoal(null)
|
||||||
|
bot.off('time', lookForMobs)
|
||||||
|
// bot.off('physicTick', lookForMobs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pathfinder to the guard position
|
||||||
|
function moveToGuardPos() {
|
||||||
|
const mcData = require('minecraft-data')(bot.version)
|
||||||
|
bot.pathfinder.setMovements(new Movements(bot, mcData))
|
||||||
|
bot.pathfinder.setGoal(new goals.GoalBlock(guardPos.x, guardPos.y, guardPos.z))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the bot has killed it's target.
|
||||||
|
// () => {
|
||||||
|
// if (guardPos) {
|
||||||
|
// moveToGuardPos()
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
function lookForMobs() {
|
||||||
|
if (!guardPos) return // Do nothing if bot is not guarding anything
|
||||||
|
|
||||||
|
// Only look for mobs within 16 blocks
|
||||||
|
const filter = e => e.position.distanceTo(bot.entity.position) < 16 && filterHostileMobs(e)
|
||||||
|
|
||||||
|
const entityEnemy = bot.nearestEntity(filter)
|
||||||
|
if (entityEnemy) {
|
||||||
|
if(filterCreeper(entityEnemy))
|
||||||
|
// Start attacking
|
||||||
|
// bot.off('time', lookForMobs)
|
||||||
|
// bot.on('physicTick', lookForMobs)
|
||||||
|
bot.pvp.attack(entityEnemy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function guardSelf(entity) {
|
||||||
|
if (!cfg.guard.self || entity !== bot.entity || guardPos) return // Do nothing
|
||||||
|
// bot.chat("")
|
||||||
|
bot.chat((
|
||||||
|
() => { const a = ["ouch!", "oww!", "help", "", "", ""]; return a[Math.floor(Math.random() * a.length)] }
|
||||||
|
)())
|
||||||
|
|
||||||
|
console.info(bot.nearestEntity(filterallMobs))
|
||||||
|
|
||||||
|
// Only look for mobs within 10 blocks
|
||||||
|
const filter = e => e.position.distanceTo(bot.entity.position) < 10 && filterHostileMobs(e)
|
||||||
|
|
||||||
|
const entityEnemy = bot.nearestEntity(filter)
|
||||||
|
if (entityEnemy && !filterCreeper(entityEnemy)) {
|
||||||
|
// Start attacking
|
||||||
|
// bot.off('time', lookForMobs)
|
||||||
|
// bot.on('physicTick', lookForMobs)
|
||||||
|
bot.pvp.attack(entityEnemy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bot.on('physicTick', lookForMobs)
|
||||||
|
|
||||||
|
// Listen for player commands
|
||||||
|
// bot.on('chat', (username, message) => {
|
||||||
|
// // Guard the location the player is standing
|
||||||
|
// if (message === 'guard') {
|
||||||
|
// const player = bot.players[username]
|
||||||
|
|
||||||
|
// if (!player) {
|
||||||
|
// bot.chat("I can't see you.")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// bot.chat('I will guard that location.')
|
||||||
|
// guardArea(player.entity.position)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Stop guarding
|
||||||
|
// if (message === 'stop') {
|
||||||
|
// bot.chat('I will no longer guard this area.')
|
||||||
|
// stopGuarding()
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
const load = (config) => {
|
||||||
|
cfg = config
|
||||||
|
bot = cfg.bot
|
||||||
|
cfg.guard = {
|
||||||
|
pos: null,
|
||||||
|
auto: true,
|
||||||
|
self: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
guardPos = cfg.guard.pos
|
||||||
|
|
||||||
|
|
||||||
|
bot.loadPlugin(pathfinder)
|
||||||
|
bot.loadPlugin(pvp)
|
||||||
|
|
||||||
|
bot.on('stoppedAttacking', guardArea)
|
||||||
|
bot.on('entityHurt', guardSelf)
|
||||||
|
// bot.on("time", guardArea)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const unload = () => {
|
||||||
|
stopGuarding()
|
||||||
|
bot.off('time', lookForMobs)
|
||||||
|
// bot.off('physicTick', lookForMobs)
|
||||||
|
bot.off('stoppedAttacking', guardArea)
|
||||||
|
bot.off('entityHurt', guardSelf)
|
||||||
|
// bot.off("time", guardArea)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { load, unload, guardArea, guardSelf, stopGuarding }
|
190
lib/plugins/inventory.js
Normal file
190
lib/plugins/inventory.js
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
/*
|
||||||
|
* Using the inventory is one of the first things you learn in Minecraft,
|
||||||
|
* now it's time to teach your bot the same skill.
|
||||||
|
*
|
||||||
|
* Command your bot with chat messages and make him toss, equip, use items
|
||||||
|
* and even craft new items using the built-in recipe book.
|
||||||
|
*
|
||||||
|
* To learn more about the recipe system and how crafting works
|
||||||
|
* remember to read the API documentation!
|
||||||
|
*/
|
||||||
|
const mineflayer = require('mineflayer')
|
||||||
|
|
||||||
|
// if (process.argv.length < 4 || process.argv.length > 6) {
|
||||||
|
// console.log('Usage : node inventory.js <host> <port> [<name>] [<password>]')
|
||||||
|
// process.exit(1)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const bot = mineflayer.createBot({
|
||||||
|
// host: process.argv[2],
|
||||||
|
// port: parseInt(process.argv[3]),
|
||||||
|
// username: process.argv[4] ? process.argv[4] : 'inventory',
|
||||||
|
// password: process.argv[5]
|
||||||
|
// })
|
||||||
|
|
||||||
|
let cfg = {}
|
||||||
|
let bot = {}
|
||||||
|
// let mcd
|
||||||
|
|
||||||
|
function inventory(username, message) {
|
||||||
|
if (username === bot.username) return
|
||||||
|
const command = message.split(' ')
|
||||||
|
switch (true) {
|
||||||
|
// case message === 'loaded':
|
||||||
|
// bot.waitForChunksToLoad(() => {
|
||||||
|
// bot.chat('Ready!')
|
||||||
|
// })
|
||||||
|
// break
|
||||||
|
case /^list$/.test(message):
|
||||||
|
sayItems()
|
||||||
|
break
|
||||||
|
case /^toss \d+ \w+$/.test(message):
|
||||||
|
// toss amount name
|
||||||
|
// ex: toss 64 diamond
|
||||||
|
tossItem(command[2], command[1])
|
||||||
|
break
|
||||||
|
case /^toss \w+$/.test(message):
|
||||||
|
// toss name
|
||||||
|
// ex: toss diamond
|
||||||
|
tossItem(command[1])
|
||||||
|
break
|
||||||
|
case /^equip \w+ \w+$/.test(message):
|
||||||
|
// equip destination name
|
||||||
|
// ex: equip hand diamond
|
||||||
|
equipItem(command[2], command[1], quiet = cfg.quiet)
|
||||||
|
break
|
||||||
|
case /^unequip \w+$/.test(message):
|
||||||
|
// unequip testination
|
||||||
|
// ex: unequip hand
|
||||||
|
unequipItem(command[1])
|
||||||
|
break
|
||||||
|
case /^use$/.test(message):
|
||||||
|
useEquippedItem()
|
||||||
|
break
|
||||||
|
case /^craft \d+ \w+$/.test(message):
|
||||||
|
// craft amount item
|
||||||
|
// ex: craft 64 stick
|
||||||
|
craftItem(command[2], command[1])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function sayItems(items = bot.inventory.items()) {
|
||||||
|
const output = items.map(itemToString).join(', ')
|
||||||
|
if (output) {
|
||||||
|
console.info("inventory:", output)
|
||||||
|
!cfg.quiet && bot.chat(output)
|
||||||
|
} else {
|
||||||
|
!cfg.quiet && bot.chat('empty')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tossItem(name, amount) {
|
||||||
|
amount = parseInt(amount, 10)
|
||||||
|
const item = itemByName(name)
|
||||||
|
if (!item) {
|
||||||
|
bot.chat(`I have no ${name}`)
|
||||||
|
} else if (amount) {
|
||||||
|
bot.toss(item.type, null, amount, checkIfTossed)
|
||||||
|
} else {
|
||||||
|
bot.tossStack(item, checkIfTossed)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkIfTossed(err) {
|
||||||
|
if (err) {
|
||||||
|
bot.chat(`unable to toss: ${err.message}`)
|
||||||
|
} else if (amount) {
|
||||||
|
bot.chat(`tossed ${amount} x ${name}`)
|
||||||
|
} else {
|
||||||
|
bot.chat(`tossed ${name}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function equipItem(name, destination, quiet = false) {
|
||||||
|
const item = itemByName(name)
|
||||||
|
if (item) {
|
||||||
|
bot.equip(item, destination, checkIfEquipped)
|
||||||
|
} else {
|
||||||
|
!quiet && bot.chat(`I have no ${name}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkIfEquipped(err) {
|
||||||
|
if (err) {
|
||||||
|
bot.chat(`cannot equip ${name}: ${err.message}`)
|
||||||
|
} else {
|
||||||
|
!quiet && bot.chat(`equipped ${name}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unequipItem(destination) {
|
||||||
|
bot.unequip(destination, (err) => {
|
||||||
|
if (err) {
|
||||||
|
bot.chat(`cannot unequip: ${err.message}`)
|
||||||
|
} else {
|
||||||
|
bot.chat('unequipped')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function useEquippedItem() {
|
||||||
|
bot.chat('activating item')
|
||||||
|
bot.activateItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
function craftItem(name, amount) {
|
||||||
|
amount = parseInt(amount, 10)
|
||||||
|
const item = require('minecraft-data')(bot.version).findItemOrBlockByName(name)
|
||||||
|
const craftingTable = bot.findBlock({
|
||||||
|
matching: 58
|
||||||
|
})
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
const recipe = bot.recipesFor(item.id, null, 1, craftingTable)[0]
|
||||||
|
if (recipe) {
|
||||||
|
bot.chat(`I can make ${name}`)
|
||||||
|
bot.craft(recipe, amount, craftingTable, (err) => {
|
||||||
|
if (err) {
|
||||||
|
bot.chat(`error making ${name}`)
|
||||||
|
} else {
|
||||||
|
bot.chat(`did the recipe for ${name} ${amount} times`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
bot.chat(`I cannot make ${name}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bot.chat(`unknown item: ${name}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function itemToString(item) {
|
||||||
|
if (item) {
|
||||||
|
return `${item.name} x ${item.count}`
|
||||||
|
} else {
|
||||||
|
return '(nothing)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function itemByName(name) {
|
||||||
|
return bot.inventory.items().filter(item => item.name === name)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const load = (config) => {
|
||||||
|
cfg = config
|
||||||
|
bot = cfg.bot
|
||||||
|
// cfg.inventory = {
|
||||||
|
// auto: true,
|
||||||
|
// quiet: false
|
||||||
|
// }
|
||||||
|
bot.on('chat', inventory)
|
||||||
|
}
|
||||||
|
|
||||||
|
const unload = () => {
|
||||||
|
bot.off('chat', inventory)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { load, unload, equipItem }
|
190
lib/plugins/miner.js
Normal file
190
lib/plugins/miner.js
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
const mineflayer = require('mineflayer')
|
||||||
|
const pathfinder = require('mineflayer-pathfinder').pathfinder
|
||||||
|
const {
|
||||||
|
gameplay,
|
||||||
|
MoveTo,
|
||||||
|
BreakBlock,
|
||||||
|
ObtainItems,
|
||||||
|
ObtainItem,
|
||||||
|
GiveTo,
|
||||||
|
Craft
|
||||||
|
} = require('prismarine-gameplay')
|
||||||
|
// const { Gameplay } = require('prismarine-gameplay/lib/gameplay')
|
||||||
|
// const { Vec3 } = require('vec3')
|
||||||
|
let mcData = require('minecraft-data')
|
||||||
|
|
||||||
|
let cfg = {}
|
||||||
|
let bot = {}
|
||||||
|
|
||||||
|
// if (process.argv.length < 4 || process.argv.length > 6) {
|
||||||
|
// console.log('Usage : node miner.js <host> <port> [<name>] [<password>]')
|
||||||
|
// process.exit(1)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const bot = mineflayer.createBot({
|
||||||
|
// host: process.argv[2],
|
||||||
|
// port: parseInt(process.argv[3]),
|
||||||
|
// username: process.argv[4] ? process.argv[4] : 'collect_items',
|
||||||
|
// // password: process.argv[5]
|
||||||
|
// })
|
||||||
|
|
||||||
|
|
||||||
|
// bot.on('spawn', () => bot.gameplay.debugText = true)
|
||||||
|
|
||||||
|
// bot.on('chat', (username, message) => mine(username, message))
|
||||||
|
|
||||||
|
function checkBlockExists(name){
|
||||||
|
if (mcData.blocksByName[name] === undefined) {
|
||||||
|
bot.chat(`${name} is not a block name`)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function miner(username, message) {
|
||||||
|
const player = bot.players[username] ? bot.players[username].entity : null
|
||||||
|
|
||||||
|
const command = message.split(' ')
|
||||||
|
switch (true) {
|
||||||
|
// case /^echo .*/.test(message):
|
||||||
|
// bot.chat(command.slice(1).join(" "))
|
||||||
|
// break
|
||||||
|
|
||||||
|
// case /^zz+/.test(message):
|
||||||
|
// bot.chat("/afk")
|
||||||
|
// break
|
||||||
|
case /^debug$/.test(message):
|
||||||
|
bot.gameplay.debugText = !!!bot.gameplay.debugText
|
||||||
|
break
|
||||||
|
|
||||||
|
case /^moveto -?[0-9]+ -?[0-9]+$/.test(message):
|
||||||
|
bot.gameplay.solveFor(
|
||||||
|
new MoveTo({
|
||||||
|
x: parseInt(command[1]),
|
||||||
|
z: parseInt(command[2])
|
||||||
|
}), logError)
|
||||||
|
break
|
||||||
|
|
||||||
|
case /^moveto -?[0-9]+ -?[0-9]+ -?[0-9]+$/.test(message):
|
||||||
|
bot.gameplay.solveFor(
|
||||||
|
new MoveTo({
|
||||||
|
x: parseInt(command[1]),
|
||||||
|
y: parseInt(command[2]),
|
||||||
|
z: parseInt(command[3])
|
||||||
|
}), logError)
|
||||||
|
break
|
||||||
|
|
||||||
|
case /^moveto \w+$/.test(message):
|
||||||
|
const player2 = bot.players[command[1]] ? bot.players[command[1]].entity : null
|
||||||
|
if (!player2) {
|
||||||
|
bot.chat(`can't see ${command[1]}..`)
|
||||||
|
} else {
|
||||||
|
bot.gameplay.solveFor(
|
||||||
|
new MoveTo({
|
||||||
|
x: player2.position.x,
|
||||||
|
y: player2.position.y,
|
||||||
|
z: player2.position.z
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case /^comehere$/.test(message):
|
||||||
|
if (!player) {
|
||||||
|
bot.chat("can't see you..")
|
||||||
|
} else {
|
||||||
|
bot.gameplay.solveFor(
|
||||||
|
new MoveTo({
|
||||||
|
x: player.position.x,
|
||||||
|
y: player.position.y,
|
||||||
|
z: player.position.z
|
||||||
|
}), logError)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case /^break -?[0-9]+ -?[0-9]+ -?[0-9]+$/.test(message):
|
||||||
|
bot.gameplay.solveFor(
|
||||||
|
new BreakBlock({
|
||||||
|
position: new Vec3(
|
||||||
|
parseInt(command[1]),
|
||||||
|
parseInt(command[2]),
|
||||||
|
parseInt(command[3])
|
||||||
|
)
|
||||||
|
}), logError)
|
||||||
|
break
|
||||||
|
|
||||||
|
case /^collect [0-9]+ [a-zA-Z_]+$/.test(message):
|
||||||
|
if(!checkBlockExists(command[2])) {return false}
|
||||||
|
bot.gameplay.solveFor(
|
||||||
|
new ObtainItems({
|
||||||
|
itemType: command[2],
|
||||||
|
count: parseInt(command[1])
|
||||||
|
}), logError)
|
||||||
|
break
|
||||||
|
|
||||||
|
case /^collect [a-zA-Z_]+$/.test(message):
|
||||||
|
if(!checkBlockExists(command[2])) {return false}
|
||||||
|
bot.gameplay.solveFor(
|
||||||
|
new ObtainItem({
|
||||||
|
itemType: command[1]
|
||||||
|
}), logError)
|
||||||
|
break
|
||||||
|
|
||||||
|
case /^bringme [0-9]+ [a-zA-Z_]+$/.test(message):
|
||||||
|
if(!checkBlockExists(command[2])) {return false}
|
||||||
|
bot.gameplay.solveFor(
|
||||||
|
new GiveTo({
|
||||||
|
itemType: command[2],
|
||||||
|
count: parseInt(command[1]),
|
||||||
|
entity: player
|
||||||
|
}), logError)
|
||||||
|
break
|
||||||
|
|
||||||
|
case /^craft [0-9]+ [a-zA-Z_]+$/.test(message):
|
||||||
|
if(!checkBlockExists(command[2])) {return false}
|
||||||
|
bot.gameplay.solveFor(
|
||||||
|
new Craft({
|
||||||
|
itemType: command[2],
|
||||||
|
count: parseInt(command[1]),
|
||||||
|
entity: player
|
||||||
|
}), logError)
|
||||||
|
break
|
||||||
|
|
||||||
|
case /^stop$/.test(message):
|
||||||
|
bot.chat("♪♪ can't stop me now!! ♪♪")
|
||||||
|
// player = bot.player.entity
|
||||||
|
if (player) {
|
||||||
|
bot.gameplay.solveFor(
|
||||||
|
new MoveTo({
|
||||||
|
x: player.position.x,
|
||||||
|
y: player.position.y,
|
||||||
|
z: player.position.z
|
||||||
|
}), logError)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function logError(err) {
|
||||||
|
if (err)
|
||||||
|
bot.chat(`Failed task: ${err.message}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const load = (config) => {
|
||||||
|
cfg = config
|
||||||
|
bot = cfg.bot
|
||||||
|
|
||||||
|
mcData = mcData(bot.version)
|
||||||
|
bot.loadPlugin(pathfinder)
|
||||||
|
bot.loadPlugin(gameplay)
|
||||||
|
bot.on("chat", miner)
|
||||||
|
}
|
||||||
|
|
||||||
|
const unload = () => {
|
||||||
|
// bot.gameplay
|
||||||
|
bot.off("chat", miner)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { load, unload }
|
115
lib/plugins/sleeper.js
Normal file
115
lib/plugins/sleeper.js
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
|
||||||
|
const pathfinder = require('mineflayer-pathfinder').pathfinder
|
||||||
|
const { Bot } = require('mineflayer')
|
||||||
|
const {
|
||||||
|
gameplay,
|
||||||
|
MoveTo,
|
||||||
|
MoveToInteract,
|
||||||
|
ObtainItem,
|
||||||
|
// Craft
|
||||||
|
} = require('prismarine-gameplay')
|
||||||
|
|
||||||
|
let cfg = {}
|
||||||
|
let bot = {}
|
||||||
|
let inv
|
||||||
|
// cfg.autosleep = false
|
||||||
|
|
||||||
|
function sleep(quiet) {
|
||||||
|
quiet = quiet !== undefined ? quiet : cfg.sleep.quiet
|
||||||
|
if(bot.game.dimension !== "minecraft:overworld" || cfg.sleep.force){
|
||||||
|
!quiet && bot.chat("can't sleep, not in overworld now")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let bed = bot.findBlock({
|
||||||
|
matching: block => bot.isABed(block)
|
||||||
|
})
|
||||||
|
let bedstatus = bed && bot.parseBedMetadata(bed).occupied ? "n unoccupied" : ""
|
||||||
|
if(bed && bedstatus == "n unoccupied"){
|
||||||
|
bot.lookAt(bed.position)
|
||||||
|
bed = bot.findBlock({
|
||||||
|
matching: block => bot.isABed(block) && !bot.parseBedMetadata(block).occupied
|
||||||
|
}) || bed
|
||||||
|
bedstatus = bot.parseBedMetadata(bed).occupied ? "n unoccupied" : ""
|
||||||
|
}
|
||||||
|
if (bed && bedstatus == "") {
|
||||||
|
bot.lookAt(bed.position)
|
||||||
|
// const nearbed =
|
||||||
|
bot.gameplay.solveFor(
|
||||||
|
new MoveTo((bed.position.range = 2) && bed.position), (err) => {
|
||||||
|
// new MoveTo(bed.position), (err) => {
|
||||||
|
// new MoveToInteract(bed.position), (err) => {
|
||||||
|
if (err) {
|
||||||
|
!quiet && bot.chat(`can't reach bed: ${err.message}`)
|
||||||
|
} else {
|
||||||
|
bot.waitForChunksToLoad(() => {
|
||||||
|
bot.sleep(bed, (err) => {
|
||||||
|
if (err) {
|
||||||
|
!quiet && bot.chat(`can't sleep: ${err.message}`)
|
||||||
|
} else {
|
||||||
|
!quiet && bot.chat("zzz")
|
||||||
|
console.log("sleeping? ", bot.isSleeping)
|
||||||
|
// hack until this is fixed
|
||||||
|
// bot.isSleeping = bot.isSleeping ? bot.isSleeping : true
|
||||||
|
bot.isSleeping = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// } else if (bed){
|
||||||
|
} else if (inv && inv.equipItem("red_bed", "hand", true)) {
|
||||||
|
// doesn't work fortunately
|
||||||
|
// FIXME: DONT IMPLEMENT until it is detected as NOT NETHER
|
||||||
|
bot.placeBlock()
|
||||||
|
} else {
|
||||||
|
bot.gameplay.solveFor(
|
||||||
|
new ObtainItem("bed"), (err) => {
|
||||||
|
if (err) {
|
||||||
|
!quiet && bot.chat(`need a${bedstatus} bed: may not see if just placed`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// bot.chat('/afk')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function wake() {
|
||||||
|
bot.wake((err) => {
|
||||||
|
if (err) {
|
||||||
|
bot.chat(`can't wake up: ${err.message}`)
|
||||||
|
} else {
|
||||||
|
bot.chat('woke up')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoSleep() {
|
||||||
|
if (!bot.time.isDay && !cfg.sleep.timeoutFn && cfg.sleep.auto && !bot.isSleeping) {
|
||||||
|
sleep()
|
||||||
|
cfg.sleep.timeoutFn = setTimeout(() => { cfg.sleep.timeoutFn = null }, cfg.sleep.timeout) // give 2 seconds for multiple events
|
||||||
|
console.log("sleeping?", bot.isSleeping, bot.time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const load = (config) => {
|
||||||
|
cfg = config
|
||||||
|
bot = cfg.bot
|
||||||
|
cfg.sleep = {
|
||||||
|
auto: true,
|
||||||
|
// timeout: 30 * 1000,
|
||||||
|
timeout: 2 * 60 * 1000,
|
||||||
|
quiet: false
|
||||||
|
}
|
||||||
|
|
||||||
|
bot.loadPlugin(pathfinder)
|
||||||
|
bot.loadPlugin(gameplay)
|
||||||
|
inv = cfg.plugins["inventory"]
|
||||||
|
bot.on("time", autoSleep)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const unload = () => {
|
||||||
|
bot.off("time", autoSleep)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { load, unload, sleep, wake }
|
115
lib/plugins/statemachine.js
Normal file
115
lib/plugins/statemachine.js
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
// Load your dependency plugins.
|
||||||
|
|
||||||
|
const {pathfinder} = require('mineflayer-pathfinder')
|
||||||
|
// bot.loadPlugin(require('prismarine-viewer').mineflayer)
|
||||||
|
// const mineflayerViewer = require('prismarine-viewer').mineflayer
|
||||||
|
|
||||||
|
// Import required behaviors.
|
||||||
|
const {
|
||||||
|
StateTransition,
|
||||||
|
BotStateMachine,
|
||||||
|
EntityFilters,
|
||||||
|
BehaviorFollowEntity,
|
||||||
|
BehaviorLookAtEntity,
|
||||||
|
BehaviorGetClosestEntity,
|
||||||
|
NestedStateMachine,
|
||||||
|
BehaviorIdle,
|
||||||
|
StateMachineWebserver,
|
||||||
|
} = require("mineflayer-statemachine");
|
||||||
|
|
||||||
|
// TODO chat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// wait for our bot to login.
|
||||||
|
function statemachineInit() {
|
||||||
|
cfg.botAddress = new RegExp(`^${bot.username} (!.+)`)
|
||||||
|
// This targets object is used to pass data between different states. It can be left empty.
|
||||||
|
const targets = new Object();
|
||||||
|
|
||||||
|
// Create our states
|
||||||
|
const getClosestPlayer = new BehaviorGetClosestEntity(
|
||||||
|
bot, targets, entity => EntityFilters().PlayersOnly(entity) && bot.entity.position.distanceTo(entity.position) <= 50 ); // && a.username !== bot.username);
|
||||||
|
const followPlayer = new BehaviorFollowEntity(bot, targets);
|
||||||
|
const lookAtPlayer = new BehaviorLookAtEntity(bot, targets);
|
||||||
|
const stay = new BehaviorIdle();
|
||||||
|
|
||||||
|
// Create our transitions
|
||||||
|
const transitions = [
|
||||||
|
|
||||||
|
// We want to start following the player immediately after finding them.
|
||||||
|
// Since getClosestPlayer finishes instantly, shouldTransition() should always return true.
|
||||||
|
new StateTransition({
|
||||||
|
parent: getClosestPlayer,
|
||||||
|
child: followPlayer,
|
||||||
|
onTransition: (quiet) => quiet || bot.chat(`Hi ${targets.entity.username}!`),
|
||||||
|
shouldTransition: () => bot.entity.position.distanceTo(targets.entity.position) <= 50,
|
||||||
|
// shouldTransition: () => getClosestPlayer.distanceToTarget() < 100 || console.info("player too far!") && false,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// If the distance to the player is less than two blocks, switch from the followPlayer
|
||||||
|
// state to the lookAtPlayer state.
|
||||||
|
new StateTransition({
|
||||||
|
parent: followPlayer,
|
||||||
|
child: lookAtPlayer,
|
||||||
|
// onTransition: () => console.log(targets),
|
||||||
|
shouldTransition: () => followPlayer.distanceToTarget() < 2,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// If the distance to the player is more than two blocks, switch from the lookAtPlayer
|
||||||
|
// state to the followPlayer state.
|
||||||
|
new StateTransition({
|
||||||
|
parent: lookAtPlayer,
|
||||||
|
child: followPlayer,
|
||||||
|
shouldTransition: () => lookAtPlayer.distanceToTarget() >= 5,
|
||||||
|
}),
|
||||||
|
new StateTransition({
|
||||||
|
parent: lookAtPlayer,
|
||||||
|
child: stay,
|
||||||
|
onTransition: () => bot.chat("ok, staying"),
|
||||||
|
// shouldTransition: () => true,
|
||||||
|
}),
|
||||||
|
new StateTransition({
|
||||||
|
parent: stay,
|
||||||
|
child: getClosestPlayer,
|
||||||
|
// shouldTransition: () => Math.random() > 0.01,
|
||||||
|
// shouldTransition: () => Math.random() > 0.1 && getClosestPlayer.distanceToTarget() < 2,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Now we just wrap our transition list in a nested state machine layer. We want the bot
|
||||||
|
// to start on the getClosestPlayer state, so we'll specify that here.
|
||||||
|
const rootLayer = new NestedStateMachine(transitions, getClosestPlayer, stay);
|
||||||
|
|
||||||
|
// We can start our state machine simply by creating a new instance.
|
||||||
|
cfg.stateMachines.follow = new BotStateMachine(bot, rootLayer);
|
||||||
|
const webserver = new StateMachineWebserver(bot, cfg.stateMachines.follow);
|
||||||
|
webserver.startServer();
|
||||||
|
|
||||||
|
// mineflayerViewer(bot, { port: 3000 })
|
||||||
|
// const path = [bot.entity.position.clone()]
|
||||||
|
// bot.on('move', () => {
|
||||||
|
// if (path[path.length - 1].distanceTo(bot.entity.position) > 1) {
|
||||||
|
// path.push(bot.entity.position.clone())
|
||||||
|
// bot.viewer.drawLine('path', path)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
const load = (config) => {
|
||||||
|
cfg = config
|
||||||
|
bot = cfg.bot
|
||||||
|
// cfg.inventory = {
|
||||||
|
// auto: true,
|
||||||
|
// quiet: false
|
||||||
|
// }
|
||||||
|
// bot.on('chat', inventory)
|
||||||
|
bot.loadPlugin(pathfinder)
|
||||||
|
// statemachineInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
const unload = () => {
|
||||||
|
// bot.off('chat', inventory)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { load, unload }
|
56
package.json
Normal file
56
package.json
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
{
|
||||||
|
"name": "angram-bot",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "A high-level, general purpose and modular minecraft bot using hot re-loadable (without restarting the bot!) plugins. Batteries included, launch to run!",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "jest --verbose",
|
||||||
|
"pretest": "pnpm run lint && require-self",
|
||||||
|
"prepare": "pnpm install require-self && require-self",
|
||||||
|
"lint": "standard",
|
||||||
|
"fix": "standard --fix",
|
||||||
|
"dev": "node ./lib/index.js localhost",
|
||||||
|
"prod": "node ./lib/index.js games.protospace.ca"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/PrismarineJS/prismarine-template.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"prismarine",
|
||||||
|
"template",
|
||||||
|
"minecraft",
|
||||||
|
"mineflayer",
|
||||||
|
"ai"
|
||||||
|
],
|
||||||
|
"author": "Jay",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/PrismarineJS/prismarine-template/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/PrismarineJS/prismarine-template#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"jest": "^26.6.3",
|
||||||
|
"require-self": "^0.2.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"minecraft-data": "^2.70.2",
|
||||||
|
"mineflayer": "^2.34.0",
|
||||||
|
"mineflayer-armor-manager": "^1.3",
|
||||||
|
"mineflayer-pathfinder": "^1.1",
|
||||||
|
"mineflayer-pvp": "^1",
|
||||||
|
"prismarine-block": "^1",
|
||||||
|
"prismarine-chat": "^1",
|
||||||
|
"prismarine-entity": "^1.1.0",
|
||||||
|
"prismarine-gameplay": "github:TheDudeFromCI/prismarine-gameplay#crafting",
|
||||||
|
"prismarine-item": "^1.5.0",
|
||||||
|
"prismarine-nbt": "^1.3",
|
||||||
|
"prismarine-recipe": "^1",
|
||||||
|
"typescript": "^4",
|
||||||
|
"vec3": "^0.1",
|
||||||
|
"dotenv-packed": "^1.2"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib/**/*"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user