let bot let mcData let quiet // import v from 'vec3' const v = require('vec3') let cfg = { info: { quiet: quiet, recentCommand: null, }, // to satisfy typescript quiet: null, bot: bot } var RelativePosEnum (function (RelativePosEnum) { RelativePosEnum[RelativePosEnum["feet"] = 0] = "feet" RelativePosEnum[RelativePosEnum["standing"] = 1] = "standing" RelativePosEnum[RelativePosEnum["head"] = 2] = "head" RelativePosEnum[RelativePosEnum["looking"] = 3] = "looking" RelativePosEnum[RelativePosEnum["infront"] = 4] = "infront" RelativePosEnum[RelativePosEnum["behind"] = 5] = "behind" })(RelativePosEnum || (RelativePosEnum = {})) function relPosToBlock(entity, relPos) { let pos if (!(relPos in RelativePosEnum)) return console.warn("info: not a relative position:", relPos) relPos = typeof relPos === "number" ? RelativePosEnum[relPos] : relPos switch (relPos) { case "feet": pos = entity.position; break case "standing": pos = entity.position.offset(0, -1, 0) break // todo use CARDINAL directions api from pathfinder // case "behind": // entity.yaw // pos = entity.position // break // case "front": // case "infront": // pos = entity.position // break case "head": pos = entity.position.offset(0, 1.85, 0) break case "looking": if (entity === bot.entity) { return bot.blockAtCursor() // return bot.blockInSight(128, 128) } default: quiet || bot.chat(`info: pos '${relPos}' not implemented`) break } if (pos) { // nearest block return bot.blockAt(pos) } } function isVec3(vec) { return vec?.length === 3 || vec.x && vec.y && vec.z } function block(entity = bot.entity, pos = entity?.position?.offset(0, -1, 0)) { console.assert(pos || entity) const block = isVec3(pos) ? bot.blockAt(v(pos)) : typeof pos === "string" ? relPosToBlock(entity, RelativePosEnum[pos]) : pos console.log(block, block?.getProperties && block.getProperties()) if (!block) { quiet || bot.chat("empty block") return block } bot.lookAt(block?.position) let info = [block.type, block.name] if (block.metadata) info.push(Object.entries(block.getProperties())) quiet || bot.chat(info.join(": ")) } function item( entity = bot.entity, slot = entity.heldItem ) { const item = typeof slot === "number" ? bot.inventory.slots[slot + bot.QUICK_BAR_START] : slot console.log("info item:", item) if (!item) { quiet || bot.chat("no item") return item } let info = [item.type, item.name, item.count] if (item.metadata) info.push("meta: " + item.metadata.length) if (item.nbt) { info.push(compound_value(item.nbt)) } 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 = bot.nearestEntity()) { const entity = typeof name === "string" ? (name = name.toLowerCase()) && bot.nearestEntity((entity) => { const enames = [entity.username?.toLowerCase(), entity.name, entity.displayName?.toLowerCase()] return enames.includes(name) }) : name console.log("info entity:", entity) if (!entity) { 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) quiet || bot.chat(info.join("; ")) return entity } function blockOrItemFromId(num, quiet = cfg.info.quiet) { const block = mcData?.blocks[num] const item = mcData?.items[num] // const entity = mcData?.entities[num] if (block || item) { quiet || bot.chat( (block && `block: ${block.name}, ` || "") + (item && `item: ${item.name}, ` || "") // + (entity && `entity: ${entity.name}, ` || "") ) } else { quiet || bot.chat("info: nonexistent block or item") } return { block, item } } function command(message_parts, player) { if (message_parts.length > 0) { cfg.info.recentCommand = message_parts } if (player === null) player = void 0 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() } break; case 1: switch (message_parts[0]) { case "quiet": cfg.info.quiet = quiet = !quiet; quiet || bot.chat(`info: ${quiet ? "" : "not "}being quiet`); break; case "i": case "item": item() break case "e": case "entity": entity() break case "me": block(player) break case "b": case "block": block() break default: const num = parseInt(message_parts[0]) if (isFinite(num)) { blockOrItemFromId(num) } else { quiet || bot.chat("info usage: `!info [me|i|e|b||quiet]`") } break; } break; case 2: switch (message_parts[0]) { case "i": case "item": switch (message_parts[1]) { case "me": item(player) break default: const slot = parseInt(message_parts[1]) slot && item(undefined, slot) break } break case "b": case "block": switch (message_parts[1]) { case "me": block(player) break default: if (message_parts[1] in RelativePosEnum) { block(undefined, message_parts[1]) } else { // or entity(message_parts[1]).position console.log(bot.players[message_parts[1]]) quiet || bot.chat("info: not yet implemented") } } break case "e": case "entity": default: entity(message_parts[1]) break; } break case 4: switch (message_parts[0]) { case "b": case "block": default: block(undefined, message_parts.slice(1)) break; } break; default: cfg.quiet || bot.chat("info: unknown command") break; } } const load = (config) => { config.info = cfg.info cfg = config bot = cfg.bot mcData = bot.mcData || (bot.mcData = require('minecraft-data')(bot.version)) } const unload = () => {} module.exports = { load, unload, command, block, item, entity }