Compare commits

...

66 Commits

Author SHA1 Message Date
jay
6a1ab4dd91 feat(builder): 🚧 add cover plugin for mycellium
Work in progress, based on Trend's code. Everything is hardcoded.
2021-02-17 09:44:57 +05:00
jay
2f88eedce9 fix(mover): lessen move duration for more precise control 2021-01-28 03:52:12 +05:00
jay
a0893f2b29 feat(mover): allow riding specific entity by name 2021-01-28 02:50:08 +05:00
jay
b1a592dbbd fix(mover): 🐛 workaround for vehicle not being removed in api on dismount
`bot.vehicle` isn't removed on dismount, so this done manually
2021-01-28 02:24:59 +05:00
jay
f336e3d736 feat(mover): switch between moving and riding automatically for manual movement 2021-01-28 02:18:42 +05:00
jay
d953bd4cf6 fix(mover): 🎨 fix quiet not being "followed" 2021-01-28 02:10:52 +05:00
jay
fc43985337 fix(mover): 🐛 fix crash when no mobs nearby while searching for rides 2021-01-28 01:28:37 +05:00
jay
6b1157147d feat(mover): implement more featureful and robust ride command
Now does the following:
- finds entities (both vehicles and animals) which are suitable for riding.
- moves to get into range.

However:
- while in a vehicle, pathfinder doesn't appear to detec coords.
- `bot.moveVehicle` doesn't work, so bot doesn't move when riding
2021-01-28 00:48:45 +05:00
jay
e6d29576e7 refactor(command): 🚚 move vehicle commands near other moves
Move the following:
- vehicle commands next to other movement related commands like `go`.
- move `ride` / `mount` command functionality into mover plugin
2021-01-26 22:49:23 +05:00
jay
974d460061 feat(mover): add basic movement commands
Forward, left, etc. Uses bot.controlState.
But left and right does the opposite of expectations.
2021-01-18 15:13:53 +05:00
jay
1d361e04a6 build: ⬆️ update deps 2021-01-18 01:54:05 +05:00
jay
7597620626 ci: 🔧 add vscode conventional commits scopes 2021-01-18 01:47:32 +05:00
jay
e5faa6f022 feat(informer): add more detail to item info
Gives detail of traversing the item's nbt data
2021-01-18 01:24:47 +05:00
jay
cc18ac5c2e refactor(mover): 🚚 move commands inside mover plugin itself
This reduces the code inside the command plugin.
Most of the logic was only relevant to moving anyway.
Command aliases like `come` and `follow` still remain in command plugin.
2021-01-17 23:59:39 +05:00
jay
7050a1621b fix(informer): 🐛 add missing name for entity info when entity is a player
Uses `username` when `entity.name` is missing; should check other cases
2021-01-17 18:32:05 +05:00
jay
7cbfa16476 feat(informer): add case for when sub command is passed a single param
Currently returns:
- Item at given slot number
- Entity that matches given name
2021-01-17 16:20:20 +05:00
jay
63849e0729 fix(informer): 🥅 catch and report when objects are missing 2021-01-17 16:16:41 +05:00
jay
4e7f8d59fd feat(informer): add more detailed block metadata info
Uses `block.getProperties()`. Thanks to a [comment][1]

[1]: https://github.com/PrismarineJS/mineflayer-pathfinder/pull/84/files#r541196424
2021-01-17 15:54:31 +05:00
jay
7b2b936f81 feat(mover): implement moveY (vertical move up or down)
Doesn't appear to be working properly, bot assumes XZ is goal reached
2021-01-17 13:56:18 +05:00
jay
8a39596b1d feat(informer): add info for block at given position 2021-01-17 13:02:05 +05:00
jay
2601b7cfb1 fix(informer): 🥅 fix crash for block info when no block or an empty block is found 2021-01-17 12:20:49 +05:00
jay
67932b2f6a fix(sleeper): 🥅 catch sleeping edge case errors
Happens when trying to sleep while previously unable to move.
Or maybe when trying to sleep during dawn.
2021-01-16 16:39:42 +05:00
jay
9a6e684b11 feat(informer): add info about nearest entities 2021-01-16 16:17:42 +05:00
jay
3488a94233 feat(informer): info about held item(s) 2021-01-16 14:51:49 +05:00
jay
4d21327086 fix(mover): 🚸 better messages 2021-01-16 13:57:21 +05:00
jay
e74d796124 fix(command): 🐛 make follow command work again without params 2021-01-16 13:50:35 +05:00
jay
3d5ffe38cd feat(mover): inform when goal reached 2021-01-16 13:25:26 +05:00
jay
b519913355 feat(mover): implement moving to X Z goal (without y) 2021-01-15 00:36:17 +05:00
jay
b1dab1968c fix(command): ✏️ fix follow command not working 2021-01-05 11:34:49 +05:00
jay
3219ec6155 feat(informer): actually add a command for info plugin 2021-01-05 11:32:33 +05:00
jay
f38ad8c819 fix(command): 🚧 fix and workaround bot look at this
Workaround being unable to look at what player is looking at.
Currently looks at what player is standing on.
2021-01-05 09:30:14 +05:00
jay
4b8a39d38c feat(informer): add optional metadata to block info display 2021-01-05 09:20:35 +05:00
jay
6b71de0356 refactor(informer): 🚚 proper name to block info function
Be more specific: `block` instead of `info`.
In anticipation of future functions in this module.
2021-01-05 08:52:38 +05:00
jay
96214ffe37 fix(informer): 🐛 use block.type instead of block.id
`block.id` is nonexistent when tested live
2021-01-05 08:32:30 +05:00
jay
034f8d331a fix(informer): 🐛 convert array to string for chat
`bot.chat` only supports strings
2021-01-05 08:18:21 +05:00
jay
69d0f5830d feat(mover): add close(er) command to follow / come closer 2021-01-05 08:11:11 +05:00
jay
8e719d5ccf feat(sleeper): add and adjust functionality to properly sleep
Can now use beds in inventory but with a hacky block placing workaround.
`findBlock` returns a null position.
So it uses the closest adjacent block and assumes it'll work.

Bot also is more robust at sleeping,
2020-12-27 05:50:16 +05:00
jay
112eb04a8d refactor(sleeper): ♻️ simplify and make sleeping code more robust 2020-12-27 01:58:17 +05:00
jay
ba7c53be0c refactor(command): ♻️ make toss exit early if non-existent block 2020-12-27 00:30:46 +05:00
jay
5b4718fa5d fix(command): 🐛 add missing return 2020-12-25 07:29:06 +05:00
jay
902732c6dd chore: ⬆️ update deps 2020-12-25 07:20:05 +05:00
jay
65d13a3379 style: fix crlf -> lf 2020-12-25 07:17:54 +05:00
jay
288b7045b6 feat: add informer plugin to show info
Data and information plugin.
Expose methods in mcData with acceptable ux.
Also central place for all kinds of debug features.
2020-12-24 21:39:50 +05:00
jay
94574a4296 refactor: ♻️ reorder plugin loading
Load in order of simplicity and dependance.

TODO: Use `bot.waitForChunksToLoad`:
Split plugins based on whether it requires blocks to be loaded or no.
2020-12-24 21:32:08 +05:00
jay
68e60921b1 refactor(mover): 🔥 remove unused code + comments, minor fixes
Refactoring and fixing code.
 No major functionality change.
 Probably will have less bugs.
 - carry over how mcData is loaded
 - stop bot on unload
 - fix wrong magma block name
2020-12-24 19:57:45 +05:00
jay
f2281a7cb3 feat(command): temp. move inventory chat handling to command
This is temporary, and only the `toss` function.
Old functionality is still intact.
New code will eventually move back as a subcommand to replace the old.
2020-12-24 19:41:52 +05:00
jay
e9f2080556 refactor(command): ♻️ refactor how mcData is loaded
`mcData` is now directly put on bot and loaded once.
It can be accessed from anywhere `bot` is accessible.
2020-12-24 17:51:44 +05:00
jay
086251bce6 feat: add a basic feature to rejoin on server restart 2020-12-24 13:38:16 +05:00
jay
0ae961521f feat(command): expand follow to include rest of the !go follow sub commands 2020-12-24 11:57:23 +05:00
jay
eae4e95803 Merge branch 'nogameplay' into master 2020-12-24 11:28:22 +05:00
jay
fb066ee8a5 refactor: ♻️ use array in pathfinder movements list 2020-12-24 11:26:19 +05:00
jay
f4445749e6 fix: ✏️ add missing plugin name 2020-12-24 11:23:16 +05:00
jay
bf45a53f08 refactor: remove gameplay
temporarily remove gameplay until it is fixed and more stable

