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