Compare commits
	
		
			79 Commits
		
	
	
		
			nogameplay
			...
			afd2e002df
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					afd2e002df | ||
| 
						 | 
					3c5ec6b360 | ||
| 
						 | 
					b71728e503 | ||
| 
						 | 
					d001280383 | ||
| 
						 | 
					f50e388c39 | ||
| 
						 | 
					135ce6567b | ||
| 
						 | 
					e5803eee59 | ||
| 
						 | 
					1b21fcb096 | ||
| 
						 | 
					4b3e58ceb3 | ||
| 
						 | 
					8e854a0a2f | ||
| 
						 | 
					c42a3a2304 | ||
| 
						 | 
					b453b7d6bd | ||
| 
						 | 
					6b79f1fc60 | ||
| 
						 | 
					6e1ef5aada | ||
| 
						 | 
					e2ae7e5ad2 | ||
| 
						 | 
					42138a421b | ||
| 
						 | 
					aded1e4193 | ||
| 
						 | 
					60394e38eb | ||
| 
						 | 
					22490f7ec1 | ||
| 
						 | 
					72c4622091 | ||
| 
						 | 
					0757776d8b | ||
| 
						 | 
					a0ffaf1654 | ||
| 
						 | 
					1597acca72 | ||
| 
						 | 
					1e82045221 | ||
| 
						 | 
					33c4fc0c77 | ||
| 
						 | 
					33c4233223 | ||
| 
						 | 
					3f3ebbae10 | ||
| 
						 | 
					fd0e1e1347 | ||
| 
						 | 
					2f88eedce9 | ||
| 
						 | 
					a0893f2b29 | ||
| 
						 | 
					b1a592dbbd | ||
| 
						 | 
					f336e3d736 | ||
| 
						 | 
					d953bd4cf6 | ||
| 
						 | 
					fc43985337 | ||
| 
						 | 
					6b1157147d | ||
| 
						 | 
					e6d29576e7 | ||
| 
						 | 
					974d460061 | ||
| 
						 | 
					1d361e04a6 | ||
| 
						 | 
					7597620626 | ||
| 
						 | 
					e5faa6f022 | ||
| 
						 | 
					cc18ac5c2e | ||
| 
						 | 
					7050a1621b | ||
| 
						 | 
					7cbfa16476 | ||
| 
						 | 
					63849e0729 | ||
| 
						 | 
					4e7f8d59fd | ||
| 
						 | 
					7b2b936f81 | ||
| 
						 | 
					8a39596b1d | ||
| 
						 | 
					2601b7cfb1 | ||
| 
						 | 
					67932b2f6a | ||
| 
						 | 
					9a6e684b11 | ||
| 
						 | 
					3488a94233 | ||
| 
						 | 
					4d21327086 | ||
| 
						 | 
					e74d796124 | ||
| 
						 | 
					3d5ffe38cd | ||
| 
						 | 
					b519913355 | ||
| 
						 | 
					b1dab1968c | ||
| 
						 | 
					3219ec6155 | ||
| 
						 | 
					f38ad8c819 | ||
| 
						 | 
					4b8a39d38c | ||
| 
						 | 
					6b71de0356 | ||
| 
						 | 
					96214ffe37 | ||
| 
						 | 
					034f8d331a | ||
| 
						 | 
					69d0f5830d | ||
| 
						 | 
					8e719d5ccf | ||
| 
						 | 
					112eb04a8d | ||
| 
						 | 
					ba7c53be0c | ||
| 
						 | 
					5b4718fa5d | ||
| 
						 | 
					902732c6dd | ||
| 
						 | 
					65d13a3379 | ||
| 
						 | 
					288b7045b6 | ||
| 
						 | 
					94574a4296 | ||
| 
						 | 
					68e60921b1 | ||
| 
						 | 
					f2281a7cb3 | ||
| 
						 | 
					e9f2080556 | ||
| 
						 | 
					086251bce6 | ||
| 
						 | 
					0ae961521f | ||
| 
						 | 
					eae4e95803 | ||
| 
						 | 
					fb066ee8a5 | ||
| 
						 | 
					f4445749e6 | 
							
								
								
									
										16
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					# See this article for reference: https://help.github.com/articles/dealing-with-line-endings/
 | 
				
			||||||
 | 
					# Refreshing repo after line ending change:
 | 
				
			||||||
 | 
					# https://help.github.com/articles/dealing-with-line-endings/#refreshing-a-repository-after-changing-line-endings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Handle line endings automatically for files detected as text
 | 
				
			||||||
 | 
					# and leave all files detected as binary untouched.
 | 
				
			||||||
 | 
					* text=auto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# The above will handle all files NOT found below
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# These files are text and should be normalized (Convert crlf => lf)
 | 
				
			||||||
 | 
					# Use lf as eol for these files
 | 
				
			||||||
 | 
					*.js    text eol=lf
 | 
				
			||||||
 | 
					*.ts    text eol=lf
 | 
				
			||||||
 | 
					*.json  text eol=lf
 | 
				
			||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -10,6 +10,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# production
 | 
					# production
 | 
				
			||||||
/build
 | 
					/build
 | 
				
			||||||
 | 
					/data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# misc
 | 
					# misc
 | 
				
			||||||