miner plugin won't work now
2020-12-24 10:40:46 +05:00
jay
a7ccb08d43 refactor: remove sleeper's prismarine-gameplay dependence 2020-12-24 10:35:01 +05:00
jay
8e4eb7748f style: fix crlf to lf 2020-12-24 09:33:08 +05:00
jay
47a944fe2a fix: 🐛 compat: don't use ?. for compat with older node.js 2020-12-24 09:19:32 +05:00
jay
1a3c345017 chore: 🙈 ignore .env 2020-12-24 09:16:11 +05:00
jay
de0af4d2ac feat: 🚀 add and use dotenv-packed
Use dotenv for more convenient credential management
2020-12-24 08:52:50 +05:00
jay
73d5f43ad3 feat: actually enable mover and eater plugins 2020-12-23 13:28:34 +05:00
jay
b970231519 feat: add mover plugin
General purpose mover / goto plugin.
Replaces the old solver based `comehere` in miner.js.
Instead, directly based on mineflayer bot apis and pathfinder.
This makes it simpler and easier to debug.
While less general, pathfinder is sophisticated enough for most cases.

For anything that needs moving from point A to point B.
Such as:
- following
- go to a location

Not in scope: locations and places. Would be a separate plugin.
2020-12-22 15:38:45 +05:00
jay
787d08dd31 feat: add automatic eater plugin 2020-12-22 15:29:06 +05:00
jay
f8df1fa319 feat: update command plugin 2020-12-22 11:31:46 +05:00
jay
8eb6d790b0 feat: update sleeper 2020-12-22 11:31:06 +05:00
jay
5ead6b7267 feat: update miner 2020-12-22 11:30:16 +05:00
jay
149a01551c feat: update inventory 2020-12-22 11:29:31 +05:00
jay
b1f27f455d feat: update guard 2020-12-22 11:28:44 +05:00
14 changed files with 1467 additions and 401 deletions

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@
# misc # misc
.DS_Store .DS_Store
.env
.env.local .env.local
.env.development.local .env.development.local
.env.test.local .env.test.local

56
.vscode/launch.json vendored
View File

@@ -1,29 +1,29 @@
{ {
// Use IntelliSense to learn about possible attributes. // Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes. // Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"type": "node", "type": "node",
"request": "launch", "request": "launch",
"name": "protospace", "name": "protospace",
"skipFiles": [ "skipFiles": [
"<node_internals>/**" "<node_internals>/**"
], ],
"program": "${workspaceFolder}/lib/index.js", "program": "${workspaceFolder}/lib/index.js",
"args": ["games.protospace.ca"] "args": ["games.protospace.ca"]
}, },
{ {
"type": "node", "type": "node",
"request": "launch", "request": "launch",
"name": "Launch Program", "name": "Launch Program",
"skipFiles": [ "skipFiles": [
"<node_internals>/**" "<node_internals>/**"
], ],
"program": "${workspaceFolder}/lib/index.js", "program": "${workspaceFolder}/lib/index.js",
// port may need to be changed for each session // port may need to be changed for each session
"args": ["localhost", "56901"] "args": ["localhost", "56901"]
} }
] ]
} }

8
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"conventionalCommits.scopes": [
"command",
"mover",
"sleeper",
"informer"
]
}

View File

