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 } var VillagerProfession (function (VillagerProfession) { VillagerProfession[VillagerProfession["Unemployed"] = 0] = "Unemployed" VillagerProfession[VillagerProfession["Armourer"] = 1] = "Armourer" VillagerProfession[VillagerProfession["Butcher"] = 2] = "Butcher" VillagerProfession[VillagerProfession["Cartographer"] = 3] = "Cartographer" VillagerProfession[VillagerProfession["Cleric"] = 4] = "Cleric" VillagerProfession[VillagerProfession["Farmer"] = 5] = "Farmer" VillagerProfession[VillagerProfession["Fisherman"] = 6] = "Fisherman" VillagerProfession[VillagerProfession["Fletcher"] = 7] = "Fletcher" VillagerProfession[VillagerProfession["Leatherworker"] = 8] = "Leatherworker" VillagerProfession[VillagerProfession["Librarian"] = 9] = "Librarian" VillagerProfession[VillagerProfession["Mason"] = 10] = "Mason" VillagerProfession[VillagerProfession["Nitwit"] = 11] = "Nitwit" VillagerProfession[VillagerProfession["Shepherd"] = 12] = "Shepherd" VillagerProfession[VillagerProfession["Toolsmith"] = 13] = "Toolsmith" VillagerProfession[VillagerProfession["Weaponsmith"] = 14] = "Weaponsmith" })(VillagerProfession || (VillagerProfession = {})) 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 } entity?.position && bot.lookAt(entity.position) let info = [(entity.entityType && entity.entityType + ":" || "") + (entity.type === "object" ? entity.kind : entity.type)] info.push(entity.username || entity.name) // TODO various info depending on the type of entity; player, villager, etc // TODO refactor and combine with compound_value() switch (entity.type) { case "object": switch (entity.kind) { case "Drops": const item = entity.metadata[7]; entity.metadata; if (item.present) { info.push("item: " + item.itemId + ": " + mcData.items[item.itemId].name + `x${item.itemCount}`); } else { console.warn("info entity: metadata expected item, got:", entity.metadata[7]); } break; default: } break; case "mob": switch (name) { case 'villager': const { villagerProfession, ...otherProps } = entity.metadata[17] info.push(VillagerProfession[villagerProfession]) info.push(Object.entries(otherProps).toString()) break default: break } entity.metadata[8] && info.push("health:" + entity.metadata[8]) entity.metadata[15] && info.push("baby") if (entity.heldItem) { info.push("holding") item(entity) } case "global": case "orb": case "player": break; case "other": info.push("kind:" + entity.kind) default: } 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: const aPlayer = bot.players[message_parts[1]] if (message_parts[1] in RelativePosEnum) { block(undefined, message_parts[1]) } else if (aPlayer) { if (aPlayer.entity) { block(aPlayer.entity) } else { quiet || bot.chat(`info: player ${aPlayer.username} too far!`) } } else { // or entity(message_parts[1]).position console.log(bot.players[message_parts[1]]) quiet || bot.chat("info: not yet implemented") } } break case "me": if (message_parts[1] in RelativePosEnum) { block(player, 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(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 }