.DS_Store
 | 
					.DS_Store
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "conventionalCommits.scopes": [
 | 
				
			||||||
 | 
					        "command",
 | 
				
			||||||
 | 
					        "mover",
 | 
				
			||||||
 | 
					        "sleeper",
 | 
				
			||||||
 | 
					        "informer",
 | 
				
			||||||
 | 
					        "statemachine",
 | 
				
			||||||
 | 
					        "builder"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								lib/index.js
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								lib/index.js
									
									
									
									
									
								
							@@ -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 || 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 || 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 || 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 || env.MINECRAFT_USER,
 | 
				
			||||||
        password: process.argv[4] || process.env.MINECRAFT_PASS || env.MINECRAFT_PASS,
 | 
					        password: process.argv[4] || process.env.MINECRAFT_PASS || 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 = {}
 | 
				
			||||||
@@ -44,7 +47,9 @@ 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()
 | 
					            // `unload()` isn't exported sometimes,
 | 
				
			||||||
 | 
					            // when plugin isn't properly loaded
 | 
				
			||||||
 | 
					            require.cache[plugin].exports?.unload?.()
 | 
				
			||||||
            delete plugins[pluginname]
 | 
					            delete plugins[pluginname]
 | 
				
			||||||
            delete require.cache[plugin]
 | 
					            delete require.cache[plugin]
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -59,7 +64,7 @@ reloadplugin = (event, filename, pluginpath) => {
 | 
				
			|||||||
        console.info(event, filename)
 | 
					        console.info(event, filename)
 | 
				
			||||||
        pluginpath = (pluginpath ? pluginpath : './plugins/') + filename
 | 
					        pluginpath = (pluginpath ? pluginpath : './plugins/') + filename
 | 
				
			||||||
        const check = Object.keys(cfg.plugins)
 | 
					        const check = Object.keys(cfg.plugins)
 | 
				
			||||||
        console.info(`reload file:`, pluginpath)
 | 
					        console.info(`reload file: ./lib/${pluginpath}`)
 | 
				
			||||||
        const plugin = require.resolve(pluginpath)
 | 
					        const plugin = require.resolve(pluginpath)
 | 
				
			||||||
        if (plugin && require.cache[plugin]) {
 | 
					        if (plugin && require.cache[plugin]) {
 | 
				
			||||||
            // console.debug(Object.keys(cfg.plugins))
 | 
					            // console.debug(Object.keys(cfg.plugins))
 | 
				
			||||||
@@ -83,7 +88,8 @@ reloadplugin = (event, filename, pluginpath) => {
 | 
				
			|||||||
fs.watch('./lib/plugins', reloadplugin)
 | 
					fs.watch('./lib/plugins', reloadplugin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cfg.bot = bot
 | 
					cfg.bot = bot
 | 
				
			||||||
cfg.botAddress = new RegExp(`^${bot.username} (!.+)`)
 | 
					// TODO better name, or switch to array
 | 
				
			||||||
 | 
					cfg.botAddressPrefix = '!'
 | 
				
			||||||
cfg.quiet = true
 | 
					cfg.quiet = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -92,18 +98,20 @@ cfg.quiet = true
 | 
				
			|||||||
bot.once("spawn", () => {
 | 
					bot.once("spawn", () => {
 | 
				
			||||||
    plugins = {
 | 
					    plugins = {
 | 
				
			||||||
        command: require('./plugins/command'),
 | 
					        command: require('./plugins/command'),
 | 
				
			||||||
        sleeper: require('./plugins/sleeper'),
 | 
					        informer: require('./plugins/informer'),
 | 
				
			||||||
        armor: require('./plugins/armor'),
 | 
					 | 
				
			||||||
        mover: require('./plugins/mover'),
 | 
					 | 
				
			||||||
        guard: require('./plugins/guard'),
 | 
					 | 
				
			||||||
        inventory: require('./plugins/inventory'),
 | 
					        inventory: require('./plugins/inventory'),
 | 
				
			||||||
        eater: require('./plugins/eater'),
 | 
					 | 
				
			||||||
        finder: require('./plugins/finder'),
 | 
					        finder: require('./plugins/finder'),
 | 
				
			||||||
 | 
					        mover: require('./plugins/mover'),
 | 
				
			||||||
 | 
					        sleeper: require('./plugins/sleeper'),
 | 
				
			||||||
 | 
					        eater: require('./plugins/eater'),
 | 
				
			||||||
 | 
					        armor: require('./plugins/armor'),
 | 
				
			||||||
 | 
					        guard: require('./plugins/guard'),
 | 
				
			||||||
        // miner: require('./plugins/miner.js'),
 | 
					        // miner: require('./plugins/miner.js'),
 | 
				
			||||||
        // statemachine: require('./plugins/statemachine'),
 | 
					        statemachine: require('./plugins/statemachine'),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cfg.plugins = plugins
 | 
					    cfg.plugins = plugins
 | 
				
			||||||
 | 
					    cfg.botAddressRegex = new RegExp(`^${bot.username} (${cfg.botAddressPrefix}.+)`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const plugin of Object.values(plugins)) {
 | 
					    for (const plugin of Object.values(plugins)) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
const v = require('vec3')
 | 
					const v = require('vec3')
 | 
				
			||||||
let mcData = require('minecraft-data')
 | 
					let mcData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let cfg = {}
 | 
					let cfg = {}
 | 
				
			||||||
let bot = {}
 | 
					let bot = {}
 | 
				
			||||||
@@ -30,11 +30,10 @@ const events = {
 | 
				
			|||||||
    whisper: function command_whisper(username, message) {
 | 
					    whisper: function command_whisper(username, message) {
 | 
				
			||||||
        if ([bot.username, "me"].includes(username)) return
 | 
					        if ([bot.username, "me"].includes(username)) return
 | 
				
			||||||
        if (/^gossip/.test(message)) return
 | 
					        if (/^gossip/.test(message)) return
 | 
				
			||||||
        // if (/^!/.test(message) && username === cfg.admin){
 | 
					 | 
				
			||||||
        if (username === cfg.admin) {
 | 
					        if (username === cfg.admin) {
 | 
				
			||||||
            message = message.replace("\\", "@")
 | 
					            message = message.replace("\\", "@")
 | 
				
			||||||
            console.info("whispered command", message)
 | 
					            console.info("whispered command", message)
 | 
				
			||||||
            if (/^!/.test(message)) {
 | 
					            if (message.startsWith(cfg.botAddressPrefix)) {
 | 
				
			||||||
                command(username, message)
 | 
					                command(username, message)
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                bot.chat(message)
 | 
					                bot.chat(message)
 | 
				
			||||||
@@ -46,12 +45,25 @@ const events = {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    , 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) {
 | 
				
			||||||
 | 
					    // TODO better name, maybe an array?
 | 
				
			||||||
 | 
					    cfg.botAddressPrefix = cfg.botAddressPrefix || "!"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function fuzzyRespond(responses, probability = 1, timeout = 1) {
 | 
					    function fuzzyRespond(responses, probability = 1, timeout = 1) {
 | 
				
			||||||
        if (Math.random() < probability) {
 | 
					        if (Math.random() < probability) {
 | 
				
			||||||
            const response = responses[Math.floor(Math.random() * responses.length)]
 | 
					            const response = responses[Math.floor(Math.random() * responses.length)]
 | 
				
			||||||
@@ -66,7 +78,7 @@ function command(username, message) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (username === bot.username && !message.startsWith("!")) return
 | 
					    if (username === bot.username && !message.startsWith(cfg.botAddressPrefix)) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const player = bot.players[username] ? bot.players[username].entity : null
 | 
					    const player = bot.players[username] ? bot.players[username].entity : null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -78,11 +90,12 @@ function command(username, message) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (message.startsWith("!") || cfg.botAddress.test(message)) {
 | 
					    if (message.startsWith(cfg.botAddressPrefix) || cfg.botAddressRegex.test(message)) {
 | 
				
			||||||
        message = cfg.botAddress.test(message) ? cfg.botAddress.exec(message)[1] : message
 | 
					        message = cfg.botAddressRegex.test(message) ? cfg.botAddressRegex.exec(message)[1] : message
 | 
				
			||||||
 | 
					 | 
				
			||||||
        console.log(message)
 | 
					        console.log(message)
 | 
				
			||||||
        message = message.slice(1) // remove `!`
 | 
					        // remove `!`
 | 
				
			||||||
 | 
					        message = message.startsWith(cfg.botAddressPrefix) ? message.slice(cfg.botAddressPrefix.length) : message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // TODO command dispatchEvent, for aliases
 | 
					        // TODO command dispatchEvent, for aliases
 | 
				
			||||||
        function subcommand(message) {
 | 
					        function subcommand(message) {
 | 
				
			||||||
            const message_parts = message.split(/\s+/)
 | 
					            const message_parts = message.split(/\s+/)
 | 
				
			||||||
@@ -183,104 +196,44 @@ function command(username, message) {
 | 
				
			|||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                case "follow":
 | 
					                case "follow":
 | 
				
			||||||
 | 
					                    switch (message_parts.length) {
 | 
				
			||||||
 | 
					                        case 1:
 | 
				
			||||||
                            subcommand("go follow me")
 | 
					                            subcommand("go follow me")
 | 
				
			||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
                case "come":
 | 
					
 | 
				
			||||||
                    subcommand("go follow once")
 | 
					                        default:
 | 
				
			||||||
 | 
					                            subcommand("go " + message)
 | 
				
			||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case "come":
 | 
				
			||||||
 | 
					                    switch (message_parts[1]) {
 | 
				
			||||||
 | 
					                        case "close":
 | 
				
			||||||
 | 
					                        case "closer":
 | 
				
			||||||
 | 
					                            subcommand("go follow close")
 | 
				
			||||||
 | 
					                            break
 | 
				
			||||||
 | 
					                        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 "move":
 | 
				
			||||||
                case "go":
 | 
					                case "go":
 | 
				
			||||||
                    // TODO move most of the subcommands into mover.js?
 | 
					                    cfg.plugins.mover.command(message_parts.slice(1), player)
 | 
				
			||||||
                    const message_parts2 = message_parts.slice(2)
 | 
					 | 
				
			||||||
                    switch (message_parts[1]) {
 | 
					 | 
				
			||||||
                        case "init":
 | 
					 | 
				
			||||||
                            cfg.plugins.mover.initMoves()
 | 
					 | 
				
			||||||
                            break
 | 
					 | 
				
			||||||
                        case "near":
 | 
					 | 
				
			||||||
                            // message_parts2 = message_parts.slice(2)
 | 
					 | 
				
			||||||
                            switch (message_parts2.length) {
 | 
					 | 
				
			||||||
                                case 0:
 | 
					 | 
				
			||||||
                                    cfg.plugins.mover.moveNear(bot.nearestEntity().position)
 | 
					 | 
				
			||||||
                                    break
 | 
					 | 
				
			||||||
                                case 1:
 | 
					 | 
				
			||||||
                                    switch (message_parts2[0]) {
 | 
					 | 
				
			||||||
                                        case "me":
 | 
					 | 
				
			||||||
                                            if (player) {
 | 
					 | 
				
			||||||
                                                cfg.plugins.mover.moveNear(player.position)
 | 
					 | 
				
			||||||
                                            } else {
 | 
					 | 
				
			||||||
                                                cfg.quiet || bot.chat("can't see you")
 | 
					 | 
				
			||||||
                                            }
 | 
					 | 
				
			||||||
                                            break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                        default:
 | 
					 | 
				
			||||||
                                            const aPlayer = bot.players[message_parts[2]] ? bot.players[message_parts[2]].entity : null
 | 
					 | 
				
			||||||
                                            if (aPlayer) {
 | 
					 | 
				
			||||||
                                                cfg.plugins.mover.moveNear(aPlayer.position)
 | 
					 | 
				
			||||||
                                            } else {
 | 
					 | 
				
			||||||
                                                cfg.quiet || bot.chat(`can't see ${message_parts[2]}`)
 | 
					 | 
				
			||||||
                                            }
 | 
					 | 
				
			||||||
                                            break;
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                    break
 | 
					 | 
				
			||||||
                                case 2:
 | 
					 | 
				
			||||||
                                    todo()
 | 
					 | 
				
			||||||
                                    // bot.lookAt({}) goalxz?
 | 
					 | 
				
			||||||
                                    break
 | 
					 | 
				
			||||||
                                case 3:
 | 
					 | 
				
			||||||
                                    //TODO more checks
 | 
					 | 
				
			||||||
                                    cfg.plugins.mover.moveNear(message_parts2)
 | 
					 | 
				
			||||||
                                    break
 | 
					 | 
				
			||||||
                                default:
 | 
					 | 
				
			||||||
                                    break
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            break
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        case "follow":
 | 
					 | 
				
			||||||
                            // message_parts2 = message_parts.slice(2)
 | 
					 | 
				
			||||||
                            switch (message_parts2.length) {
 | 
					 | 
				
			||||||
                                case 0:
 | 
					 | 
				
			||||||
                                    cfg.plugins.mover.follow(bot.nearestEntity())
 | 
					 | 
				
			||||||
                                    break
 | 
					 | 
				
			||||||
                                case 1:
 | 
					 | 
				
			||||||
                                    switch (message_parts2[0]) {
 | 
					 | 
				
			||||||
                                        case "me":
 | 
					 | 
				
			||||||
                                        case "once":
 | 
					 | 
				
			||||||
                                            if (player) {
 | 
					 | 
				
			||||||
                                                cfg.plugins.mover.follow(player, message_parts2[0] !== "once")
 | 
					 | 
				
			||||||
                                            } else {
 | 
					 | 
				
			||||||
                                                cfg.quiet || bot.chat("can't see you")
 | 
					 | 
				
			||||||
                                            }
 | 
					 | 
				
			||||||
                                            break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                        default:
 | 
					 | 
				
			||||||
                                            const aPlayer = bot.players[message_parts[2]] ? bot.players[message_parts[2]].entity : null
 | 
					 | 
				
			||||||
                                            if (aPlayer) {
 | 
					 | 
				
			||||||
                                                cfg.plugins.mover.follow(aPlayer)
 | 
					 | 
				
			||||||
                                            } else {
 | 
					 | 
				
			||||||
                                                cfg.quiet || bot.chat(`can't see ${message_parts[2]}`)
 | 
					 | 
				
			||||||
                                            }
 | 
					 | 
				
			||||||
                                            break;
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                    break
 | 
					 | 
				
			||||||
                                // case 2:
 | 
					 | 
				
			||||||
                                // bot.lookAt({}) goalxz?
 | 
					 | 
				
			||||||
                                // break
 | 
					 | 
				
			||||||
                                // case 3:
 | 
					 | 
				
			||||||
                                //TODO more checks
 | 
					 | 
				
			||||||
                                // cfg.plugins.mover.moveNear(message_parts2)
 | 
					 | 
				
			||||||
                                // break
 | 
					 | 
				
			||||||
                                default:
 | 
					 | 
				
			||||||
                                    todo()
 | 
					 | 
				
			||||||
                                    break
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            break
 | 
					 | 
				
			||||||
                        case "stop":
 | 
					 | 
				
			||||||
                            cfg.plugins.mover.stop()
 | 
					 | 
				
			||||||
                            break
 | 
					 | 
				
			||||||
                        default:
 | 
					 | 
				
			||||||
                            return todo()
 | 
					 | 
				
			||||||
                            break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                case "attack":
 | 
					                case "attack":
 | 
				
			||||||
@@ -331,6 +284,8 @@ function command(username, message) {
 | 
				
			|||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    break
 | 
					                    break
 | 
				
			||||||
 | 
					                // TODO move look (and maybe find) to informer plugin?
 | 
				
			||||||
 | 
					                case "look":
 | 
				
			||||||
                case "lookat":
 | 
					                case "lookat":
 | 
				
			||||||
                    // const coords = v(message_parts.splice(1))
 | 
					                    // const coords = v(message_parts.splice(1))
 | 
				
			||||||
                    switch (message_parts.length) {
 | 
					                    switch (message_parts.length) {
 | 
				
			||||||
@@ -341,7 +296,7 @@ function command(username, message) {
 | 
				
			|||||||
                            switch (message_parts[1]) {
 | 
					                            switch (message_parts[1]) {
 | 
				
			||||||
                                case "me":
 | 
					                                case "me":
 | 
				
			||||||
                                    if (player) {
 | 
					                                    if (player) {
 | 
				
			||||||
                                        bot.lookAt((new v.Vec3(0, 1, 0)).add(player.position))
 | 
					                                        bot.lookAt(player.position.offset(0, 1, 0))
 | 
				
			||||||
                                    } else {
 | 
					                                    } else {
 | 
				
			||||||
                                        cfg.quiet || bot.chat("can't see you")
 | 
					                                        cfg.quiet || bot.chat("can't see you")
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
@@ -349,17 +304,16 @@ function command(username, message) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                                case "this":
 | 
					                                case "this":
 | 
				
			||||||
                                    // TODO lookat the block the user is looking at
 | 
					                                    // TODO lookat the block the user is looking at
 | 
				
			||||||
 | 
					                                    // Currently looks player position
 | 
				
			||||||
                                    if (player) {
 | 
					                                    if (player) {
 | 
				
			||||||
                                        bot.lookAt((new v.Vec3(0, 1, 0)).add(player.position))
 | 
					                                        bot.lookAt(player.position)
 | 
				
			||||||
                                        todo()
 | 
					 | 
				
			||||||
                                    } else {
 | 
					                                    } else {
 | 
				
			||||||
                                        cfg.quiet || bot.chat("can't see you")
 | 
					                                        cfg.quiet || bot.chat("can't see you")
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                    break;
 | 
					 | 
				
			||||||
                                    break
 | 
					                                    break
 | 
				
			||||||
                                default:
 | 
					                                default:
 | 
				
			||||||
                                    const aPlayer = bot.players[message_parts[2]] ? bot.players[message_parts[2]].entity : null
 | 
					                                    const aPlayer = bot.players[message_parts[2]] ? bot.players[message_parts[2]].entity : null
 | 
				
			||||||
                                    if (aPlayer) bot.lookAt((new v.Vec3(0, 1, 0)).add(aPlayer.position))
 | 
					                                    if (aPlayer) bot.lookAt(aPlayer.position.offset(0, 1, 0))
 | 
				
			||||||
                                    break;
 | 
					                                    break;
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            break
 | 
					                            break
 | 
				
			||||||
@@ -375,17 +329,8 @@ function command(username, message) {
 | 
				
			|||||||
                            break
 | 
					                            break
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    break
 | 
					                    break
 | 
				
			||||||
                case "ride":
 | 
					                case "info":
 | 
				
			||||||
                case "mount":
 | 
					                    cfg.plugins.informer.command(message_parts.splice(1), player)
 | 
				
			||||||
                    bot.mount(bot.nearestEntity())
 | 
					 | 
				
			||||||
                    break
 | 
					 | 
				
			||||||
                case "getoff":
 | 
					 | 
				
			||||||
                case "unmount":
 | 
					 | 
				
			||||||
                case "dismount":
 | 
					 | 
				
			||||||
                    bot.dismount()
 | 
					 | 
				
			||||||
                    break
 | 
					 | 
				
			||||||
                case "go":
 | 
					 | 
				
			||||||
                    bot.moveVehicle(0, 10)
 | 
					 | 
				
			||||||
                    break
 | 
					                    break
 | 
				
			||||||
                // case "use":
 | 
					                // case "use":
 | 
				
			||||||
                //     bot.useOn(bot.nearestEntity())
 | 
					                //     bot.useOn(bot.nearestEntity())
 | 
				
			||||||
@@ -416,29 +361,62 @@ function command(username, message) {
 | 
				
			|||||||
                // case "take":
 | 
					                // case "take":
 | 
				
			||||||
                //     // TODO take only what's requested, then throw all the rest
 | 
					                //     // TODO take only what's requested, then throw all the rest
 | 
				
			||||||
                //     // TODO take all
 | 
					                //     // TODO take all
 | 
				
			||||||
                // case "toss":
 | 
					
 | 
				
			||||||
                // case "drop":
 | 
					                // TODO move subcommands to cfg.plugins.inventory.itemByName
 | 
				
			||||||
                //     if (!message_parts[1]) { return false } // FIXME, works but ugly
 | 
					                case "toss":
 | 
				
			||||||
                //     if (!checkItemExists(message_parts[1])) { return false }
 | 
					                case "drop":
 | 
				
			||||||
                //     switch (message_parts.length) {
 | 
					                    if (!message_parts[1]) { return false } // FIXME, works but ugly
 | 
				
			||||||
                //         case 2:
 | 
					                    if (!mcData.findItemOrBlockByName(message_parts[1])) {
 | 
				
			||||||
                //             bot.toss(mcData.blocksByName[message_parts[1]].id)
 | 
					                        console.log("doesn't exist:", message_parts[1])
 | 
				
			||||||
                //             break
 | 
					                        cfg.quiet || bot.chat(`item doesn't exist: ${message_parts[1]}`)
 | 
				
			||||||
                //         case 3:
 | 
					                        return false
 | 
				
			||||||
                //             bot.tossStack(
 | 
					                    }
 | 
				
			||||||
                //                 mcData.itemsByName[message_parts[1]].id,
 | 
					                    const item = cfg.plugins.inventory.itemByName(message_parts[1])
 | 
				
			||||||
                //                 (err) => {
 | 
					                    if (!item) {
 | 
				
			||||||
                //                     if (err) {
 | 
					                        console.log("don't have:", message_parts[1])
 | 
				
			||||||
                //                         console.log(err)
 | 
					                        cfg.quiet || bot.chat(`don't have item: ${message_parts[1]}`)
 | 
				
			||||||
                //                         bot.chat(err)
 | 
					                        return false
 | 
				
			||||||
                //                     }
 | 
					                    }
 | 
				
			||||||
                //                 }
 | 
					                    switch (message_parts.length) {
 | 
				
			||||||
                //             )
 | 
					                        case 2:
 | 
				
			||||||
                //             break
 | 
					                            bot.tossStack(
 | 
				
			||||||
                //         default:
 | 
					                                item,
 | 
				
			||||||
                //             break
 | 
					                                (err) => {
 | 
				
			||||||
                //     }
 | 
					                                    if (err) {
 | 
				
			||||||
                //     break;
 | 
					                                        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 "sm":
 | 
				
			||||||
 | 
					                case "step":
 | 
				
			||||||
 | 
					                    cfg.plugins.statemachine?.command?.(
 | 
				
			||||||
 | 
					                        message_parts[0] == "sm" ? message_parts.slice(1) : message_parts, player
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                        // TODO refactor into plugin detection and command exec function
 | 
				
			||||||
 | 
					                        // safecommand(plugin_name, message_parts, player)
 | 
				
			||||||
 | 
					                        // message_parts includes command?
 | 
				
			||||||
 | 
					                        || bot.chat("statemachine plugin not loaded")
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
                case "location":
 | 
					                case "location":
 | 
				
			||||||
                    // TODO put in /lib/location
 | 
					                    // TODO put in /lib/location
 | 
				
			||||||
                    switch (message_parts[1]) {
 | 
					                    switch (message_parts[1]) {
 | 
				
			||||||
@@ -496,7 +474,7 @@ function command(username, message) {
 | 
				
			|||||||
            case "howdy":
 | 
					            case "howdy":
 | 
				
			||||||
            case "heyo":
 | 
					            case "heyo":
 | 
				
			||||||
            case "yo":
 | 
					            case "yo":
 | 
				
			||||||
                if (player) bot.lookAt((new v.Vec3(0, 1, 0)).add(player.position))
 | 
					                if (player) bot.lookAt(player.position.offset(0, 1, 0))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // TODO sneak
 | 
					                // TODO sneak
 | 
				
			||||||
                // function swingArm() {
 | 
					                // function swingArm() {
 | 
				
			||||||
@@ -537,13 +515,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 = () => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,11 +2,7 @@ let cfg = {}
 | 
				
			|||||||
let bot = {}
 | 
					let bot = {}
 | 
				
			||||||
let isEating = false
 | 
					let isEating = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function callbackHandle(err) {
 | 
					function eat(callback = e => e && console.error(e)) {
 | 
				
			||||||
    if (err) console.error(err)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function eat(callback) {
 | 
					 | 
				
			||||||
    isEating = true
 | 
					    isEating = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const foodNames = require('minecraft-data')(bot.version).foodsArray.map((item) => item.name)
 | 
					    const foodNames = require('minecraft-data')(bot.version).foodsArray.map((item) => item.name)
 | 
				
			||||||
@@ -37,23 +33,23 @@ function eat(callback) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    bot.equip(best_food, 'hand', function (error) {
 | 
					    bot.equip(best_food, 'hand', function (error) {
 | 
				
			||||||
        if (error) {
 | 
					        if (error) {
 | 
				
			||||||
            console.error(error)
 | 
					            console.warn(error, best_food)
 | 
				
			||||||
            isEating = false
 | 
					            isEating = false
 | 
				
			||||||
            bot.emit('eat_stop')
 | 
					            bot.emit('eat_stop')
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            bot.consume(function (err) {
 | 
					            try {
 | 
				
			||||||
                if (err) {
 | 
					                bot.consume().catch(error => {
 | 
				
			||||||
                    console.error(err)
 | 
					                    if (error.message === "Food is full") {
 | 
				
			||||||
                    isEating = false
 | 
					                        console.warn(error, best_food)
 | 
				
			||||||
                    bot.emit('eat_stop')
 | 
					 | 
				
			||||||
                    return callback(err)
 | 
					 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                    isEating = false
 | 
					                        return callback({ error, best_food })
 | 
				
			||||||
                    bot.emit('eat_stop')
 | 
					 | 
				
			||||||
                    callback(null)
 | 
					 | 
				
			||||||
                    if (!bot.food === 20) eat(callbackHandle)
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                }).finally(() => {
 | 
				
			||||||
 | 
					                    isEating = false
 | 
				
			||||||
 | 
					                    bot.emit('eat_stop')
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
 | 
					            } catch { }
 | 
				
			||||||
 | 
					            if (bot.food !== 20) eat(callback)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -76,7 +72,7 @@ function checkFood() {
 | 
				
			|||||||
                // TODO implement better idle state
 | 
					                // TODO implement better idle state
 | 
				
			||||||
            ) || true // idle most likely
 | 
					            ) || true // idle most likely
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            eat(callbackHandle)
 | 
					            eat()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -71,9 +71,7 @@ function lookForMobs() {
 | 
				
			|||||||
        bot.pvp.attack(entityEnemy)
 | 
					        bot.pvp.attack(entityEnemy)
 | 
				
			||||||
    } else if (entityEnemy) {
 | 
					    } else if (entityEnemy) {
 | 
				
			||||||
        bot.lookAt(
 | 
					        bot.lookAt(
 | 
				
			||||||
            // (new v.Vec3(0, 1, 0)).add(
 | 
					 | 
				
			||||||
            entityEnemy.position
 | 
					            entityEnemy.position
 | 
				
			||||||
            // )
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        cfg.quiet || bot.chat("AH! A creeper! They creep me out!")
 | 
					        cfg.quiet || bot.chat("AH! A creeper! They creep me out!")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										147
									
								
								lib/plugins/informer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								lib/plugins/informer.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,147 @@
 | 
				
			|||||||
 | 
					let cfg
 | 
				
			||||||
 | 
					let bot
 | 
				
			||||||
 | 
					let mcData
 | 
				
			||||||
 | 
					// import v from 'vec3'
 | 
				
			||||||
 | 
					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 = typeof name === "string" ? bot.nearestEntity((entity) => {
 | 
				
			||||||
 | 
					        const ename = entity.name || entity.username
 | 
				
			||||||
 | 
					        return name && ename ? ename == name : true
 | 
				
			||||||
 | 
					    }) : entity
 | 
				
			||||||
 | 
					    console.log(entity)
 | 
				
			||||||
 | 
					    if (!entity) {
 | 
				
			||||||
 | 
					        cfg.quiet || bot.chat("no entity")
 | 
				
			||||||
 | 
					        return entity
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let info = [entity.type, entity.username || entity.name]
 | 
				
			||||||
 | 
					    // TODO various info depending on the type of entity; player, villager, etc
 | 
				
			||||||
 | 
					    if (entity.metadata) info.push("len: " + entity.metadata.length)
 | 
				
			||||||
 | 
					    cfg.quiet || bot.chat(info.join("; "))
 | 
				
			||||||
 | 
					    return entity
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function command(message_parts, player) {
 | 
				
			||||||
 | 
					    if (message_parts.length > 0) {
 | 
				
			||||||
 | 
					        cfg.info.recentCommand = message_parts
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    switch (message_parts.length) {
 | 
				
			||||||
 | 
					        case 0:
 | 
				
			||||||
 | 
					            if (cfg.info.recentCommand) {
 | 
				
			||||||
 | 
					                command(cfg.info.recentCommand, player)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // TODO dispatch on instance of entity, block, etc..
 | 
				
			||||||
 | 
					                // TODO have the logic inside the function or with a utility function
 | 
				
			||||||
 | 
					                block(player.position || player?.entity.position || null)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 1:
 | 
				
			||||||
 | 
					            switch (message_parts[0]) {
 | 
				
			||||||
 | 
					                case "i":
 | 
				
			||||||
 | 
					                case "item":
 | 
				
			||||||
 | 
					                    item()
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                case "e":
 | 
				
			||||||
 | 
					                case "entity":
 | 
				
			||||||
 | 
					                    entity()
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                case "b":
 | 
				
			||||||
 | 
					                case "block":
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    block()
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case 2:
 | 
				
			||||||
 | 
					            switch (message_parts[0]) {
 | 
				
			||||||
 | 
					                case "i":
 | 
				
			||||||
 | 
					                case "item":
 | 
				
			||||||
 | 
					                    item(message_parts[1])
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                case "e":
 | 
				
			||||||
 | 
					                case "entity":
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    entity(message_parts[1])
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case 4:
 | 
				
			||||||
 | 
					            switch (message_parts[0]) {
 | 
				
			||||||
 | 
					                case "b":
 | 
				
			||||||
 | 
					                case "block":
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    block(message_parts.slice(1))
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            cfg.quiet || bot.chat("info: unknown command")
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const load = (config) => {
 | 
				
			||||||
 | 
					    cfg = config
 | 
				
			||||||
 | 
					    bot = cfg.bot
 | 
				
			||||||
 | 
					    cfg.info = {
 | 
				
			||||||
 | 
					        quiet: cfg.quiet,
 | 
				
			||||||
 | 
					        recentCommand: null,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    mcData = bot.mcData || (bot.mcData = require('minecraft-data')(bot.version))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const unload = () => {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = { load, unload, command, block, item, entity }
 | 
				
			||||||
@@ -225,4 +225,4 @@ const unload = () => {
 | 
				
			|||||||
    bot.off('chat', inventory)
 | 
					    bot.off('chat', inventory)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = { load, unload, equipItem, craftItem }
 | 
					module.exports = { load, unload, equipItem, craftItem, itemByName }
 | 
				
			||||||
@@ -1,49 +1,86 @@
 | 
				
			|||||||
// import { EntityFilters } from "mineflayer-statemachine"
 | 
					 | 
				
			||||||
// import v from "vec3"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// import { Movements } from "mineflayer-pathfinder"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// const mineflayer = require('mineflayer')
 | 
					 | 
				
			||||||
const { Movements } = require('mineflayer-pathfinder')
 | 
					const { Movements } = require('mineflayer-pathfinder')
 | 
				
			||||||
// const { GoalBLah } = require('mineflayer-pathfinder').goals
 | 
					 | 
				
			||||||
const v = require('vec3')
 | 
					const v = require('vec3')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let cfg = {}
 | 
					let cfg = {}
 | 
				
			||||||
let bot = {}
 | 
					let bot = {}
 | 
				
			||||||
// let moving
 | 
					// let moving
 | 
				
			||||||
let pathfinder
 | 
					let pathfinder
 | 
				
			||||||
 | 
					let mcData
 | 
				
			||||||
let movements = []
 | 
					let movements = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function initMoves(bot = bot, mcData = require('minecraft-data')(bot.version)) {
 | 
					function initMoves(bot = bot, mcData = bot.mcData) {
 | 
				
			||||||
    let defaultMove = new Movements(bot, mcData)
 | 
					    if (movements.length > 0) {
 | 
				
			||||||
    defaultMove.canDig = false
 | 
					        bot.pathfinder.setMovements(movements.defaultMove)
 | 
				
			||||||
    defaultMove.scafoldingBlocks.push(mcData.blocksByName.slime_block.id)
 | 
					        return console.warn("go init: movements already initialized!", movements)
 | 
				
			||||||
    // defaultMove.blocksCantBreak.add(mcData.blocksByName.glass.id)
 | 
					    }
 | 
				
			||||||
    // defaultMove.blocksToAvoid.add(mcData.blocksByName.magma.id)
 | 
					    const normalMove = new Movements(bot, mcData)
 | 
				
			||||||
    movements.defaultMove = defaultMove
 | 
					    normalMove.canDig = false
 | 
				
			||||||
 | 
					    normalMove.scafoldingBlocks.push(mcData.blocksByName.slime_block.id)
 | 
				
			||||||
 | 
					    normalMove.blocksCantBreak.add(mcData.blocksByName.glass.id)
 | 
				
			||||||
 | 
					    normalMove.blocksToAvoid.add(mcData.blocksByName.magma_block.id)
 | 
				
			||||||
 | 
					    movements.push(normalMove)
 | 
				
			||||||
 | 
					    movements.defaultMove = movements[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bot.pathfinder.setMovements(defaultMove)
 | 
					    const aggresiveMove = new Movements(bot, mcData)
 | 
				
			||||||
 | 
					    //Object.create or assign?
 | 
				
			||||||
 | 
					    Object.assign(aggresiveMove, normalMove)
 | 
				
			||||||
 | 
					    aggresiveMove.canDig = true
 | 
				
			||||||
 | 
					    movements.push(aggresiveMove)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const safeMove = new Movements(bot, mcData)
 | 
				
			||||||
 | 
					    Object.assign(safeMove, normalMove)
 | 
				
			||||||
 | 
					    safeMove.allowParkour = false
 | 
				
			||||||
 | 
					    safeMove.canDig = false
 | 
				
			||||||
 | 
					    movements.push(safeMove)
 | 
				
			||||||
 | 
					    // console.info("go init: moves:", movements)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bot.pathfinder.setMovements(normalMove)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function moveNear(pos, distance = 3) {
 | 
					function moveNear(pos, distance = 3) {
 | 
				
			||||||
    const { GoalNear } = require('mineflayer-pathfinder').goals
 | 
					    const { GoalNear } = require('mineflayer-pathfinder').goals
 | 
				
			||||||
    cfg.quiet || bot.chat(`moving to ${pos}`)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pos = v(pos)
 | 
					    pos = v(pos)
 | 
				
			||||||
 | 
					    cfg.quiet || bot.chat(`moving to ${pos.floored()}`)
 | 
				
			||||||
    bot.pathfinder.setMovements(movements.defaultMove)
 | 
					    bot.pathfinder.setMovements(movements.defaultMove)
 | 
				
			||||||
    bot.pathfinder.setGoal(new GoalNear(pos.x, pos.y, pos.z, distance))
 | 
					    bot.pathfinder.setGoal(new GoalNear(pos.x, pos.y, pos.z, distance))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function follow(entity, dynamic = true) {
 | 
					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)
 | 
					    console.assert(entity)
 | 
				
			||||||
    const { GoalFollow } = require('mineflayer-pathfinder').goals
 | 
					    const { GoalFollow } = require('mineflayer-pathfinder').goals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // console.log(entity)
 | 
				
			||||||
 | 
					    cfg.quiet || bot.chat(
 | 
				
			||||||
    cfg.quiet && console.log(entity)
 | 
					 | 
				
			||||||
        || bot.chat(
 | 
					 | 
				
			||||||
        `following ${entity.type
 | 
					        `following ${entity.type
 | 
				
			||||||
        }: ${entity.username || entity.displayName
 | 
					        }: ${entity.username || entity.displayName
 | 
				
			||||||
        }${dynamic ? "" : " once"}`
 | 
					        }${dynamic ? "" : " once"}`
 | 
				
			||||||
@@ -54,18 +91,265 @@ function follow(entity, dynamic = true) {
 | 
				
			|||||||
    // console.log(entity)
 | 
					    // console.log(entity)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bot.pathfinder.setMovements(movements.defaultMove)
 | 
					    bot.pathfinder.setMovements(movements.defaultMove)
 | 
				
			||||||
    bot.pathfinder.setGoal(new GoalFollow(entity, 3), dynamic)
 | 
					    bot.pathfinder.setGoal(new GoalFollow(entity, distance), dynamic)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function away(entity = bot.nearestEntity(), invertInvert = true, dynamic = true, distance = 10) {
 | 
				
			||||||
 | 
					    const currentGoal = bot.pathfinder.goal
 | 
				
			||||||
 | 
					    console.assert(currentGoal || entity)
 | 
				
			||||||
 | 
					    const { GoalInvert } = require('mineflayer-pathfinder').goals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bot.pathfinder.setMovements(movements.defaultMove)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!currentGoal) {
 | 
				
			||||||
 | 
					        const { GoalFollow } = require('mineflayer-pathfinder').goals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (entity.entity) {
 | 
				
			||||||
 | 
					            console.log("go away entity:", entity, entity.entity)
 | 
				
			||||||
 | 
					            entity = entity.entity
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cfg.quiet || bot.chat(
 | 
				
			||||||
 | 
					            `going away from ${entity?.type
 | 
				
			||||||
 | 
					            }: ${entity?.username || entity?.displayName
 | 
				
			||||||
 | 
					            }${dynamic ? "" : " once"}`
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        // alternative implementation
 | 
				
			||||||
 | 
					        // follow(entity, dynamic, distance)
 | 
				
			||||||
 | 
					        // bot.pathfinder.setGoal(new GoalInvert(bot.pathfinder.goal), dynamic)
 | 
				
			||||||
 | 
					        return bot.pathfinder.setGoal(new GoalInvert(
 | 
				
			||||||
 | 
					            new GoalFollow(entity, distance)
 | 
				
			||||||
 | 
					        ), dynamic)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (currentGoal instanceof GoalInvert) {
 | 
				
			||||||
 | 
					        const currEntity = currentGoal.goal.entity
 | 
				
			||||||
 | 
					        console.log("go away inverse goal:", currentGoal.goal)
 | 
				
			||||||
 | 
					        if (invertInvert) {
 | 
				
			||||||
 | 
					            cfg.quiet || bot.chat(
 | 
				
			||||||
 | 
					                `switching towards ${currentGoal.goal?.constructor.name
 | 
				
			||||||
 | 
					                }: ${currEntity?.type
 | 
				
			||||||
 | 
					                }: ${currEntity?.username || currEntity?.displayName
 | 
				
			||||||
 | 
					                }${dynamic ? "" : " once"}`
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            bot.pathfinder.setGoal(currentGoal.goal, dynamic)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            cfg.quiet || bot.chat(
 | 
				
			||||||
 | 
					                `already going away from ${currentGoal.goal?.constructor.name
 | 
				
			||||||
 | 
					                }; not switching`
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        const currEntity = currentGoal.entity
 | 
				
			||||||
 | 
					        console.log("go away goal:", currentGoal)
 | 
				
			||||||
 | 
					        cfg.quiet || bot.chat(
 | 
				
			||||||
 | 
					            `going away from ${currentGoal?.constructor.name
 | 
				
			||||||
 | 
					            }: ${currEntity?.type
 | 
				
			||||||
 | 
					            }: ${currEntity?.username || currEntity?.displayName
 | 
				
			||||||
 | 
					            }${dynamic ? "" : " once"}`
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        bot.pathfinder.setGoal(new GoalInvert(currentGoal), dynamic)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function ride(entity) {
 | 
				
			||||||
 | 
					    entity = entity?.entity || entity
 | 
				
			||||||
 | 
					    const ridableMobs = ["Horse", "Donkey", "Pig", "Strider", "Mule"]
 | 
				
			||||||
 | 
					    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) {
 | 
				
			||||||
 | 
					        // FIXME moveVehicle should be +-1 or 0?
 | 
				
			||||||
 | 
					        const amount = parseInt(message_parts2[0]) * -reverse
 | 
				
			||||||
 | 
					        bot.moveVehicle(turn && Math.sign(amount) || 0, !turn && amount || 0)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        command([directionLabel].concat(message_parts2))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function hit(blockOrEntity) {
 | 
					function hit(blockOrEntity) {
 | 
				
			||||||
    bot.chat(`hitting ${entity.name || entity.type}`)
 | 
					    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() {
 | 
					function stop() {
 | 
				
			||||||
    bot.pathfinder.setGoal(null)
 | 
					    bot.pathfinder.setGoal(null)
 | 
				
			||||||
    bot.stopDigging()
 | 
					    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 "away":
 | 
				
			||||||
 | 
					        case "run":
 | 
				
			||||||
 | 
					        case "runaway":
 | 
				
			||||||
 | 
					            away()
 | 
				
			||||||
 | 
					            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 "up":
 | 
				
			||||||
 | 
					        case "u":
 | 
				
			||||||
 | 
					        case "j":
 | 
				
			||||||
 | 
					            moveOrRide(1, 1, "jump", 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) => {
 | 
					const load = (config) => {
 | 
				
			||||||
    cfg = config
 | 
					    cfg = config
 | 
				
			||||||
    bot = cfg.bot
 | 
					    bot = cfg.bot
 | 
				
			||||||
@@ -77,19 +361,23 @@ const load = (config) => {
 | 
				
			|||||||
        movements: []
 | 
					        movements: []
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mcData = bot.mcData || (bot.mcData = require('minecraft-data')(bot.version))
 | 
				
			||||||
    pathfinder = bot.pathfinder || bot.loadPlugin(require('mineflayer-pathfinder').pathfinder)
 | 
					    pathfinder = bot.pathfinder || bot.loadPlugin(require('mineflayer-pathfinder').pathfinder)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // initMoves(bot, mcData)
 | 
					    // initMoves(bot, mcData)
 | 
				
			||||||
    setTimeout(initMoves, 500, bot)
 | 
					    setTimeout(initMoves, 500, bot, mcData)
 | 
				
			||||||
 | 
					    bot.on('goal_reached', goalReached)
 | 
				
			||||||
    // bot.loadPlugin(pathfinder)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // bot.on('time', hello)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const unload = () => {
 | 
					const unload = () => {
 | 
				
			||||||
    // TODO stop pathfinding maybe?
 | 
					    stop()
 | 
				
			||||||
 | 
					    bot.off('goal_reached', goalReached)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = { load, unload, stop, initMoves, moveNear, follow }
 | 
					module.exports = {
 | 
				
			||||||
 | 
					    load, unload, command,
 | 
				
			||||||
 | 
					    stop, initMoves,
 | 
				
			||||||
 | 
					    moveNear, moveXZ, moveY, follow,
 | 
				
			||||||
 | 
					    ride
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -7,49 +7,78 @@ 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)
 | 
				
			||||||
        bot.waitForChunksToLoad(() => {
 | 
					        bot.waitForChunksToLoad(() => {
 | 
				
			||||||
            cfg.plugins.moveNear(bed.position)
 | 
					            cfg.plugins.mover && cfg.plugins.mover.moveNear(bed.position, 2)
 | 
				
			||||||
 | 
					            bot.once('goal_reached', (goal) => {
 | 
				
			||||||
 | 
					                console.info(goal)
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
                    bot.sleep(bed, (err) => {
 | 
					                    bot.sleep(bed, (err) => {
 | 
				
			||||||
                        if (err) {
 | 
					                        if (err) {
 | 
				
			||||||
                            !quiet && bot.chat(`can't sleep: ${err.message}`)
 | 
					                            !quiet && bot.chat(`can't sleep: ${err.message}`)
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            !quiet && bot.chat("zzz")
 | 
					                            !quiet && bot.chat("zzz")
 | 
				
			||||||
 | 
					                            // apparently, `bot.isSleeping = true` takes a while
 | 
				
			||||||
 | 
					                            // maybe it's async
 | 
				
			||||||
                            console.log("sleeping? ", bot.isSleeping)
 | 
					                            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 (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 {
 | 
				
			||||||
        // TODO: use mover
 | 
					        // TODO: use mover
 | 
				
			||||||
        // bot.gameplay.solveFor(
 | 
					        // bot.gameplay.solveFor(
 | 
				
			||||||
        //     new ObtainItem("bed"), (err) => {
 | 
					        //     new ObtainItem("bed"), (err) => {
 | 
				
			||||||
        //         if (err) {
 | 
					        //         if (err) {
 | 
				
			||||||
        //             !quiet && bot.chat(`need a${bedstatus} bed: may not see if just placed`)
 | 
					        !quiet && bot.chat(`need a${bed_occupied ? "n unoccupied" : ""} bed: may not see if just placed`)
 | 
				
			||||||
        //         }
 | 
					        //         }
 | 
				
			||||||
        //     }
 | 
					        //     }
 | 
				
			||||||
        // )
 | 
					        // )
 | 
				
			||||||
@@ -72,7 +101,7 @@ 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)
 | 
					        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)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,115 +1,479 @@
 | 
				
			|||||||
// Load your dependency plugins.
 | 
					// import { createMachine, interpret, InterpreterStatus } from "xstate";
 | 
				
			||||||
 | 
					const { createMachine, interpret, InterpreterStatus } = require('xstate');
 | 
				
			||||||
const {pathfinder} = require('mineflayer-pathfinder')
 | 
					// import { access, mkdir, writeFile, readFile } from "fs";
 | 
				
			||||||
// bot.loadPlugin(require('prismarine-viewer').mineflayer)
 | 
					const { access, mkdir, writeFile, readFile } = require('fs');
 | 
				
			||||||
// const mineflayerViewer = require('prismarine-viewer').mineflayer
 | 
					// ANGRAM_PREFIX='MINECRAFT'
 | 
				
			||||||
 | 
					const { MINECRAFT_DATA_FOLDER } = process.env || require("dotenv-packed").parseEnv().parsed;
 | 
				
			||||||
// Import required behaviors.
 | 
					const storage_dir = MINECRAFT_DATA_FOLDER || './data/' + "/sm/";
 | 
				
			||||||
const {
 | 
					// import { createBot } from "mineflayer"
 | 
				
			||||||
    StateTransition,
 | 
					// let { pathfinder, Movements, goals } = require('mineflayer-pathfinder')
 | 
				
			||||||
    BotStateMachine,
 | 
					// let cfg
 | 
				
			||||||
    EntityFilters,
 | 
					// let bot = createBot({ username: 'statebot' })
 | 
				
			||||||
    BehaviorFollowEntity,
 | 
					let bot;
 | 
				
			||||||
    BehaviorLookAtEntity,
 | 
					let quiet;
 | 
				
			||||||
    BehaviorGetClosestEntity,
 | 
					let updateRate = 20;
 | 
				
			||||||
    NestedStateMachine,
 | 
					const machines = {
 | 
				
			||||||
    BehaviorIdle,
 | 
					    list: {},
 | 
				
			||||||
    StateMachineWebserver,
 | 
					    running: {},
 | 
				
			||||||
} = require("mineflayer-statemachine");
 | 
					};
 | 
				
			||||||
 | 
					let webserver;
 | 
				
			||||||
// TODO chat
 | 
					let cfg = {
 | 
				
			||||||
 | 
					    statemachine: {
 | 
				
			||||||
 | 
					        webserver: null,
 | 
				
			||||||
 | 
					        // quiet: true,
 | 
				
			||||||
// wait for our bot to login.
 | 
					        quiet: quiet,
 | 
				
			||||||
function statemachineInit() {
 | 
					        list: {},
 | 
				
			||||||
    cfg.botAddress = new RegExp(`^${bot.username} (!.+)`)
 | 
					        running: {},
 | 
				
			||||||
    // This targets object is used to pass data between different states. It can be left empty.
 | 
					        draft: null,
 | 
				
			||||||
    const targets = new Object();
 | 
					        recent: null,
 | 
				
			||||||
 | 
					        // debug: null,
 | 
				
			||||||
    // Create our states
 | 
					        debug: true,
 | 
				
			||||||
    const getClosestPlayer = new BehaviorGetClosestEntity(
 | 
					        updateRate: updateRate
 | 
				
			||||||
        bot, targets, entity => EntityFilters().PlayersOnly(entity) && bot.entity.position.distanceTo(entity.position) <= 50 ); // && a.username !== bot.username);
 | 
					    }
 | 
				
			||||||
    const followPlayer = new BehaviorFollowEntity(bot, targets);
 | 
					    // FIXME temp variables to satisfy typescript autocomplete
 | 
				
			||||||
    const lookAtPlayer = new BehaviorLookAtEntity(bot, targets);
 | 
					    // , quiet: null
 | 
				
			||||||
    const stay = new BehaviorIdle();
 | 
					    ,
 | 
				
			||||||
 | 
					    bot: bot,
 | 
				
			||||||
    // Create our transitions
 | 
					    plugins: { statemachine: null }
 | 
				
			||||||
    const transitions = [
 | 
					};
 | 
				
			||||||
 | 
					// Edit your machine(s) here
 | 
				
			||||||
        // We want to start following the player immediately after finding them.
 | 
					function init(smName = "dummy", webserver) {
 | 
				
			||||||
        // Since getClosestPlayer finishes instantly, shouldTransition() should always return true.
 | 
					    access(storage_dir, err => {
 | 
				
			||||||
        new StateTransition({
 | 
					        if (err?.code === 'ENOENT') {
 | 
				
			||||||
            parent: getClosestPlayer,
 | 
					            mkdir(storage_dir, e => e && console.warn("sm init: create dir", e));
 | 
				
			||||||
            child: followPlayer,
 | 
					        }
 | 
				
			||||||
            onTransition: (quiet) => quiet || bot.chat(`Hi ${targets.entity.username}!`),
 | 
					        else if (err) {
 | 
				
			||||||
            shouldTransition: () => bot.entity.position.distanceTo(targets.entity.position) <= 50,
 | 
					            console.warn("sm init: create dir", err);
 | 
				
			||||||
            // shouldTransition: () => getClosestPlayer.distanceToTarget() < 100 || console.info("player too far!") && false,
 | 
					        }
 | 
				
			||||||
        }),
 | 
					    });
 | 
				
			||||||
 | 
					    // const machine = newSM(smName)
 | 
				
			||||||
        // If the distance to the player is less than two blocks, switch from the followPlayer
 | 
					    // machine.states.idle.on.TOGGLE = "start"
 | 
				
			||||||
        // state to the lookAtPlayer state.
 | 
					    // machine.states.start.on.TOGGLE = "idle"
 | 
				
			||||||
        new StateTransition({
 | 
					    const machine = createMachine({
 | 
				
			||||||
            parent: followPlayer,
 | 
					        id: smName,
 | 
				
			||||||
            child: lookAtPlayer,
 | 
					        initial: "idle",
 | 
				
			||||||
            // onTransition: () => console.log(targets),
 | 
					        states: {
 | 
				
			||||||
            shouldTransition: () => followPlayer.distanceToTarget() < 2,
 | 
					            idle: {
 | 
				
			||||||
        }),
 | 
					                on: { TOGGLE: "start", NEXT: "start", PREV: "look", STOP: "finish" }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
        // If the distance to the player is more than two blocks, switch from the lookAtPlayer
 | 
					            start: {
 | 
				
			||||||
        // state to the followPlayer state.
 | 
					                on: { TOGGLE: "look", NEXT: "look", PREV: "idle" },
 | 
				
			||||||
        new StateTransition({
 | 
					                entry: 'lookAtPlayerOnce',
 | 
				
			||||||
            parent: lookAtPlayer,
 | 
					            },
 | 
				
			||||||
            child: followPlayer,
 | 
					            look: {
 | 
				
			||||||
            shouldTransition: () => lookAtPlayer.distanceToTarget() >= 5,
 | 
					                on: { TOGGLE: "idle", NEXT: "idle", PREV: "start" },
 | 
				
			||||||
        }),
 | 
					                // entry: ['look', 'blah']
 | 
				
			||||||
        new StateTransition({
 | 
					                // entry: 'lookAtPlayerOnce',
 | 
				
			||||||
            parent: lookAtPlayer,
 | 
					                activities: 'lookAtPlayer',
 | 
				
			||||||
            child: stay,
 | 
					                meta: { debug: true }
 | 
				
			||||||
            onTransition: () => bot.chat("ok, staying"),
 | 
					            },
 | 
				
			||||||
            // shouldTransition: () => true,
 | 
					            finish: {
 | 
				
			||||||
        }),
 | 
					                type: 'final'
 | 
				
			||||||
        new StateTransition({
 | 
					            },
 | 
				
			||||||
            parent: stay,
 | 
					        },
 | 
				
			||||||
            child: getClosestPlayer,
 | 
					        on: { START: '.start', STOP: '.idle' },
 | 
				
			||||||
            // shouldTransition: () => Math.random() > 0.01,
 | 
					        meta: { debug: true },
 | 
				
			||||||
            // shouldTransition: () => Math.random() > 0.1 && getClosestPlayer.distanceToTarget() < 2,
 | 
					        context: { player: null, rate: updateRate },
 | 
				
			||||||
        }),
 | 
					    }, {
 | 
				
			||||||
    ];
 | 
					        actions: {
 | 
				
			||||||
 | 
					            // action implementation
 | 
				
			||||||
    // Now we just wrap our transition list in a nested state machine layer. We want the bot
 | 
					            lookAtPlayerOnce: (context, event) => {
 | 
				
			||||||
    // to start on the getClosestPlayer state, so we'll specify that here.
 | 
					                const player = context?.player && (context?.player?.isValid || context?.player?.entity?.isValid)
 | 
				
			||||||
    const rootLayer = new NestedStateMachine(transitions, getClosestPlayer, stay);
 | 
					                    || bot.nearestEntity(entity => entity.type === 'player');
 | 
				
			||||||
 | 
					                if (player?.position || player?.entity) {
 | 
				
			||||||
    // We can start our state machine simply by creating a new instance.
 | 
					                    context.player = player;
 | 
				
			||||||
    cfg.stateMachines.follow = new BotStateMachine(bot, rootLayer);
 | 
					                    bot.lookAt((player.entity || player).position.offset(0, 1, 0));
 | 
				
			||||||
    const webserver = new StateMachineWebserver(bot, cfg.stateMachines.follow);
 | 
					                }
 | 
				
			||||||
    webserver.startServer();
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    // mineflayerViewer(bot, { port: 3000 })
 | 
					        activities: {
 | 
				
			||||||
    // const path = [bot.entity.position.clone()]
 | 
					            lookAtPlayer: (context, event) => {
 | 
				
			||||||
    // bot.on('move', () => {
 | 
					                const player = (context?.player?.isValid || context?.player?.entity?.isValid) && context?.player
 | 
				
			||||||
    //     if (path[path.length - 1].distanceTo(bot.entity.position) > 1) {
 | 
					                    || bot.nearestEntity(entity => entity.type === 'player' && entity.isValid);
 | 
				
			||||||
    //         path.push(bot.entity.position.clone())
 | 
					                // TODO check pos every event?
 | 
				
			||||||
    //         bot.viewer.drawLine('path', path)
 | 
					                if (player?.position || player?.entity) {
 | 
				
			||||||
 | 
					                    context.player = player;
 | 
				
			||||||
 | 
					                    function looks() {
 | 
				
			||||||
 | 
					                        bot.lookAt((player.entity || player).position.offset(0, 1, 0));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    bot.on("time", looks);
 | 
				
			||||||
 | 
					                    return () => bot.off("time", looks);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    quiet || bot.chat("look: no valid players");
 | 
				
			||||||
 | 
					                    // TODO use xstate logger
 | 
				
			||||||
 | 
					                    context.debug && console.log("sm: no valid player", this, context, player);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        delays: {
 | 
				
			||||||
 | 
					            /* ... */
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        guards: {
 | 
				
			||||||
 | 
					            /* ... */
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        services: {
 | 
				
			||||||
 | 
					            /* ... */
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    console.log("sm init: machine", machine);
 | 
				
			||||||
 | 
					    const service = runSM(saveSM(machine));
 | 
				
			||||||
 | 
					    if (service?.send) {
 | 
				
			||||||
 | 
					        setTimeout(service.send, 200, "TOGGLE");
 | 
				
			||||||
 | 
					        // setTimeout(service.send, 400, "TOGGLE")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					        console.warn("sm init: service", service);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function newSM(smName = "sm_" + Object.keys(cfg.statemachine.list).length) {
 | 
				
			||||||
 | 
					    smName = smName.replace(/\s+/, '_');
 | 
				
			||||||
 | 
					    if (cfg.statemachine.list[smName]) {
 | 
				
			||||||
 | 
					        console.warn("sm exists", smName);
 | 
				
			||||||
 | 
					        quiet || bot.chat(`sm ${smName} already exists, edit or use another name instead`);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const machine = createMachine({
 | 
				
			||||||
 | 
					        id: smName,
 | 
				
			||||||
 | 
					        initial: "start",
 | 
				
			||||||
 | 
					        // TODO use history states for PAUSE and RESUME
 | 
				
			||||||
 | 
					        states: { idle: { on: { START: "start" } }, start: { on: { STOP: "idle" } } }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    cfg.statemachine.draft = machine;
 | 
				
			||||||
 | 
					    return machine;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function saveSM(machine = cfg.statemachine.draft) {
 | 
				
			||||||
 | 
					    if (!machine?.id) {
 | 
				
			||||||
 | 
					        console.warn("sm save: invalid", machine);
 | 
				
			||||||
 | 
					        quiet || bot.chat("sm: couldn't save, invalid");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // TODO do tests and validation
 | 
				
			||||||
 | 
					    // 1. ensure default states [start, idle]
 | 
				
			||||||
 | 
					    // 2. ensure closed cycle from start to idle
 | 
				
			||||||
 | 
					    cfg.statemachine.list[machine.id] = machine;
 | 
				
			||||||
 | 
					    if (machine.id === cfg.statemachine.draft?.id) {
 | 
				
			||||||
 | 
					        cfg.statemachine.draft = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    writeFile(
 | 
				
			||||||
 | 
					        // TODO
 | 
				
			||||||
 | 
					        // `${storage_dir}/${player}/${machine.id}.json`
 | 
				
			||||||
 | 
					        `${storage_dir}/${machine.id}.json`,
 | 
				
			||||||
 | 
					        // TODO decide which data to store
 | 
				
			||||||
 | 
					        // https://xstate.js.org/docs/guides/states.html#persisting-state
 | 
				
			||||||
 | 
					        // JSON.stringify(machine.toJSON),
 | 
				
			||||||
 | 
					        // JSON.stringify(machine.states.toJSON), // + activities, delays, etc
 | 
				
			||||||
 | 
					        JSON.stringify({ config: machine.config, context: machine.context }), e => e && console.log("sm load sm: write file", e));
 | 
				
			||||||
 | 
					    // return run ? runSM(machine) : machine
 | 
				
			||||||
 | 
					    return machine;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function loadSM(name, run = true) {
 | 
				
			||||||
 | 
					    //: StateMachine<any, any, any> {
 | 
				
			||||||
 | 
					    readFile(
 | 
				
			||||||
 | 
					        // readFileSync(
 | 
				
			||||||
 | 
					        // TODO
 | 
				
			||||||
 | 
					        // `${storage_dir}/${player}/${machine.id}.json`
 | 
				
			||||||
 | 
					        `${storage_dir}/${name}.json`, afterRead
 | 
				
			||||||
 | 
					        // JSON.stringify(machine.toJSON),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    function afterRead(err, jsonString) {
 | 
				
			||||||
 | 
					        if (err) {
 | 
				
			||||||
 | 
					            console.warn("sm load sm: read file", err);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            const machine = createMachine(JSON.parse(jsonString).config);
 | 
				
			||||||
 | 
					            // TODO do tests and validation
 | 
				
			||||||
 | 
					            // 1. ensure default states [start, idle]
 | 
				
			||||||
 | 
					            // 2. ensure closed cycle from start to idle
 | 
				
			||||||
 | 
					            cfg.statemachine.list[machine.id] = machine;
 | 
				
			||||||
 | 
					            if (machine.id === cfg.statemachine.draft?.id) {
 | 
				
			||||||
 | 
					                cfg.statemachine.draft = machine;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (run) {
 | 
				
			||||||
 | 
					                runSM(machine);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // return run ? runSM(machine) : machine
 | 
				
			||||||
 | 
					    // return machine
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// function isInterpreter(SMorService: StateMachine<any, any, any> | Interpreter<any>): SMorService is Interpreter<any> {
 | 
				
			||||||
 | 
					//   return (SMorService as Interpreter<any>).start !== undefined;
 | 
				
			||||||
// }
 | 
					// }
 | 
				
			||||||
    // })
 | 
					function getSM(name = cfg.statemachine.draft || cfg.statemachine.recent, asService = false, quiet = false) {
 | 
				
			||||||
 | 
					    const machine = typeof name === "string" ? cfg.statemachine.list[name]
 | 
				
			||||||
 | 
					        : name;
 | 
				
			||||||
 | 
					    if (!machine) {
 | 
				
			||||||
 | 
					        console.warn("sm get: doesn't exist", name);
 | 
				
			||||||
 | 
					        cfg.statemachine.quiet || bot.chat(`sm ${name} doesn't exist`);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (asService) {
 | 
				
			||||||
const load = (config) => {
 | 
					        const service = cfg.statemachine.running[machine?.id];
 | 
				
			||||||
    cfg = config
 | 
					        if (!service) {
 | 
				
			||||||
    bot = cfg.bot
 | 
					            quiet || console.warn("sm get: already stopped", machine);
 | 
				
			||||||
    // cfg.inventory = {
 | 
					            quiet || cfg.statemachine.quiet || bot.chat(`sm ${machine?.id} isn't running`);
 | 
				
			||||||
    //     auto: true,
 | 
					            return interpret(machine);
 | 
				
			||||||
    //     quiet: false
 | 
					 | 
				
			||||||
    // }
 | 
					 | 
				
			||||||
    // bot.on('chat', inventory)
 | 
					 | 
				
			||||||
    bot.loadPlugin(pathfinder)
 | 
					 | 
				
			||||||
    // statemachineInit()
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        return service;
 | 
				
			||||||
const unload = () => {
 | 
					 | 
				
			||||||
    // bot.off('chat', inventory)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
module.exports = { load, unload }
 | 
					        // return machine.machine ? machine.machine : machine
 | 
				
			||||||
 | 
					        return machine;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function runSM(name = getSM(undefined, undefined, true), player // or supervisor?
 | 
				
			||||||
 | 
					    , restart = false) {
 | 
				
			||||||
 | 
					    if (!name)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    const service = getSM(name, true, true);
 | 
				
			||||||
 | 
					    if (!service)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    const machine = service.machine;
 | 
				
			||||||
 | 
					    if (!machine)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    switch (service.status) {
 | 
				
			||||||
 | 
					        case InterpreterStatus.Running:
 | 
				
			||||||
 | 
					            if (!restart) {
 | 
				
			||||||
 | 
					                console.warn("sm run: already running", service.id);
 | 
				
			||||||
 | 
					                quiet || bot.chat(`sm ${service.id} already running`);
 | 
				
			||||||
 | 
					                return service;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            stopSM(machine);
 | 
				
			||||||
 | 
					        case InterpreterStatus.NotStarted:
 | 
				
			||||||
 | 
					        case InterpreterStatus.Stopped:
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            console.warn("sm run: unknown status:", service.status, service);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (cfg.statemachine.debug || machine.meta?.debug) {
 | 
				
			||||||
 | 
					        service.onTransition((state) => {
 | 
				
			||||||
 | 
					            quiet || bot.chat(`sm trans: ${machine.id}, ${state.value}`);
 | 
				
			||||||
 | 
					            quiet || state.meta?.debug && bot.chat(`sm next events: ${machine.id}, ${state.nextEvents}`);
 | 
				
			||||||
 | 
					            console.log("sm debug: trans", state.value, state);
 | 
				
			||||||
 | 
					        }).onDone((done) => {
 | 
				
			||||||
 | 
					            quiet || bot.chat(`sm done: ${machine.id}, ${done}`);
 | 
				
			||||||
 | 
					            console.log("sm debug: done", done.data, done);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    cfg.statemachine.running[machine.id] = service;
 | 
				
			||||||
 | 
					    cfg.statemachine.recent = machine;
 | 
				
			||||||
 | 
					    service.start();
 | 
				
			||||||
 | 
					    // return machine
 | 
				
			||||||
 | 
					    return service;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function stopSM(name = getSM(), quiet = false) {
 | 
				
			||||||
 | 
					    let service = getSM(name, true, quiet);
 | 
				
			||||||
 | 
					    if (!service)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    const machine = service.machine;
 | 
				
			||||||
 | 
					    switch (service.status) {
 | 
				
			||||||
 | 
					        case InterpreterStatus.NotStarted:
 | 
				
			||||||
 | 
					        case InterpreterStatus.Stopped:
 | 
				
			||||||
 | 
					            console.log("sm stop status", service.status, service.id, cfg.statemachine.running[service.id]);
 | 
				
			||||||
 | 
					        // TODO check if any bugs
 | 
				
			||||||
 | 
					        case InterpreterStatus.Running:
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            console.warn("sm stop: unknown status:", service.status);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    service?.stop?.();
 | 
				
			||||||
 | 
					    cfg.statemachine.running[machine.id] = null;
 | 
				
			||||||
 | 
					    delete cfg.statemachine.running[machine.id];
 | 
				
			||||||
 | 
					    // return machine
 | 
				
			||||||
 | 
					    return service;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function actionSM(action, name = getSM()) {
 | 
				
			||||||
 | 
					    if (!action) {
 | 
				
			||||||
 | 
					        return console.warn("sm action", action);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let service = getSM(name, true, true);
 | 
				
			||||||
 | 
					    if (service.status !== InterpreterStatus.Running)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    // const machine = service.machine
 | 
				
			||||||
 | 
					    service.send(action.toUpperCase());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function stepSM(command = "", ...message_parts) {
 | 
				
			||||||
 | 
					    let service = getSM(undefined, true);
 | 
				
			||||||
 | 
					    if (!service)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    if (!service.send) {
 | 
				
			||||||
 | 
					        console.warn("sm step: can't send", service.machine);
 | 
				
			||||||
 | 
					        // TODO start a temporary service to interpret
 | 
				
			||||||
 | 
					        quiet || bot.chat("sm: step doesn't support machines that aren't running yet");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (service?.status !== InterpreterStatus.Running) {
 | 
				
			||||||
 | 
					        console.warn("sm step: machine not running, attempting start", service);
 | 
				
			||||||
 | 
					        runSM;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // const machine = service.machine
 | 
				
			||||||
 | 
					    switch (command) {
 | 
				
			||||||
 | 
					        case "edit":
 | 
				
			||||||
 | 
					            // maybe `edit <type>`, like `add`?
 | 
				
			||||||
 | 
					            // where type:
 | 
				
			||||||
 | 
					            // context
 | 
				
			||||||
 | 
					            // action
 | 
				
			||||||
 | 
					            // timeout | all timeout
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "undo":
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "del":
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "p":
 | 
				
			||||||
 | 
					        case "prev":
 | 
				
			||||||
 | 
					            service?.send("PREV");
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "add":
 | 
				
			||||||
 | 
					        // maybe `add <type>`?
 | 
				
			||||||
 | 
					        // where type:
 | 
				
			||||||
 | 
					        // context
 | 
				
			||||||
 | 
					        // action
 | 
				
			||||||
 | 
					        // timeout
 | 
				
			||||||
 | 
					        case "new":
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        // case "with":
 | 
				
			||||||
 | 
					        //   console.log(this)
 | 
				
			||||||
 | 
					        //   stepSM(getSM(message_parts[0], true, true), ...message_parts.slice(1))
 | 
				
			||||||
 | 
					        //   break;
 | 
				
			||||||
 | 
					        case "help":
 | 
				
			||||||
 | 
					            quiet || bot.chat("![sm ]step [ p(rev) | n(ext) ]");
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "n":
 | 
				
			||||||
 | 
					        case "next":
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            service?.send("NEXT");
 | 
				
			||||||
 | 
					            quiet || bot.chat("stepped");
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function tickSM(time = bot.time.timeOfDay, rate = 20) {
 | 
				
			||||||
 | 
					    if (time % rate !== 0) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    console.log("sm tick", rate, time);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function debugSM(name = getSM()) {
 | 
				
			||||||
 | 
					    if (!name)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    let service = getSM(name, true);
 | 
				
			||||||
 | 
					    if (!service)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    const machine = service.machine;
 | 
				
			||||||
 | 
					    machine.meta.debug = !!!machine.meta.debug;
 | 
				
			||||||
 | 
					    console.info("sm debug", machine.meta, service, machine);
 | 
				
			||||||
 | 
					    cfg.statemachine.quiet || machine.meta.debug && bot.chat("sm debug: " + machine.id);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function command(message_parts) {
 | 
				
			||||||
 | 
					    const message_parts2 = message_parts.slice(1);
 | 
				
			||||||
 | 
					    switch (message_parts[0]) {
 | 
				
			||||||
 | 
					        case "add":
 | 
				
			||||||
 | 
					            command(["new"].concat(message_parts2));
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "finish":
 | 
				
			||||||
 | 
					        case "done":
 | 
				
			||||||
 | 
					        case "end":
 | 
				
			||||||
 | 
					            command(["save"].concat(message_parts2));
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "do":
 | 
				
			||||||
 | 
					        case "load":
 | 
				
			||||||
 | 
					        case "start":
 | 
				
			||||||
 | 
					            command(["run"].concat(message_parts2));
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "debug":
 | 
				
			||||||
 | 
					            switch (message_parts[1]) {
 | 
				
			||||||
 | 
					                case "sm":
 | 
				
			||||||
 | 
					                case "global":
 | 
				
			||||||
 | 
					                case "meta":
 | 
				
			||||||
 | 
					                    cfg.statemachine.debug = !!!cfg.statemachine.debug;
 | 
				
			||||||
 | 
					                    quiet || bot.chat(`sm debug: ${cfg.statemachine.debug}`);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        case "new":
 | 
				
			||||||
 | 
					        case "save":
 | 
				
			||||||
 | 
					        case "run":
 | 
				
			||||||
 | 
					        case "step":
 | 
				
			||||||
 | 
					        case "stop":
 | 
				
			||||||
 | 
					        // temp
 | 
				
			||||||
 | 
					        case "action":
 | 
				
			||||||
 | 
					            switch (message_parts2.length) {
 | 
				
			||||||
 | 
					                case 0:
 | 
				
			||||||
 | 
					                    console.warn(`sm ${message_parts[0]}: no name, using defaults`);
 | 
				
			||||||
 | 
					                case 1:
 | 
				
			||||||
 | 
					                    // FIXME `this` doesn't work always
 | 
				
			||||||
 | 
					                    (this.runSM && this || cfg.plugins.statemachine)[message_parts[0] + "SM"](...message_parts2);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    if (["action"].includes(message_parts[0])) {
 | 
				
			||||||
 | 
					                        (this.runSM && this || cfg.plugins.statemachine)[message_parts[0] + "SM"](...message_parts2);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else {
 | 
				
			||||||
 | 
					                        console.warn(`sm ${message_parts[0]}: more than 1 arg passed`, message_parts2);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "undo":
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "list":
 | 
				
			||||||
 | 
					        case "status":
 | 
				
			||||||
 | 
					            // TODO current/recent, updateRate
 | 
				
			||||||
 | 
					            const { list, running } = cfg.statemachine;
 | 
				
			||||||
 | 
					            console.log("sm list", running, list);
 | 
				
			||||||
 | 
					            quiet || bot.chat(`${Object.keys(running).length} of ${Object.keys(list).length} active: ${Object.keys(running)}`
 | 
				
			||||||
 | 
					                + (message_parts[1] === "all" ? `${Object.keys(list)}` : ''));
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "quiet":
 | 
				
			||||||
 | 
					            quiet = cfg.statemachine.quiet = !!!cfg.statemachine.quiet;
 | 
				
			||||||
 | 
					            quiet || bot.chat(`sm: ${cfg.statemachine.quiet ? "" : "not "}being quiet`);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            // TODO general helper from declarative commands object
 | 
				
			||||||
 | 
					            quiet || bot.chat(`sm help: !sm [new | step| save | run | list | quiet | debug]`);
 | 
				
			||||||
 | 
					            console.warn("sm unknown command", message_parts);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function load(config) {
 | 
				
			||||||
 | 
					    webserver = cfg.statemachine.webserver = config.statemachine?.webserver || webserver;
 | 
				
			||||||
 | 
					    config.statemachine = cfg.statemachine || {
 | 
				
			||||||
 | 
					        webserver: null,
 | 
				
			||||||
 | 
					        // quiet: true,
 | 
				
			||||||
 | 
					        quiet: false,
 | 
				
			||||||
 | 
					        list: {},
 | 
				
			||||||
 | 
					        running: {},
 | 
				
			||||||
 | 
					        draft: null,
 | 
				
			||||||
 | 
					        recent: null,
 | 
				
			||||||
 | 
					        // debug: null,
 | 
				
			||||||
 | 
					        debug: true,
 | 
				
			||||||
 | 
					        updateRate: updateRate
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    cfg = config;
 | 
				
			||||||
 | 
					    bot = cfg.bot;
 | 
				
			||||||
 | 
					    // pathfinder = bot.pathfinder || bot.loadPlugin(require('mineflayer-pathfinder').pathfinder)
 | 
				
			||||||
 | 
					    // mcData = bot.mcData || (bot.mcData = require('minecraft-data')(bot.version))
 | 
				
			||||||
 | 
					    init(undefined, webserver);
 | 
				
			||||||
 | 
					    updateRate = cfg.statemachine.updateRate || updateRate;
 | 
				
			||||||
 | 
					    bot.on('time', tickSM);
 | 
				
			||||||
 | 
					    // bot.once('time', tickSM, 5)
 | 
				
			||||||
 | 
					    console.log("sm load", cfg.statemachine);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function unload() {
 | 
				
			||||||
 | 
					    const { list, running } = cfg.statemachine;
 | 
				
			||||||
 | 
					    bot.off('time', tickSM);
 | 
				
			||||||
 | 
					    Object.keys(running).forEach(sm => {
 | 
				
			||||||
 | 
					        stopSM(sm);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    // delete cfg.statemachine;
 | 
				
			||||||
 | 
					    cfg.statemachine = null;
 | 
				
			||||||
 | 
					    console.log("sm unload: deleted", cfg.statemachine);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					    load, unload, command, init,
 | 
				
			||||||
 | 
					    newSM, saveSM, loadSM, runSM, stopSM, actionSM, stepSM, debugSM
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										30
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								package.json
									
									
									
									
									
								
							@@ -30,24 +30,26 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "homepage": "https://github.com/PrismarineJS/prismarine-template#readme",
 | 
					  "homepage": "https://github.com/PrismarineJS/prismarine-template#readme",
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
 | 
					    "@types/node": "^14.14.35",
 | 
				
			||||||
    "jest": "^26.6.3",
 | 
					    "jest": "^26.6.3",
 | 
				
			||||||
    "require-self": "^0.2.3"
 | 
					    "require-self": "^0.2.3",
 | 
				
			||||||
 | 
					    "typescript": "^4.2.3"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "minecraft-data": "^2.70.2",
 | 
					    "dotenv-packed": "^1.2.1",
 | 
				
			||||||
    "mineflayer": "^2.34.0",
 | 
					    "minecraft-data": "^2.80.0",
 | 
				
			||||||
    "mineflayer-armor-manager": "^1.3",
 | 
					    "mineflayer": "^3.4.0",
 | 
				
			||||||
    "mineflayer-pathfinder": "^1.1",
 | 
					    "mineflayer-armor-manager": "^1.4.0",
 | 
				
			||||||
    "mineflayer-pvp": "^1",
 | 
					    "mineflayer-pathfinder": "^1.6.1",
 | 
				
			||||||
    "prismarine-block": "^1",
 | 
					    "mineflayer-pvp": "^1.0.2",
 | 
				
			||||||
    "prismarine-chat": "^1",
 | 
					    "prismarine-block": "^1.8.0",
 | 
				
			||||||
 | 
					    "prismarine-chat": "^1.0.3",
 | 
				
			||||||
    "prismarine-entity": "^1.1.0",
 | 
					    "prismarine-entity": "^1.1.0",
 | 
				
			||||||
    "prismarine-item": "^1.5.0",
 | 
					    "prismarine-item": "^1.8.0",
 | 
				
			||||||
    "prismarine-nbt": "^1.3",
 | 
					    "prismarine-nbt": "^1.5.0",
 | 
				
			||||||
    "prismarine-recipe": "^1",
 | 
					    "prismarine-recipe": "^1.1.0",
 | 
				
			||||||
    "typescript": "^4",
 | 
					    "vec3": "^0.1.7",
 | 
				
			||||||
    "vec3": "^0.1",
 | 
					    "xstate": "^4.17.0"
 | 
				
			||||||
    "dotenv-packed": "^1.2"
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "files": [
 | 
					  "files": [
 | 
				
			||||||
    "lib/**/*"
 | 
					    "lib/**/*"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user