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