mineflayer-mouse
Version:
First-class **battle-tested** simple API for emulating real Minecraft mouse control in Mineflayer. You should use it for digging (mining) or placing blocks and entity attacking or using and using items!
164 lines (163 loc) • 7.43 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isBlockIntersectsWithEntities = exports.botTryPlaceBlockPrediction = exports.directionToVector = void 0;
const itemBlocksStatic_1 = require("./itemBlocksStatic");
const prismarine_block_1 = __importDefault(require("prismarine-block"));
const vec3_1 = require("vec3");
const minecraft_data_1 = __importDefault(require("minecraft-data"));
exports.directionToVector = [new vec3_1.Vec3(0, -1, 0), new vec3_1.Vec3(0, 1, 0), new vec3_1.Vec3(0, 0, -1), new vec3_1.Vec3(0, 0, 1), new vec3_1.Vec3(-1, 0, 0), new vec3_1.Vec3(1, 0, 0)];
const directionToAxis = ['y', 'y', 'z', 'z', 'x', 'x'];
const directionToFacing = ['south', 'west', 'north', 'east', 'up', 'down'];
const botTryPlaceBlockPrediction = (bot, cursorBlock, faceNum, delta, doWorldUpdate, doWorldUpdateDelay, override, checkEntities) => {
if (!bot.heldItem)
return false;
const isSneaking = bot.controlState.sneak;
const adventurePlaceAllowed = bot.heldItem.blocksCanPlaceOn?.some(([blockName]) => blockName === cursorBlock.name) ?? false;
const isBlockPlaceAction = bot.game.gameMode === 'adventure' ? adventurePlaceAllowed : (isSneaking ||
// not interact action
itemBlocksStatic_1.activatableBlockWithoutItemPatterns.every(pattern => !pattern.test(cursorBlock.name)));
if (!isBlockPlaceAction)
return false;
const referencePosition = cursorBlock.position.clone();
const oldBlock = bot.world.getBlock(referencePosition);
const blockIsEmpty = oldBlock?.shapes.length === 0; // grass
const directionVector = blockIsEmpty ? new vec3_1.Vec3(0, 0, 0) : exports.directionToVector[faceNum];
const placingPosition = referencePosition.plus(directionVector);
const mcData = (0, minecraft_data_1.default)(bot.version);
const itemName = bot.heldItem.name;
const block = mcData.blocksByName[itemBlocksStatic_1.itemToBlockRemaps[itemName] ?? itemName];
if (block) {
const cursorY = delta.y;
let half = cursorY > 0.5 ? 'top' : 'bottom';
if (faceNum === 0)
half = 'top';
else if (faceNum === 1)
half = 'bottom';
const axis = directionToAxis[faceNum];
const facing = directionToFacing[faceNum];
const prismarineBlock = (0, prismarine_block_1.default)(bot.version).fromStateId(block.defaultState, 0);
let finalBlock = getBlockFromProperties((0, prismarine_block_1.default)(bot.version), prismarineBlock, block, [
{
// like slabs
matchingState: 'type',
requireValues: ['bottom', 'top', 'double'],
// todo support double
value: half
},
{
// like stairs
matchingState: 'axis',
requireValues: ['x', 'y', 'z'],
value: axis
},
{
// like fences, signs
matchingState: 'facing',
requireValues: ['north', 'south', 'east', 'west', 'up', 'down'],
value: facing
},
]);
finalBlock.position = placingPosition;
if (override) {
const overriddenBlock = override(finalBlock);
if (overriddenBlock === null) {
// block placement cancelled
return false;
}
else if (overriddenBlock) {
finalBlock = overriddenBlock;
}
}
const isIntersectsWithEntities = checkEntities ? (0, exports.isBlockIntersectsWithEntities)(bot.entities, placingPosition, finalBlock.shapes) : false;
if (isIntersectsWithEntities) {
// console.log('Intersecting with entity', isIntersectsWithEntities.name)
return false;
}
if (doWorldUpdate) {
const doUpdate = () => {
bot.world.setBlockStateId(placingPosition, finalBlock.stateId);
};
if (doWorldUpdateDelay) {
let timeout = setTimeout(doUpdate, doWorldUpdateDelay);
bot.on('end', () => {
clearTimeout(timeout);
});
bot.on('blockUpdate', (_, newBlock) => {
if (newBlock.position.equals(placingPosition)) {
clearTimeout(timeout);
}
});
}
else {
doUpdate();
}
}
}
return true;
};
exports.botTryPlaceBlockPrediction = botTryPlaceBlockPrediction;
const isBlockIntersectsWithEntities = (entities, position, blockShapes) => {
for (const entity of Object.values(entities)) {
const w = entity.width / 2;
const entityShapes = [[-w, 0, -w, w, entity.height, w]];
// Check each entity shape against each block shape for intersection
for (const entityShape of entityShapes) {
// Translate entity shape to entity position
const translatedEntityShape = [
entityShape[0] + entity.position.x,
entityShape[1] + entity.position.y,
entityShape[2] + entity.position.z,
entityShape[3] + entity.position.x,
entityShape[4] + entity.position.y,
entityShape[5] + entity.position.z
];
for (const blockShape of blockShapes) {
// Translate block shape to target position
const translatedBlockShape = [
blockShape[0] + position.x,
blockShape[1] + position.y,
blockShape[2] + position.z,
blockShape[3] + position.x,
blockShape[4] + position.y,
blockShape[5] + position.z
];
// Check if boxes intersect
if (boxesIntersect(translatedEntityShape, translatedBlockShape)) {
return entity;
}
}
}
}
return false;
};
exports.isBlockIntersectsWithEntities = isBlockIntersectsWithEntities;
// Helper function to check if two boxes intersect
const boxesIntersect = (box1, box2) => {
return (box1[0] <= box2[3] && box1[3] >= box2[0]) && // X axis overlap
(box1[1] <= box2[4] && box1[4] >= box2[1]) && // Y axis overlap
(box1[2] <= box2[5] && box1[5] >= box2[2]); // Z axis overlap
};
const getBlockFromProperties = (prismarineBlockInstance, prismarineBlock, blockData, properties) => {
const states = blockData.states;
if (!states)
return prismarineBlock;
const defaultProps = prismarineBlock.getProperties();
const finalProps = {};
for (const prop of states) {
const propName = prop.name;
const propValue = properties.find(p => {
if (p.matchingState !== propName)
return false;
if (p.requireValues) {
if (!p.requireValues.every(v => prop.values?.includes(v)))
return false;
}
return true;
})?.value;
finalProps[propName] = propValue ?? defaultProps[propName];
}
return prismarineBlockInstance.fromProperties(blockData.id, finalProps, 0);
};