@@ -1,4 +1,4 @@
// TODO reload const env = require("dotenv-packed").parseEnv().parsed
const fs = require('fs'); const fs = require('fs');
let cfg = { let cfg = {
admin: "Applezaus", admin: "Applezaus",
@@ -9,19 +9,22 @@ let cfg = {
const mineflayer = require("mineflayer"); const mineflayer = require("mineflayer");
// const { createGetAccessor } = require('typescript'); // const { createGetAccessor } = require('typescript');
const bot = const options = !isNaN(parseInt(process.argv[3])) && parseInt(process.argv[3]) > 1e2 ?
!isNaN(parseInt(process.argv[3])) && parseInt(process.argv[3]) > 1e2 ? {
mineflayer.createBot({ host: process.argv[2] || process.env.MINECRAFT_HOST || env.MINECRAFT_HOST || 'localhost', // Change this to the ip you want.
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 || env.MINECRAFT_PORT // || 58471,
port: parseInt(process.argv[3]) || process.env.MINECRAFT_PORT // || 58471, }
}) :
: {
mineflayer.createBot({ host: process.argv[2] || process.env.MINECRAFT_HOST || env.MINECRAFT_HOST || 'localhost', // Change this to the ip you want.
host: process.argv[2] || process.env.MINECRAFT_HOST || 'localhost', // Change this to the ip you want. username: process.argv[3] || process.env.MINECRAFT_USER || env.MINECRAFT_USER,
username: process.argv[3] || process.env.MINECRAFT_USER, password: process.argv[4] || process.env.MINECRAFT_PASS || env.MINECRAFT_PASS,
password: process.argv[4] || process.env.MINECRAFT_PASS, // port: process.argv[5] || process.env.MINECRAFT_PORT || 58471,
// port: process.argv[5] || process.env.MINECRAFT_PORT || 58471, }
})
const bot = mineflayer.createBot(options)
cfg.botOptions = options
let plugins = {} let plugins = {}
@@ -29,7 +32,7 @@ let plugins = {}
function loadplugin(pluginname, pluginpath) { function loadplugin(pluginname, pluginpath) {
try { try {
plugins[pluginname] = require(pluginpath) plugins[pluginname] = require(pluginpath)
plugins[pluginname]?.load(cfg) plugins[pluginname].load(cfg)
} catch (error) { } catch (error) {
if (error.code == 'MODULE_NOT_FOUND') { if (error.code == 'MODULE_NOT_FOUND') {
console.warn('plugin not used:', pluginpath) console.warn('plugin not used:', pluginpath)
@@ -44,7 +47,7 @@ function unloadplugin(pluginname, pluginpath) {
const plugin = require.resolve(pluginpath) const plugin = require.resolve(pluginpath)
try { try {
if (plugin && require.cache[plugin]) { if (plugin && require.cache[plugin]) {
require.cache[plugin].exports?.unload() require.cache[plugin].exports.unload()
delete plugins[pluginname] delete plugins[pluginname]
delete require.cache[plugin] delete require.cache[plugin]
} }
@@ -92,14 +95,15 @@ cfg.quiet = true
bot.once("spawn", () => { bot.once("spawn", () => {
plugins = { plugins = {
command: require('./plugins/command'), command: require('./plugins/command'),
eater: require('./plugins/eater'),
inventory: require('./plugins/inventory'),
informer: require('./plugins/informer'),
finder: require('./plugins/finder'),
sleeper: require('./plugins/sleeper'), sleeper: require('./plugins/sleeper'),
armor: require('./plugins/armor'), armor: require('./plugins/armor'),
// mover: require('./plugins/mover'), mover: require('./plugins/mover'),
guard: require('./plugins/guard'), guard: require('./plugins/guard'),
inventory: require('./plugins/inventory'), // miner: require('./plugins/miner.js'),
// eater: require('./plugins/eater'),
finder: require('./plugins/finder'),
miner: require('./plugins/miner'),
// statemachine: require('./plugins/statemachine'), // statemachine: require('./plugins/statemachine'),
} }

View File

@@ -1,12 +1,12 @@
const v = require('vec3') const v = require('vec3')
let mcData = require('minecraft-data') let mcData
let cfg = {} let cfg = {}
let bot = {} let bot = {}
// bot.chatAddPattern(new RegExp(`^hi|hello ${bot.username}`, "i"), 'greet', "General greeting") // bot.chatAddPattern(new RegExp(`^hi|hello ${bot.username}`, "i"), 'greet', "General greeting")
function todo() { function todo() {
bot.chat("not implemented yet") cfg.quiet && console.warn("not implemented") || bot.chat("not implemented yet")
} }
function checkBlockExists(name) { function checkBlockExists(name) {
@@ -34,19 +34,47 @@ const events = {
if (username === cfg.admin) { if (username === cfg.admin) {
message = message.replace("\\", "@") message = message.replace("\\", "@")
console.info("whispered command", message) console.info("whispered command", message)
bot.chat("/" + message) if (/^!/.test(message)) {
command(username, message)
} else {
bot.chat(message)
// bot.chat("/" + message)
}
} else { } else {
bot.whisper(cfg.admin, `gossip ${username}: ${message}`) bot.whisper(cfg.admin, `gossip ${username}: ${message}`)
console.info(username, "whispered", message) console.info(username, "whispered", message)
} }
} }
, chat: command , chat: command
, kicked: (reason, loggedIn) => console.warn(reason, loggedIn) , kicked: function rejoin(reason, loggedIn) {
console.warn(reason, loggedIn && "logged_in")
if (reason.extra && reason.extra[0].text === "Server closed") {
bot.quit()
bot.end()
// TODO implement all startup features (maybe refactor all into a single function / module?)
setTimeout((bot, cfg) => {
bot = mineflayer.createBot(cfg.botOptions)
}, 15 * 60 * 1000, bot, cfg);
}
}
} }
const events_registered = [] const events_registered = []
function command(username, message) { function command(username, message) {
function fuzzyRespond(responses, probability = 1, timeout = 1) {
if (Math.random() < probability) {
const response = responses[Math.floor(Math.random() * responses.length)]
return setTimeout(() => bot.chat(response), timeout * Math.random() * 1000)
}
}
function swingArm(swung = 3) {
if (swung > 0) {
setTimeout(swingArm, 500 * Math.random(), --swung)
bot.swingArm()
}
}
if (username === bot.username && !message.startsWith("!")) return if (username === bot.username && !message.startsWith("!")) return
@@ -59,240 +87,412 @@ function command(username, message) {
return 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)) { if (message.startsWith("!") || cfg.botAddress.test(message)) {
message = cfg.botAddress.test(message) ? cfg.botAddress.exec(message)[1] : message message = cfg.botAddress.test(message) ? cfg.botAddress.exec(message)[1] : message
console.log(message) console.log(message)
message = message.slice(1) message = message.slice(1) // remove `!`
message_parts = message.split(/\s+/) // TODO command dispatchEvent, for aliases
switch (message_parts[0]) { function subcommand(message) {
// case "follow": const message_parts = message.split(/\s+/)
// // if(username === cfg.admin)
// cfg.stateMachines.follow.transitions[4].trigger() switch (message_parts[0]) {
// // cfg.stateMachines.follow.transitions[1].trigger() case "stop":
// break; bot.pathfinder && bot.pathfinder.setGoal(null)
// case "stay": bot.stopDigging()
// // if(username === cfg.admin) bot.chat("ok")
// cfg.stateMachines.follow.transitions[3].trigger() break;
// break;
case "echo": case "mute":
// bot.chat(message_parts[1]) case "quiet":
// bot.chat(message.slice(1)) case "silent":
bot.chat(message) cfg.quiet = !cfg.quiet
break; cfg.quiet || bot.chat(`ok, ${cfg.quiet ? "" : "not "}being ${message_parts[0]}`)
case "autosleep": break;
case "sleep": // case "follow":
message_parts[1] = message_parts[0] == "autosleep" ? "auto" : message_parts[1] // // if(username === cfg.admin)
switch (message_parts[1]) { // cfg.stateMachines.follow.transitions[4].trigger()
case "auto": // // cfg.stateMachines.follow.transitions[1].trigger()
cfg.sleep.auto = !cfg.sleep.auto // break;
bot.chat(`ok, ${cfg.sleep.auto ? "" : "not "}auto sleeping `) // case "stay":
bot.chat("/afk") // // if(username === cfg.admin)
break; // cfg.stateMachines.follow.transitions[3].trigger()
case "silent": // break;
case "quiet": case "echo":
cfg.sleep.quiet = !cfg.sleep.quiet // bot.chat(message_parts[1])
bot.chat(`ok, ${cfg.sleep.quiet ? "" : "not "}sleeping quietly`) // bot.chat(message.slice(1))
break; cfg.quiet && bot.whisper(username, message) || bot.chat(message)
case "timeout": break;
if (!message_parts[2]) { case "autosleep":
bot.chat(`sleeping every ${Math.round(cfg.sleep.timeout / 60 / 1000)}mins`) case "sleep":
return message_parts[1] = message_parts[0] == "autosleep" ? "auto" : message_parts[1]
} switch (message_parts[1]) {
const timeout = parseFloat(message_parts[2], 10) case "auto":
if (timeout) { cfg.sleep.auto = !cfg.sleep.auto
cfg.sleep.timeout = timeout * 60 * 1000 bot.chat(`ok, ${cfg.sleep.auto ? "" : "not "}auto sleeping`)
cfg.sleep.timeoutFn = null bot.chat("/afk")
cfg.plugins.sleeper.sleep(true) //reset timer
bot.chat(`ok, next sleep attempt in ${timeout}mins`)
break; break;
} case "silent":
default: case "quiet":
bot.chat(`usage: !sleep [auto | quiet | timeout <mins>], or zzz for manual sleep`) cfg.sleep.quiet = !cfg.sleep.quiet
break; bot.chat(`ok, ${cfg.sleep.quiet ? "" : "not "}sleeping quietly`)
} break;
break; //todo; needed? case "timeout":
case "zzz": if (!message_parts[2]) {
cfg.plugins.sleeper.sleep() bot.chat(`sleeping every ${Math.round(cfg.sleep.timeout / 60 / 1000)}mins`)
case "afk": return
bot.chat("/afk") }
break; const timeout = parseFloat(message_parts[2], 10)
case "awaken": if (timeout) {
case "wake": cfg.sleep.timeout = timeout * 60 * 1000
case "wakeup": cfg.sleep.timeoutFn = null
cfg.plugins.sleeper.wake() cfg.plugins.sleeper.sleep(true) //reset timer
break; bot.chat(`ok, next sleep attempt in ${timeout}mins`)
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; break;
} }
default: default:
bot.chat("don't know wym") bot.chat(`usage: !sleep [auto | quiet | timeout <mins>], or zzz for manual sleep`)
} break;
// entity = new BehaviorGetClosestEntity(bot, bot.nearestEntity(), a => EntityFilters().MobsOnly(a)) }
// if (entity) { break; //todo; needed?
// bot.attack(entity, true) case "zzz":
// } else { cfg.plugins.sleeper.sleep()
// bot.chat('no nearby entities') case "afk":
// } bot.chat("/afk")
// // bot.attack(new BehaviorGetClosestEntity(bot, {}, EntityFilters().MobsOnly())) break;
break; case "awaken":
case "find": case "wake":
switch (message_parts.length) { case "wakeup":
case 2: cfg.plugins.sleeper.wake()
cfg.plugins.finder.findBlocks(message_parts[1]) break;
break case "autoeat":
case "eat":
switch (message_parts[1]) {
case "auto":
cfg.eat.auto = !cfg.eat.auto
bot.chat(`ok, ${cfg.eat.auto ? "" : "not "}auto eating `)
break;
case "at":
case "set":
const amount = parseInt(message_parts[2], 10)
if (amount < 20 && amount > 0) {
cfg.eat.startAt = amount
cfg.eat.quiet || bot.chat(`ok, eating when hunger at ${amount}`)
}
break
case "silent":
case "quiet":
cfg.eat.quiet = !!!cfg.eat.quiet
break;
default:
cfg.plugins.eater.eat((err) => { if (err) { bot.chat(err.message) } })
// bot.chat(`usage: !sleep [auto | quiet | timeout <mins>], or zzz for manual sleep`)
break;
}
break;
default: case "follow":
break; switch (message_parts.length) {
} case 1:
break subcommand("go follow me")
case "lookat": break;
// 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 default:
// case "give": subcommand("go " + message)
// // switch (message_parts[1]) { break;
// // case "hand": }
// // case "right": break;
// // break; case "come":
// // case "left": switch (message_parts[1]) {
// // break; case "close":
// // // case "slot": case "closer":
// // default: subcommand("go follow close")
// // let slot = parseInt(message_parts[1]) break
// // if (!isNaN(slot)) { case "up":
case "down":
cfg.plugins.mover.moveY(player.position)
break
default:
subcommand("go follow once")
}
break;
case "ride":
case "mount":
cfg.plugins.mover.command(message_parts)
break
case "unride":
case "getoff":
case "unmount":
case "dismount":
bot.dismount()
bot.vehicle = void 0
break
case "move":
case "go":
cfg.plugins.mover.command(message_parts.slice(1), player)
break;
// // } else { 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; break;
// // } }
// // break; default:
// case "take": bot.chat("don't know wym")
// // TODO take only what's requested, then throw all the rest }
// // TODO take all // entity = new BehaviorGetClosestEntity(bot, bot.nearestEntity(), a => EntityFilters().MobsOnly(a))
// case "toss": // if (entity) {
// case "drop": // bot.attack(entity, true)
// if (!message_parts[1]) { return false } // FIXME, works but ugly // } else {
// if (!checkItemExists(message_parts[1])) { return false } // bot.chat('no nearby entities')
// switch (message_parts.length) { // }
// case 2: // // bot.attack(new BehaviorGetClosestEntity(bot, {}, EntityFilters().MobsOnly()))
// bot.toss(mcData.blocksByName[message_parts[1]].id) break;
// break case "find":
// case 3: switch (message_parts.length) {
// bot.tossStack( case 2:
// mcData.itemsByName[message_parts[1]].id, cfg.plugins.finder.findBlocks(message_parts[1])
// (err) => { break
// 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;
}
break
// TODO move look (and maybe find) to informer plugin?
case "look":
case "lookat":
// const coords = v(message_parts.splice(1))
switch (message_parts.length) {
case 1:
bot.lookAt(bot.nearestEntity().position)
break
case 2:
switch (message_parts[1]) {
case "me":
if (player) {
bot.lookAt((new v.Vec3(0, 1, 0)).add(player.position))
} else {
cfg.quiet || bot.chat("can't see you")
}
break;
default: case "this":
break; // TODO lookat the block the user is looking at
} // Currently looks player position
case "where": if (player) {
case "where?": bot.lookAt(player.position)
// TODO put in /lib/location } else {
console.log(bot.entity.position) cfg.quiet || bot.chat("can't see you")
bot.chat(bot.entity.position.floored().toString()) }
break; break
case "warp": default:
// if(message_parts[1] == "spawn") const aPlayer = bot.players[message_parts[2]] ? bot.players[message_parts[2]].entity : null
bot.chat("/" + message) if (aPlayer) bot.lookAt((new v.Vec3(0, 1, 0)).add(aPlayer.position))
break; break;
}
break
case 3:
todo()
// bot.lookAt({})
break
case 4:
//TODO more checks
bot.lookAt(v(message_parts.splice(1)))
break
default:
break
}
break
case "info":
cfg.plugins.informer.command(message_parts.splice(1))
break
// case "use":
// bot.useOn(bot.nearestEntity())
// break;
default: // // TODO move all inventory related tasks into inventory.js
if (cfg.mods.includes(username)) case "craft":
cfg.plugins.inventory.craftItem(message_parts[1])
break
// 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
// TODO move subcommands to cfg.plugins.inventory.itemByName
case "toss":
case "drop":
if (!message_parts[1]) { return false } // FIXME, works but ugly
if (!mcData.findItemOrBlockByName(message_parts[1])) {
console.log("doesn't exist:", message_parts[1])
cfg.quiet || bot.chat(`item doesn't exist: ${message_parts[1]}`)
return false
}
const item = cfg.plugins.inventory.itemByName(message_parts[1])
if (!item) {
console.log("don't have:", message_parts[1])
cfg.quiet || bot.chat(`don't have item: ${message_parts[1]}`)
return false
}
switch (message_parts.length) {
case 2:
bot.tossStack(
item,
(err) => {
if (err) {
console.error(err)
cfg.quiet || bot.chat(err.message)
}
}
)
break
case 3:
const amount = parseInt(message_parts[2])
bot.toss(
item.type,
null, //metadata
amount,
(err) => {
if (err) {
console.error(err)
cfg.quiet || bot.chat(err.message)
}
}
)
break
default:
break
}
break;
case "location":
// TODO put in /lib/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)
if (cfg.mods.includes(username)) //anarchy
bot.chat(
bot.game.dimension.split(":", 2)[1].replace("_", " ")
+ " " + bot.entity.position.floored().toString()
)
break;
case "warp":
// if (message_parts[1] == "spawn")
// if (cfg.mods.includes(username)) //anarchy
bot.chat("/" + message) bot.chat("/" + message)
break;
default:
if (cfg.mods.includes(username))
bot.chat("/" + message)
break;
}
}
subcommand(message)
} else {
// TODO, maybe extract to a new function `fuzzychat`?, so can address direct messages
switch (message) {
case "wassup":
case "what's cooking":
case "what's new":
case "what's up":
case "whats cooking":
case "whats new":
case "whats up":
case "sup":
case "suh":
fuzzyRespond([
"jus chilin", "nothin", "random stuff"
], 0.3, 3)
case "hi":
case "hey":
case "hello":
case "ola":
case "howdy":
case "heyo":
case "yo":
if (player) bot.lookAt((new v.Vec3(0, 1, 0)).add(player.position))
// TODO sneak
// function swingArm() {
// if (swung > 0) {
// setTimeout(swing, 500 * Math.random())
// bot.p()
// swung--
// }
// }
setTimeout(swingArm, 1000, 4) // or sneak greating
return
case "F":
return fuzzyRespond(["F"], 0.9, 1)
case "RIP":
return fuzzyRespond(["F", "oh no"], 0.3, 2)
case "69":
case "cool":
Math.random() < 0.5 && bot.chat("nice!")
return
case "awesome":
case "superb":
case "nice":
case "nice!":
return fuzzyRespond(["cool", "very nice!"], 0.3, 5)
case "good bot":
return fuzzyRespond(["thanks", "why, thank you!", ":)", "(:", "you're too kind!"], 1, 5)
case "bad bot":
case "not nice":
return fuzzyRespond([":(", "):", "sorry"], 0.1, 10)
default:
break; break;
} }
} }
@@ -302,13 +502,12 @@ const load = (config) => {
cfg = config cfg = config
bot = cfg.bot bot = cfg.bot
mcData = mcData(bot.version) mcData = bot.mcData || (bot.mcData = require('minecraft-data')(bot.version))
for (const [key, fn] of Object.entries(events)) { for (const [key, fn] of Object.entries(events)) {
events_registered.push( events_registered.push(
bot.on(key, fn) bot.on(key, fn)
) )
} }
mcData = require('minecraft-data')(bot.version)
} }
const unload = () => { const unload = () => {

113
lib/plugins/eater.js Normal file
View File

@@ -0,0 +1,113 @@
let cfg = {}
let bot = {}
let isEating = false
function callbackHandle(err) {
if (err) console.error(err)
}
function eat(callback) {
isEating = true
const foodNames = require('minecraft-data')(bot.version).foodsArray.map((item) => item.name)
let available_food = bot.inventory
.items()
.filter((item) => foodNames.includes(item.name))
if (available_food.length === 0 || !available_food) {
isEating = false
return callback(new Error('No food found.'))
}
if (cfg.eat.bannedFood.length > 0) {
available_food = available_food.filter(
(item) => !cfg.eat.bannedFood.includes(item.name)
)
}
let priority = cfg.eat.priority
let best_food = available_food.reduce((prev, current) => (prev[priority] > current[priority]) ? prev : current)
if (!best_food) {
isEating = false
return callback(new Error('No best food has been found.'))
}
bot.emit('eat_start')
bot.equip(best_food, 'hand', function (error) {
if (error) {
console.error(error)
isEating = false
bot.emit('eat_stop')
} else {
bot.consume(function (err) {
if (err) {
console.error(err)
isEating = false
bot.emit('eat_stop')
return callback(err)
} else {
isEating = false
bot.emit('eat_stop')
callback(null)
if (!bot.food === 20) eat(callbackHandle)
}
})
}
})
}
function checkFood() {
console.info("eater: "
// , " status: ", !isEating
, cfg.eat.auto && "auto"
, bot.food < cfg.eat.startAt && "hungry"
, "hunger:", bot.food
, "at:", cfg.eat.startAt)
if (
!isEating
&& cfg.eat.auto
&& bot.food < cfg.eat.startAt
) {
if (
(bot.pathfinder
&& !(bot.pathfinder.isMining() || bot.pathfinder.isBuilding())
// TODO implement better idle state
) || true // idle most likely
) {
eat(callbackHandle)
}
}
}
function resetEat(value) {
// to prevent the plugin from breaking if the bot gets killed while eating btw
isEating = !!value // false
}
const load = (config) => {
cfg = config
bot = cfg.bot
cfg.eat = {
priority: 'saturation', //'foodPoints', //
// startAt: 19, //anarchy
// startAt: 18,
startAt: 14,
bannedFood: [
"enchanted_golden_apple", "golden_apple", "pufferfish", "chorus_fruit"
],
auto: true
}
bot.on('health', checkFood)
bot.on('spawn', resetEat)
}
const unload = () => {
bot.off('health', checkFood)
bot.off('spawn', resetEat)
}
module.exports = { load, unload, eat, resetEat }

View File

@@ -6,10 +6,12 @@ let cfg = {}
let bot = {} let bot = {}
let guardPos let guardPos
const filterallMobs = e => e.type === 'mob' const filterAllMobs = e => e.type === 'mob'
// const filterPlayers = e => e.type === 'player' && e.username !== 'Applezaus' const filterPlayers = e => e.type === 'player'
const filterMods = e => e.type === 'player' && [cfg.mods].includes(e.username)
// const filterPassiveMobs = e => e.kind === 'Passive mobs' // const filterPassiveMobs = e => e.kind === 'Passive mobs'
const filterCreeper = e => e.mobType === 'Creeper' const filterCreeper = e => e.mobType === 'Creeper'
// const filterCreeperOrEndermen = e => ['Creeper', 'EndderMen'].includes(e.mobType) //TODO endmen hostile by default?
const filterHostileMobs = e => e.kind === 'Hostile mobs' const filterHostileMobs = e => e.kind === 'Hostile mobs'
// // not needed if detecting by kind // // not needed if detecting by kind
// && e.mobType !== 'Armor Stand' // Mojang classifies armor stands as mobs for some reason? // && e.mobType !== 'Armor Stand' // Mojang classifies armor stands as mobs for some reason?
@@ -60,33 +62,56 @@ function lookForMobs() {
const filter = e => e.position.distanceTo(bot.entity.position) < 16 && filterHostileMobs(e) const filter = e => e.position.distanceTo(bot.entity.position) < 16 && filterHostileMobs(e)
const entityEnemy = bot.nearestEntity(filter) const entityEnemy = bot.nearestEntity(filter)
if (entityEnemy) { if (entityEnemy && !filterCreeper(entityEnemy)) {
if(filterCreeper(entityEnemy)) // if(filterCreeper(entityEnemy))
// Start attacking // Start attacking
// bot.off('time', lookForMobs) // bot.off('time', lookForMobs)
// bot.on('physicTick', lookForMobs) // bot.on('physicTick', lookForMobs)
bot.pvp.movements.canDig = !!cfg.move && cfg.move.canDig
bot.pvp.attack(entityEnemy) bot.pvp.attack(entityEnemy)
} else if (entityEnemy) {
bot.lookAt(
// (new v.Vec3(0, 1, 0)).add(
entityEnemy.position
// )
)
cfg.quiet || bot.chat("AH! A creeper! They creep me out!")
} }
} }
function guardSelf(entity) { function guardSelf(entity) {
if (!cfg.guard.self || entity !== bot.entity || guardPos) return // Do nothing if (!cfg.guard.self || entity !== bot.entity || guardPos) return // Do nothing
// bot.chat("") // bot.chat("")
bot.chat(( const hurtMessages = [
() => { const a = ["ouch!", "oww!", "help", "", "", ""]; return a[Math.floor(Math.random() * a.length)] } "ouch!", "oww!", "help", "monster!"
)()) ]
const hurtMessagesPlayer = [
"leave me alone!", "don't hurt me!", "bully!", "go away!", "stop", "no stop", "stop it", "murderer!",
"what have i ever done to you? :(",
]
console.info(bot.nearestEntity(filterAllMobs))
console.info(bot.nearestEntity(filterallMobs)) // Only look for mobs within 5 blocks
// const filter = e => e.position.distanceTo(bot.entity.position) < 5 && filterHostileMobs(e) || filterPlayers(e) && !filterMods(e)//anarchy
// Only look for mobs within 10 blocks const filter = e => e.position.distanceTo(bot.entity.position) < 5 && filterHostileMobs(e)
const filter = e => e.position.distanceTo(bot.entity.position) < 10 && filterHostileMobs(e)
const entityEnemy = bot.nearestEntity(filter) const entityEnemy = bot.nearestEntity(filter)
cfg.quiet || 0.2 < Math.random() && entityEnemy && bot.chat(
// TODO use fuzzy message function
filterPlayers(entityEnemy) ?
hurtMessagesPlayer[Math.floor(Math.random() * hurtMessagesPlayer.length)]
:
hurtMessages[Math.floor(Math.random() * hurtMessages.length)]
)
if (entityEnemy && !filterCreeper(entityEnemy)) { if (entityEnemy && !filterCreeper(entityEnemy)) {
// Start attacking // Start attacking
// bot.off('time', lookForMobs) // bot.off('time', lookForMobs)
// bot.on('physicTick', lookForMobs) // bot.on('physicTick', lookForMobs)
bot.pvp.attack(entityEnemy) bot.pvp.attack(entityEnemy)
bot.pvp.movements.canDig = !!cfg.move && cfg.move.canDig
} }
} }

128
lib/plugins/informer.js Normal file
View File

@@ -0,0 +1,128 @@
let cfg
let bot
let mcData
const v = require('vec3')
function block(pos) {
const block = pos ? bot.blockAt(v(pos)) : bot.blockAtCursor()
console.log(block, block && block.getProperties())
if (!block) {
cfg.quiet || bot.chat("empty block")
return block
}
let info = [block.type, block.name]
if (block.metadata) info.push(Object.entries(block.getProperties()))
cfg.quiet || bot.chat(info.join(": "))
}
function item(
slot,
entity = bot.entity
) {
const item = slot ?
bot.inventory.slots[parseInt(slot) + bot.QUICK_BAR_START] :
entity.heldItem
console.log(item)
if (!item) {
cfg.quiet || bot.chat("no item")
return item
}
let info = [item.type, item.name]
if (item.metadata) info.push("meta: " + item.metadata.length)
if (item.nbt) {
info.push(compound_value(item.nbt))
}
cfg.quiet || bot.chat(info.join("; "))
function compound_value(obj) {
if (typeof obj.value == "object") {
return compound_value(obj.value)
} else if (obj.value) {
return obj.value
} else if (typeof obj == "object") {
const keys = Object.keys(obj)
return keys.map(key => {
return `${key}: ${compound_value(obj[key])}`
});
} else {
return obj
}
}
return item
}
function entity(name) {
const entity = bot.nearestEntity((entity) => {
const ename = entity.name || entity.username
return name && ename ? ename == name : true
})
console.log(entity)
if (!entity) {
cfg.quiet || bot.chat("no entity")
return entity
}
let info = [entity.type, entity.name || entity.username]
if (entity.metadata) info.push("len: " + entity.metadata.length)
cfg.quiet || bot.chat(info.join("; "))
}
function command(message_parts) {
switch (message_parts.length) {
case 0:
// TODO most recent command?
block()
break;
case 1:
switch (message_parts[0]) {
case "item":
item()
break
case "entity":
entity()
break
case "block":
default:
block()
break;
}
break;
case 2:
switch (message_parts[0]) {
case "item":
item(message_parts[1])
break
case "entity":
default:
entity(message_parts[1])
break;
}
break
case 4:
switch (message_parts[0]) {
case "block":
default:
block(message_parts.slice(1))
break;
}
break;
default:
break;
}
}
const load = (config) => {
cfg = config
bot = cfg.bot
cfg.info = {
quiet: cfg.quiet,
}
mcData = bot.mcData || (bot.mcData = require('minecraft-data')(bot.version))
}
const unload = () => {}
module.exports = { load, unload, command, block, item, entity }

View File

@@ -10,22 +10,18 @@
*/ */
const mineflayer = require('mineflayer') 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 cfg = {}
let bot = {} let bot = {}
// let mcd // let mcd
const alt_destinations = {
offhand: "off-hand",
lefthand: "off-hand",
shield: "off-hand",
chest: "torso",
}
function inventory(username, message) { function inventory(username, message) {
if (username === bot.username) return if (username === bot.username) return
const command = message.split(' ') const command = message.split(' ')
@@ -72,8 +68,8 @@ function inventory(username, message) {
function sayItems(items = bot.inventory.items()) { function sayItems(items = bot.inventory.items()) {
const output = items.map(itemToString).join(', ') const output = items.map(itemToString).join(', ')
console.info("inventory:", output)
if (output) { if (output) {
console.info("inventory:", output)
!cfg.quiet && bot.chat(output) !cfg.quiet && bot.chat(output)
} else { } else {
!cfg.quiet && bot.chat('empty') !cfg.quiet && bot.chat('empty')
@@ -104,8 +100,20 @@ function tossItem(name, amount) {
function equipItem(name, destination, quiet = false) { function equipItem(name, destination, quiet = false) {
const item = itemByName(name) const item = itemByName(name)
if (item) { if (item) {
bot.equip(item, destination, checkIfEquipped) if (Object.keys(alt_destinations).includes(destination)) {
destination = alt_destinations[destination]
}
try {
bot.equip(item, destination, checkIfEquipped)
} catch (error) {
if (error.code == 'ERR_ASSERTION' && error.message.startsWith("invalid destination:")) {
bot.chat(error.message)
} else {
console.error(error)
}
}
} else { } else {
!quiet && bot.chat(`I have no ${name}`) !quiet && bot.chat(`I have no ${name}`)
} }
@@ -120,6 +128,9 @@ function equipItem(name, destination, quiet = false) {
} }
function unequipItem(destination) { function unequipItem(destination) {
if (Object.keys(alt_destinations).includes(destination)) {
destination = alt_destinations[destination]
}
bot.unequip(destination, (err) => { bot.unequip(destination, (err) => {
if (err) { if (err) {
bot.chat(`cannot unequip: ${err.message}`) bot.chat(`cannot unequip: ${err.message}`)
@@ -134,27 +145,54 @@ function useEquippedItem() {
bot.activateItem() bot.activateItem()
} }
function craftItem(name, amount) { function craftItem(name, amount = 1) {
amount = parseInt(amount, 10) amount = parseInt(amount, 10)
const item = require('minecraft-data')(bot.version).findItemOrBlockByName(name) const mcData = require('minecraft-data')(bot.version)
const item = mcData.findItemOrBlockByName(name)
const craftingTable = bot.findBlock({ const craftingTable = bot.findBlock({
matching: 58 matching: mcData.blocksByName["crafting_table"].id
}) })
const recipesNoTable = bot.recipesFor(item.id)
let recipes
if (recipesNoTable.length > 0) {
bot.chat("can make without crafting table!")
recipes = recipesNoTable
} else if (craftingTable) {
recipes = bot.recipesFor(item.id, null, null, craftingTable)
} else {
bot.chat("Couldn't find a crafting table. Maybe craft one?")
}
if (item) { if (item) {
const recipe = bot.recipesFor(item.id, null, 1, craftingTable)[0] let craftSuccess
if (recipe) { recipes && cfg.quiet || bot.chat(`${recipes.length} recipes`)
bot.chat(`I can make ${name}`) for (let recipe in recipes) {
bot.craft(recipe, amount, craftingTable, (err) => { if (craftSuccess) {
if (err) { break
bot.chat(`error making ${name}`) }
} else { setTimeout(craftIt, Math.random() * 5000, recipes[recipe], amount, craftingTable)
bot.chat(`did the recipe for ${name} ${amount} times`)
}
})
} else {
bot.chat(`I cannot make ${name}`)
} }
function craftIt(recipe, amount, craftingTable) {
cfg.quiet || bot.chat(`I can make ${name}`)
console.log("craft:", recipe)
console.time("craft recipe")
bot.craft(recipe, amount, craftingTable, craftError)
console.timeEnd("craft recipe")
}
function craftError(err) {
if (err) {
console.error(err)
cfg.quiet || bot.chat(`error making ${name}: ${err.message}`)
// continue
} else {
craftSuccess = true
bot.chat(`did the recipe for ${name} ${amount} times`)
// break
}
}
craftSuccess || cfg.quiet || bot.chat(`I couldn't make ${name}`)
} else { } else {
bot.chat(`unknown item: ${name}`) bot.chat(`unknown item: ${name}`)
} }
@@ -187,4 +225,4 @@ const unload = () => {
bot.off('chat', inventory) bot.off('chat', inventory)
} }
module.exports = { load, unload, equipItem } module.exports = { load, unload, equipItem, craftItem, itemByName }

View File

@@ -1,5 +1,6 @@
const mineflayer = require('mineflayer') // const mineflayer = require('mineflayer')
const pathfinder = require('mineflayer-pathfinder').pathfinder // const { pathfinder } = require('mineflayer-pathfinder')
let pathfinder
const { const {
gameplay, gameplay,
MoveTo, MoveTo,
@@ -10,8 +11,7 @@ const {
Craft Craft
} = require('prismarine-gameplay') } = require('prismarine-gameplay')
// const { Gameplay } = require('prismarine-gameplay/lib/gameplay') // const { Gameplay } = require('prismarine-gameplay/lib/gameplay')
// const { Vec3 } = require('vec3') const { Vec3 } = require('vec3')
let mcData = require('minecraft-data')
let cfg = {} let cfg = {}
let bot = {} let bot = {}
@@ -34,8 +34,9 @@ let bot = {}
// bot.on('chat', (username, message) => mine(username, message)) // bot.on('chat', (username, message) => mine(username, message))
function checkBlockExists(name){ function checkBlockExists(name){
if (mcData.blocksByName[name] === undefined) { const item = require('minecraft-data')(bot.version).findItemOrBlockByName(name)
bot.chat(`${name} is not a block name`) if (item === undefined) {
bot.chat(`${name} is not a block or item name`)
return false return false
} else { } else {
return true return true
@@ -121,10 +122,11 @@ function miner(username, message) {
itemType: command[2], itemType: command[2],
count: parseInt(command[1]) count: parseInt(command[1])
}), logError) }), logError)
break break
case /^collect [a-zA-Z_]+$/.test(message): case /^collect [a-zA-Z_]+$/.test(message):
if(!checkBlockExists(command[2])) {return false} if (!checkBlockExists(command[1])) { return false }
bot.gameplay.solveFor( bot.gameplay.solveFor(
new ObtainItem({ new ObtainItem({
itemType: command[1] itemType: command[1]
@@ -141,7 +143,7 @@ function miner(username, message) {
}), logError) }), logError)
break break
case /^craft [0-9]+ [a-zA-Z_]+$/.test(message): case /^craftmine [0-9]+ [a-zA-Z_]+$/.test(message):
if(!checkBlockExists(command[2])) {return false} if(!checkBlockExists(command[2])) {return false}
bot.gameplay.solveFor( bot.gameplay.solveFor(
new Craft({ new Craft({
@@ -152,23 +154,36 @@ function miner(username, message) {
break break
case /^stop$/.test(message): case /^stop$/.test(message):
bot.chat("♪♪ can't stop me now!! ♪♪") cfg.quiet || bot.chat("♪♪ can't stop me now!! ♪♪")
bot.pathfinder.setGoal(null)
bot.stopDigging()
// player = bot.player.entity // player = bot.player.entity
if (player) { // if (player) {
bot.gameplay.solveFor( // bot.gameplay.solveFor(
new MoveTo({ // new MoveTo({
x: player.position.x, // x: player.position.x,
y: player.position.y, // y: player.position.y,
z: player.position.z // z: player.position.z
}), logError) // }), logError)
} // }
break break
} }
} }
function logError(err) { function logError(err) {
if (err) if (err) {
bot.chat(`Failed task: ${err.message}`) switch (err.message) {
case "No more solutions available!":
console.log("miner: failed")
cfg.quiet || bot.chat("miner: out of solutions")
break;
default:
console.log(err)
cfg.quiet || bot.chat("miner: unknown error, check logs")
break;
}
}
} }
@@ -176,10 +191,15 @@ const load = (config) => {
cfg = config cfg = config
bot = cfg.bot bot = cfg.bot
mcData = mcData(bot.version) pathfinder = bot.pathfinder || bot.loadPlugin(require('mineflayer-pathfinder').pathfinder)
bot.loadPlugin(pathfinder) // bot.loadPlugin(pathfinder)
cfg.plugins.mover.initMoves(bot)
bot.loadPlugin(gameplay) bot.loadPlugin(gameplay)
cfg.plugins.mover.initMoves(bot)
bot.on("chat", miner) bot.on("chat", miner)
// bot.pathfinder.canDig
} }
const unload = () => { const unload = () => {
@@ -187,4 +207,4 @@ const unload = () => {
bot.off("chat", miner) bot.off("chat", miner)
} }
module.exports = { load, unload } module.exports = { load, unload, miner }

300
lib/plugins/mover.js Normal file
View File

@@ -0,0 +1,300 @@
const { Movements } = require('mineflayer-pathfinder')
const v = require('vec3')
let cfg = {}
let bot = {}
// let moving
let pathfinder
let mcData
let movements = []
function initMoves(bot = bot, mcData = bot.mcData) {
console.info(movements)
if (movements.length > 0) {
bot.pathfinder.setMovements(movements.defaultMove)
return console.warn("movements already initialized!")
}
let defaultMove = new Movements(bot, mcData)
defaultMove.canDig = false
defaultMove.scafoldingBlocks.push(mcData.blocksByName.slime_block.id)
defaultMove.blocksCantBreak.add(mcData.blocksByName.glass.id)
defaultMove.blocksToAvoid.add(mcData.blocksByName.magma_block.id)
movements.push(defaultMove)
movements.defaultMove = movements[0]
bot.pathfinder.setMovements(defaultMove)
}
function moveNear(pos, distance = 3) {
const { GoalNear } = require('mineflayer-pathfinder').goals
pos = v(pos)
cfg.quiet || bot.chat(`moving to ${pos.floored()}`)
bot.pathfinder.setMovements(movements.defaultMove)
bot.pathfinder.setGoal(new GoalNear(pos.x, pos.y, pos.z, distance))
}
function moveXZ(pos) {
const { GoalXZ } = require('mineflayer-pathfinder').goals
if (Array.isArray(pos) && pos.length == 2) {
pos = v(pos[0], 0, pos[1])
}
pos = v(pos)
console.log(pos)
cfg.quiet || bot.chat(`moving to ${pos.floored()}`)
bot.pathfinder.setMovements(movements.defaultMove)
bot.pathfinder.setGoal(new GoalXZ(pos.x, pos.z))
}
function moveY(pos) {
const { GoalY } = require('mineflayer-pathfinder').goals
if (Array.isArray(pos) && pos.length == 1) {
pos = v(null, pos[0], null)
}
pos = v(pos)
console.log(pos)
cfg.quiet || bot.chat(`moving to ${pos.floored()}`)
bot.pathfinder.setMovements(movements.defaultMove)
bot.pathfinder.setGoal(new GoalY(pos.y))
}
function follow(entity, dynamic = true, distance = 3) {
console.assert(entity)
const { GoalFollow } = require('mineflayer-pathfinder').goals
// console.log(entity)
cfg.quiet || bot.chat(
`following ${entity.type
}: ${entity.username || entity.displayName
}${dynamic ? "" : " once"}`
)
entity = entity.entity ? entity.entity : entity
// console.log(entity)
bot.pathfinder.setMovements(movements.defaultMove)
bot.pathfinder.setGoal(new GoalFollow(entity, distance), dynamic)
}
function ride(entity) {
entity = entity?.entity || entity
const ridableMobs = ["Horse", "Donkey", "Pig", "Strider"]
const vehicle = entity && typeof entity !== "string" ? entity : bot.nearestEntity(e => {
if (typeof entity === "string") return e.name === entity
const maybeRidableMob = e.mobType?.split(" ")
return e.kind == "Vehicles"
|| ridableMobs.includes(e.mobType)
|| maybeRidableMob && ridableMobs.includes(maybeRidableMob[maybeRidableMob.length - 1])
})
if (!vehicle) {
return cfg.quiet || bot.chat(`nothing to ride!`)
} else if ((dist = bot.entity.position.distanceSquared(vehicle.position)) > 36) {
bot.lookAt(vehicle.position)
follow(vehicle, false)
bot.once('goal_reached', ride)
return cfg.quiet || bot.chat(`${vehicle.name} bit far`)
}
console.log("vehicle:", vehicle)
bot.mount(vehicle)
}
function moveOrRide(turn = false, reverse = -1, directionLabel, message_parts2) {
// bot.once("attach", state = "vehiccel")
if (bot.vehicle) {
const amount = parseInt(message_parts2[0]) || 10 * -reverse
bot.moveVehicle(turn && amount || 0, !turn && amount || 0)
} else {
command([directionLabel].concat(message_parts2))
}
}
function hit(blockOrEntity) {
bot.chat(`hitting ${entity.name || entity.type}`)
}
function goalReached(goal) {
console.log(goal)
const entity = goal?.entity
let entityInfo = ""
if (entity) {
entityInfo += entity.type + ": "
switch (entity.type) {
case "player":
entityInfo += entity.username
break;
default:
break;
}
}
cfg.quiet || bot.chat(`goal reached: ${entityInfo}; pos: [x:${goal?.x}, y:${goal?.y}, z:${goal?.z}]`)
}
function stop() {
bot.pathfinder.setGoal(null)
bot.stopDigging()
}
function command(message_parts, player) {
const message_parts2 = message_parts.slice(1)
switch (message_parts[0]) {
case "init":
initMoves()
break
case "near":
switch (message_parts2.length) {
case 0:
moveNear(bot.nearestEntity().position)
break
case 1:
switch (message_parts2[0]) {
case "me":
if (player) {
moveNear(player.position)
} else {
cfg.quiet || bot.chat("can't see you")
}
break;
default:
const aPlayer = bot.players[message_parts2[0]] ? bot.players[message_parts2[0]].entity : null
if (aPlayer) {
moveNear(aPlayer.position)
} else {
cfg.quiet || bot.chat(`can't see ${message_parts2[0]}`)
}
break;
}
break
case 2:
//TODO this isn't near
moveXZ(message_parts2)
break
case 3:
//TODO more checks
moveNear(message_parts2)
break
default:
break
}
break
case "follow":
// message_parts2 = message_parts.slice(2)
switch (message_parts2.length) {
case 0:
follow(bot.nearestEntity())
break
case 1:
let dist = 3
switch (message_parts2[0]) {
case "close":
dist = 1
case "me":
case "once":
if (player) {
follow(player, message_parts2[0] === "me", dist)
} else {
cfg.quiet || bot.chat("can't see you")
}
break;
default:
const aPlayer = bot.players[message_parts2[0]] ? bot.players[message_parts2[0]].entity : null
if (aPlayer) {
follow(aPlayer)
} else {
cfg.quiet || bot.chat(`can't see ${message_parts2[0]}`)
}
break;
}
break
// case 2:
// bot.lookAt({}) goalxz?
// break
// case 3:
//TODO more checks
// moveNear(message_parts2)
// break
default:
cfg.quiet || bot.chat("unknown or bad command")
break
}
break
case "ride":
case "mount":
ride(message_parts2[0])
break
case "w":
case "f":
moveOrRide(0, -1, "forward", message_parts2)
break
case "s":
case "b":
moveOrRide(0, 1, "back", message_parts2)
break
case "a":
case "l":
moveOrRide(1, -1, "right", message_parts2)
break
case "d":
case "r":
moveOrRide(1, 1, "left", message_parts2)
break
case "back":
case "forward":
case "jump":
case "left":
case "right":
case "sneak":
case "sprint":
console.info(bot.controlState[message_parts[0]], bot.entity.position.floored())
bot.setControlState(message_parts[0], true)
console.info(bot.controlState[message_parts[0]])
setTimeout(bot.setControlState, 100 * (message_parts[1] || 2), message_parts[0], false)
setTimeout(console.info, 5000, bot.controlState[message_parts[0]], bot.entity.position.floored())
break
case "stop":
stop()
break
default:
return cfg.quiet || bot.chat(`unknown command ${message_parts[0]}`)
}
}
const load = (config) => {
cfg = config
bot = cfg.bot
cfg.move = {
// auto: true,
canDig: false,
// list: ["hello", "wassup"],
quiet: !!cfg.quiet,
movements: []
}
mcData = bot.mcData || (bot.mcData = require('minecraft-data')(bot.version))
pathfinder = bot.pathfinder || bot.loadPlugin(require('mineflayer-pathfinder').pathfinder)
// initMoves(bot, mcData)
setTimeout(initMoves, 500, bot, mcData)
bot.on('goal_reached', goalReached)
}
const unload = () => {
stop()
bot.off('goal_reached', goalReached)
}
module.exports = {
load, unload, command,
stop, initMoves,
moveNear, moveXZ, moveY, follow,
ride
}

217
lib/plugins/mycellium.js Normal file
View File

@@ -0,0 +1,217 @@
// const mineflayer = require('mineflayer')
// let pathfinder
// const { pathfinder, Movements, goals } = require('mineflayer-pathfinder')
const { Vec3 } = require('vec3')
// const { GoalFollow, GoalNear } = goals
let GoalFollow, GoalNear
// const mcData = require('minecraft-data')('1.16.5')
let mcData
// let bot = mineflayer.createBot()
let bot
let cfg = { bot: null }
let timer
let movements
/* let mcData
bot.once('spawn', () => {
mcData = require('minecraft-data')(bot.version)
}) */
function stopCovering(quiet = cfg.quiet, resetGoal = true) { // This is a function to stop the cover() loop when called
if (timer) {
clearTimeout(timer);
timer = null;
}
if (resetGoal) bot.pathfinder.setGoal(null)
bot.stopDigging()
quiet || bot.chat("stopped covering")
}
function init() {
const { Movements, goals } = require('mineflayer-pathfinder')
GoalFollow = goals.GoalFollow
GoalNear = goals.GoalNear
movements = new Movements(bot, mcData)
movements.canDig = true // Lets the bot dig
bot.pathfinder.setMovements(movements)
console.info("mycelium start")
stopCovering(true)
bot.waitForChunksToLoad(cover)
}
function cover(timeInt = 1000) {
if (!Number.isSafeInteger(timeInt)) {
console.log("cover int maybe goal?", timeInt)
switch (timeInt.message) {
case "No path to the goal!":
console.info("Cover: can't reach")
cfg.quiet || bot.chat("can't reach")
break;
default:
break;
}
if (timeInt) {
timeInt = 5000
}
} else if (timeInt < 300) {
timeInt = 1000
}
const wool = "white_wool"
const wool_item = mcData.itemsByName[wool]
const inventoryWool = bot.inventory.findInventoryItem(wool_item.id)
// console.info(wool_item.id, inventoryWool)
// if (!inventoryWool) return
// bot.loadPlugin(pathfinder)
const myceliumClean = bot.findBlock({ // Const that is a brown_mushroom
maxDistance: 6,
matching: (block) => {
// First check the type
// lol
// const { brown_mushroom, red_mushroom, white_wool } = mcData.blocksByName
// if ([brown_mushroom.id, red_mushroom.id, white_wool.id].includes(block?.type)) {
const { brown_mushroom, red_mushroom, } = mcData.blocksByName
if ([brown_mushroom.id, red_mushroom.id,].includes(block?.type)) {
// If position is defined, you can refine the search
if (block.position) {
const blockBelow = bot.blockAt(block.position.offset(0, -1, 0))
return blockBelow?.type === mcData.blocksByName.mycelium.id || blockBelow?.type === mcData.blocksByName.spruce_fence.id // Makes sure there is mycelium below
}
return true // otherwise return always true (there is water in the section so it should be checked)
}
return false
}
})
function findMycelium(dist = 5) {
return bot.findBlock({
maxDistance: dist,
matching: (block) => {
// First check the type
if (block?.type === mcData.blocksByName.mycelium.id) { // Const that is a mycelium block
// If position is defined, you can refine the search
if (block.position) {
const blockAbove = bot.blockAt(block.position.offset(0, 1, 0))
return !blockAbove || blockAbove?.type === mcData.blocksByName.air.id // Makes sure there is nothing above
}
return true // otherwise return always true (there is water in the section so it should be checked)
}
return false
}
})
}
let mycelium = findMycelium()
if (myceliumClean) {
// bot.dig(myceliumClean, true)
bot.dig(myceliumClean)
}
if (mycelium) {
timeInt = 500
if (bot.heldItem?.type !== wool_item.id) { // Equips wool if not already
if (!inventoryWool || inventoryWool.count < 10) { // Checks if there is less than 10 wool in the bots inventory
timeInt = 5000
console.warn("no wool")
// const chestLocation = new Vec3(10614, 70, 5350) // Sets chest location
const chestLocation = bot.findBlock({
maxDistance: 100,
matching: block => block && block.type === mcData.blocksByName.chest.id
})?.position // Sets chest location
const chestGoal = new GoalNear(chestLocation.x, chestLocation.y, chestLocation.z, 6) // Sets goal to chest location
return bot.pathfinder.goto(chestGoal, () => { // Run code below when it gets to the chest
bot.lookAt(chestLocation, true) // Looks at chest
const chest = bot.openChest(bot.blockAt(chestLocation)) // Sets const to for opening chest
chest.once('open', (err) => { // Opens chest
if (err) {
return console.error('Chest error', err)
}
const chest_item = chest.items().filter(item => item.type === wool_item.id)
console.info(chest, chest_item)
if (chest_item.length > 0) { // Checks that there is stuff in chest
try {
// Pulls out a chest (27 stack) of wool
// chest.withdraw(chest_item[0].type, null, 64 * 27)
chest.withdraw(chest_item[0].type, null, 64 * 3)
} catch (error) {
console.error('Chest withdraw error', error)
}
bot.once("close", cover)
} else {
console.log('Chest dont have', wool_item)
cfg.quiet || bot.chat(`Not enough ${wool} in chest`)
stopCovering()
}
setTimeout(chest.close, timeInt)
})
})
} else {
bot.equip(wool_item.id, "hand")
}
} else {
const pos = mycelium.position
bot.lookAt(pos, true)
// let tryCount = 0
const flooredPos = bot.entity.position.floored()
if (flooredPos.offset(0, 1, 0).distanceTo(pos) <= 2) {
bot.setControlState('jump', true)
if (bot.entity.position.y > mycelium.position.y) {
bot.placeBlock(mycelium, new Vec3(0, 1, 0), (err) => {
setTimeout(bot.setControlState, 2000, 'jump', false)
if (err) {
console.error('Place (jumped)', err)
}
})
}
} else {
bot.placeBlock(mycelium, new Vec3(0, 1, 0), (err) => {
if (err) {
if (err.message !== `No block has been placed : the block is still ${wool}`) {
return console.error('Place (normal)', err)
} else {
return
}
}
})
}
}
} else {
mycelium = findMycelium(100)
if (mycelium) {
const pos = mycelium.position
const goal = new GoalNear(pos.x, pos.y, pos.z, 3)
stopCovering(true)
timeInt = 2000
return bot.pathfinder.goto(goal, cover)
} else {
stopCovering(true)
return cfg.quiet || bot.chat("no uncovered mycelium nearby")
}
}
timer = setTimeout(cover, timeInt, timeInt)
}
function command(params) {
stopCovering(true)
cover()
}
const load = (config) => {
cfg = config
bot = cfg.bot
mcData = bot.mcData || (bot.mcData = require('minecraft-data')(bot.version))
pathfinder = bot.pathfinder || bot.loadPlugin(require('mineflayer-pathfinder').pathfinder)
init()
}
const unload = () => {
stopCovering(true)
}
module.exports = { load, unload, command, cover, stopCovering }

View File

@@ -1,76 +1,90 @@
const pathfinder = require('mineflayer-pathfinder').pathfinder let pathfinder
const { Bot } = require('mineflayer') //TODO replace with simple pathfinder motions
const {
gameplay,
MoveTo,
MoveToInteract,
ObtainItem,
// Craft
} = require('prismarine-gameplay')
let cfg = {} let cfg = {}
let bot = {} let bot = {}
let inv let inv
// cfg.autosleep = false // cfg.autosleep = false
function sleep(quiet) { function sleep(quiet = cfg.sleep.quiet) {
quiet = quiet !== undefined ? quiet : cfg.sleep.quiet
if(bot.game.dimension !== "minecraft:overworld" || cfg.sleep.force){ if(bot.game.dimension !== "minecraft:overworld" || cfg.sleep.force){
!quiet && bot.chat("can't sleep, not in overworld now") !quiet && bot.chat("can't sleep, not in overworld now")
return return
} }
if (bot.isSleeping && !cfg.sleep.force) {
!quiet && bot.chat("already in bed!")
return
}
let bed = bot.findBlock({ let bed = bot.findBlock({
matching: block => bot.isABed(block) matching: block => bot.isABed(block)
}) })
let bedstatus = bed && bot.parseBedMetadata(bed).occupied ? "n unoccupied" : "" let bed_occupied = bed && bot.parseBedMetadata(bed).occupied
if(bed && bedstatus == "n unoccupied"){ if (bed && bed_occupied) {
bot.lookAt(bed.position) bot.lookAt(bed.position)
bed = bot.findBlock({ bed = bot.findBlock({
matching: block => bot.isABed(block) && !bot.parseBedMetadata(block).occupied matching: block => bot.isABed(block) && !bot.parseBedMetadata(block).occupied
}) || bed }) || bed
bedstatus = bot.parseBedMetadata(bed).occupied ? "n unoccupied" : "" bed_occupied = bot.parseBedMetadata(bed).occupied
} }
if (bed && bedstatus == "") { if (bed && !bed_occupied) {
bot.lookAt(bed.position) bot.lookAt(bed.position)
// const nearbed = bot.waitForChunksToLoad(() => {
bot.gameplay.solveFor( cfg.plugins.mover && cfg.plugins.mover.moveNear(bed.position, 2)
new MoveTo((bed.position.range = 2) && bed.position), (err) => { bot.once('goal_reached', (goal) => {
// new MoveTo(bed.position), (err) => { console.info(goal)
// new MoveToInteract(bed.position), (err) => { try {
if (err) { bot.sleep(bed, (err) => {
!quiet && bot.chat(`can't reach bed: ${err.message}`) if (err) {
} else { !quiet && bot.chat(`can't sleep: ${err.message}`)
bot.waitForChunksToLoad(() => { } else {
bot.sleep(bed, (err) => { !quiet && bot.chat("zzz")
if (err) { // apparently, `bot.isSleeping = true` takes a while
!quiet && bot.chat(`can't sleep: ${err.message}`) // maybe it's async
} else { console.log("sleeping? ", bot.isSleeping)
!quiet && bot.chat("zzz") }
console.log("sleeping? ", bot.isSleeping)
// hack until this is fixed
// bot.isSleeping = bot.isSleeping ? bot.isSleeping : true
bot.isSleeping = true
}
})
}) })
} catch (error) {
console.error(error)
} }
}) })
// } else if (bed){ })
} else if (inv && inv.equipItem("red_bed", "hand", true)) { } else if (bed = bot.inventory.items().filter(bot.isABed)[0]) {
// doesn't work fortunately const v = require('vec3')
// FIXME: DONT IMPLEMENT until it is detected as NOT NETHER bot.equip(bed, "hand", (err) => { if (err) console.error(err) })
bot.placeBlock() bot.waitForChunksToLoad(() => {
let refBlock =
// FIXME hack to get around findBlock returning null
bot.blockAt(bot.entity.position.offset(1, 0, 1), false)
// bot.findBlock({
// matching: (block) => {
// // if (block && block.type !== 0 && block.position) {
// if (block && block.position) {
// console.info("found", block)
// const blockAbove = bot.blockAt(block.position.offset(0, 1, 0))
// return !blockAbove || blockAbove.type === 0
// }
// // console.info("not found", block)
// return false
// }
// , maxDistance: 10
// })
console.log(refBlock)
bot.placeBlock(refBlock, new v.Vec3(0, 1, 0), console.error)
setTimeout(sleep, 3000, true)
})
} else { } else {
bot.gameplay.solveFor( // TODO: use mover
new ObtainItem("bed"), (err) => { // bot.gameplay.solveFor(
if (err) { // new ObtainItem("bed"), (err) => {
!quiet && bot.chat(`need a${bedstatus} bed: may not see if just placed`) // if (err) {
} !quiet && bot.chat(`need a${bed_occupied ? "n unoccupied" : ""} bed: may not see if just placed`)
} // }
) // }
// )
// bot.chat('/afk') // bot.chat('/afk')
} }
bot.pathfinder.movements
} }
function wake() { function wake() {
@@ -86,8 +100,8 @@ function wake() {
function autoSleep() { function autoSleep() {
if (!bot.time.isDay && !cfg.sleep.timeoutFn && cfg.sleep.auto && !bot.isSleeping) { if (!bot.time.isDay && !cfg.sleep.timeoutFn && cfg.sleep.auto && !bot.isSleeping) {
sleep() sleep()
cfg.sleep.timeoutFn = setTimeout(() => { cfg.sleep.timeoutFn = null }, cfg.sleep.timeout) // give 2 seconds for multiple events cfg.sleep.timeoutFn = setTimeout(() => { cfg.sleep.timeoutFn = null }, cfg.sleep.timeout)
console.log("sleeping?", bot.isSleeping, bot.time) console.log("sleeping?", bot.isSleeping, bot.time.isDay, bot.time.timeOfDay)
} }
} }
@@ -98,11 +112,11 @@ const load = (config) => {
auto: true, auto: true,
// timeout: 30 * 1000, // timeout: 30 * 1000,
timeout: 2 * 60 * 1000, timeout: 2 * 60 * 1000,
quiet: false quiet: !!cfg.quiet
} }
bot.loadPlugin(pathfinder) pathfinder = bot.pathfinder || require('mineflayer-pathfinder').pathfinder
bot.loadPlugin(gameplay) // bot.loadPlugin(pathfinder)
inv = cfg.plugins["inventory"] inv = cfg.plugins["inventory"]
bot.on("time", autoSleep) bot.on("time", autoSleep)

View File

@@ -34,21 +34,20 @@
"require-self": "^0.2.3" "require-self": "^0.2.3"
}, },
"dependencies": { "dependencies": {
"minecraft-data": "^2.70.2", "dotenv-packed": "^1.2.1",
"mineflayer": "^2.34.0", "minecraft-data": "^2.73.1",
"mineflayer-armor-manager": "^1.3", "mineflayer": "^2.40.1",
"mineflayer-pathfinder": "^1.1", "mineflayer-armor-manager": "^1.4.0",
"mineflayer-pvp": "^1", "mineflayer-pathfinder": "^1.3.6",
"prismarine-block": "^1", "mineflayer-pvp": "^1.0.2",
"prismarine-chat": "^1", "prismarine-block": "^1.7.3",
"prismarine-chat": "^1.0.3",
"prismarine-entity": "^1.1.0", "prismarine-entity": "^1.1.0",
"prismarine-gameplay": "github:TheDudeFromCI/prismarine-gameplay#crafting",
"prismarine-item": "^1.5.0", "prismarine-item": "^1.5.0",
"prismarine-nbt": "^1.3", "prismarine-nbt": "^1.4.0",
"prismarine-recipe": "^1", "prismarine-recipe": "^1.1.0",
"typescript": "^4", "typescript": "^4.1.3",
"vec3": "^0.1", "vec3": "^0.1.7"
"dotenv-packed": "^1.2"
}, },
"files": [ "files": [
"lib/**/*" "lib/**/*"