const env = require("dotenv-packed").parseEnv().parsed const fs = require('fs'); const cfg = { admin: process.env.MINECRAFT_PLAYER_ADMIN || env.MINECRAFT_PLAYER_ADMIN || console.warn("main: bot admin user not provided"), mods: process.env.MINECRAFT_PLAYER_MODS || env.MINECRAFT_PLAYER_MODS || [], // json array, stateMachines: {} } const mineflayer = require("mineflayer"); // const { createGetAccessor } = require('typescript'); const options = !isNaN(parseInt(process.argv[3])) && parseInt(process.argv[3]) > 1e2 ? { 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, } : { 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, password: process.argv[4] || process.env.MINECRAFT_PASS || env.MINECRAFT_PASS, // port: process.argv[5] || process.env.MINECRAFT_PORT || 58471, } const bot = mineflayer.createBot(options) cfg.botOptions = options let plugins = {} function loadplugin(pluginname, pluginpath = './plugins/' + pluginname) { try { plugins[pluginname] = require(pluginpath) plugins[pluginname].load(cfg) } catch (error) { if (error.code == 'MODULE_NOT_FOUND') { console.warn('plugin not used:', pluginpath) } else if (plugins[pluginname] && !plugins[pluginname].load) { unloadplugin(pluginname, pluginpath) } else { console.error(error) } } } function unloadplugin(pluginname, pluginpath = './plugins/' + pluginname) { const plugin = require.resolve(pluginpath) try { if (plugin && require.cache[plugin]) { // `unload()` isn't exported sometimes, // when plugin isn't properly loaded require.cache[plugin].exports?.unload?.() delete plugins[pluginname] delete require.cache[plugin] } } catch (error) { console.error(error) } } function reloadplugin(event, filename, pluginpath = './plugins/') { if (!/\.js$/.test(filename)) { return } filename = filename.replace("\\", "/") // windows if (!cfg.fsTimeout) { console.info(event, filename) const fullpluginpath = pluginpath + filename const pluginname = (filename.split(".js")[0]).replace(/\/.+/, "") pluginpath = pluginpath + pluginname const hassubplugin = fullpluginpath.replace(/\.js$/, "") !== pluginpath const check = Object.keys(cfg.plugins) console.info(`reload file: ./lib/${fullpluginpath}`) const plugin = require.resolve(fullpluginpath) if (plugin && require.cache[plugin]) { // console.debug(Object.keys(cfg.plugins)) unloadplugin(pluginname) console.assert(Object.keys(cfg.plugins).length == check.length - 1, "plugin not removed, potential memory leak") if (hassubplugin) { console.info("reload: also unloading sub") unloadplugin(pluginname, fullpluginpath) } } loadplugin(pluginname) if (Object.keys(cfg.plugins).length != check.length) { // If left < right : // - new plugin that's not registered in cfg.plugins, so added // - rename, so old wasn't removed // If left > right : // - error in file (syntax, missing load()), so was not reloaded console.warn("plugin count mismatch", check.length, Object.keys(cfg.plugins).length, Object.keys(cfg.plugins)) } cfg.fsTimeout = setTimeout(function () { cfg.fsTimeout = null }, 2000) // give 2 seconds for multiple events } // console.log('file.js %s event', event) } fs.watch('./lib/plugins', { recursive: true }, reloadplugin) cfg.bot = bot // TODO better name, or switch to array cfg.botAddressPrefix = '!' cfg.quiet = true // == actually do stuff bot.once("spawn", () => { plugins = { command: require('./plugins/command'), informer: require('./plugins/informer'), inventory: require('./plugins/inventory'), 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'), statemachine: require('./plugins/statemachine'), } cfg.plugins = plugins // cfg.botAddressPrefix = ${bot.username.substr(-2,2)} cfg.botAddressRegex = new RegExp(`^${bot.username}:? (/|${cfg.botAddressPrefix}.+)`) // FIXME leaks every load, so adding here instead of command.js to load only once bot.addChatPattern("web", /\[WEB\] (\[.+\])?\s*([\w.]+): (.+)/, { parse: true }) for (const plugin of Object.values(plugins)) { try { plugin.load(cfg) } catch (error) { console.warn(error) } } }) bot.on("death", () => { bot.pathfinder && bot.pathfinder.setGoal(null) // plugins.guard.unload() }) // bot.on("respawn", () => { // // setTimeout(plugins.guard.load, 2000) // })