Dump of current working bot. Warning: somewhat messy code! Lints haven't been run, no tests, etc.nogameplay
commit
4f1e510386
12 changed files with 1386 additions and 0 deletions
@ -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 |
@ -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"] |
||||||
|
} |
||||||
|
] |
||||||
|
} |
@ -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)
|
||||||
|
// })
|
@ -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 } |
@ -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 } |
@ -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} |
@ -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 } |
@ -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 } |
@ -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 } |
@ -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 } |
@ -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 } |
@ -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