const { Vec3 } = require('vec3')
const conv = require('../conversions')
const PLAYER_HEIGHT = 1.8
const CROUCH_HEIGHT = 1.5
const PLAYER_WIDTH = 0.6
const PLAYER_EYEHEIGHT = 1.62
const CROUCH_EYEHEIGHT = 1.27
module.exports = inject
const animationEvents = {
0: 'entitySwingArm',
1: 'entityHurt',
2: 'entityWake',
3: 'entityEat',
4: 'entityCriticalEffect',
5: 'entityMagicCriticalEffect'
}
const entityStatusEvents = {
2: 'entityHurt',
3: 'entityDead',
6: 'entityTaming',
7: 'entityTamed',
8: 'entityShakingOffWater',
10: 'entityEatingGrass',
55: 'entityHandSwap'
}
function inject (bot) {
const { mobs } = bot.registry
const Entity = require('prismarine-entity')(bot.version)
const Item = require('prismarine-item')(bot.version)
const ChatMessage = require('prismarine-chat')(bot.registry)
bot._client.on('destroy_entity', (packet) => {
bot._client.emit('entity_destroy', { entityIds: [packet.entityId] })
})
bot.findPlayer = bot.findPlayers = (filter) => {
const filterFn = (entity) => {
if (entity.type !== 'player') return false
if (filter === null) return true
if (typeof filter === 'object' && filter instanceof RegExp) {
return entity.username.search(filter) !== -1
} else if (typeof filter === 'function') {
return filter(entity)
} else if (typeof filter === 'string') {
return entity.username.toLowerCase() === filter.toLowerCase()
}
return false
}
const resultSet = Object.values(bot.entities)
.filter(filterFn)
if (typeof filter === 'string') {
switch (resultSet.length) {
case 0:
return null
case 1:
return resultSet[0]
default:
return resultSet
}
}
return resultSet
}
bot.players = {}
bot.uuidToUsername = {}
bot.entities = {}
bot._playerFromUUID = (uuid) => Object.values(bot.players).find(player => player.uuid === uuid)
bot.nearestEntity = (match = (entity) => { return true }) => {
let best = null
let bestDistance = Number.MAX_VALUE
for (const entity of Object.values(bot.entities)) {
if (entity === bot.entity || !match(entity)) {
continue
}
const dist = bot.entity.position.distanceSquared(entity.position)
if (dist < bestDistance) {
best = entity
bestDistance = dist
}
}
return best
}
bot._client.on('login', (packet) => {
bot.players = {}
bot.uuidToUsername = {}
bot.entities = {}
bot.entity = fetchEntity(packet.entityId)
bot.username = bot._client.username
bot.entity.username = bot._client.username
bot.entity.type = 'player'
bot.entity.name = 'player'
bot.entity.height = PLAYER_HEIGHT
bot.entity.width = PLAYER_WIDTH
bot.entity.eyeHeight = PLAYER_EYEHEIGHT
})
bot._client.on('entity_equipment', (packet) => {
const entity = fetchEntity(packet.entityId)
if (packet.equipments !== undefined) {
packet.equipments.forEach(equipment => entity.setEquipment(equipment.slot, equipment.item ? Item.fromNotch(equipment.item) : null))
} else {
entity.setEquipment(packet.slot, packet.item ? Item.fromNotch(packet.item) : null)
}
bot.emit('entityEquip', entity)
})
bot._client.on('bed', (packet) => {
const entity = fetchEntity(packet.entityId)
entity.position.set(packet.location.x, packet.location.y, packet.location.z)
bot.emit('entitySleep', entity)
})
bot._client.on('animation', (packet) => {
const entity = fetchEntity(packet.entityId)
const eventName = animationEvents[packet.animation]
if (eventName) bot.emit(eventName, entity)
})
bot.on('entityCrouch', (entity) => {
entity.eyeHeight = CROUCH_EYEHEIGHT
entity.height = CROUCH_HEIGHT
})
bot.on('entityUncrouch', (entity) => {
entity.eyeHeight = PLAYER_EYEHEIGHT
entity.height = PLAYER_HEIGHT
})
bot._client.on('collect', (packet) => {
const collector = fetchEntity(packet.collectorEntityId)
const collected = fetchEntity(packet.collectedEntityId)
bot.emit('playerCollect', collector, collected)
})
const entityDataByInternalId = Object.fromEntries(bot.registry.entitiesArray.map((e) => [e.internalId, e]))
function setEntityData (entity, type, entityData) {
entityData ??= entityDataByInternalId[type]
if (entityData) {
entity.type = entityData.type || 'object'
entity.displayName = entityData.displayName
entity.entityType = entityData.id
entity.name = entityData.name
entity.kind = entityData.category
entity.height = entityData.height
entity.width = entityData.width
} else {
entity.type = 'other'
entity.entityType = type
entity.displayName = 'unknown'
entity.name = 'unknown'
entity.kind = 'unknown'
}
}
function updateEntityPos (entity, pos) {
if (bot.supportFeature('fixedPointPosition')) {
entity.position.set(pos.x / 32, pos.y / 32, pos.z / 32)
} else if (bot.supportFeature('doublePosition')) {
entity.position.set(pos.x, pos.y, pos.z)
}
entity.yaw = conv.fromNotchianYawByte(pos.yaw)
entity.pitch = conv.fromNotchianPitchByte(pos.pitch)
}
function addNewPlayer (entityId, uuid, pos) {
const entity = fetchEntity(entityId)
entity.type = 'player'
entity.name = 'player'
entity.username = bot.uuidToUsername[uuid]
entity.uuid = uuid
updateEntityPos(entity, pos)
entity.eyeHeight = PLAYER_EYEHEIGHT
entity.height = PLAYER_HEIGHT
entity.width = PLAYER_WIDTH
if (bot.players[entity.username] !== undefined && !bot.players[entity.username].entity) {
bot.players[entity.username].entity = entity
}
return entity
}
function addNewNonPlayer (entityId, uuid, entityType, pos) {
const entity = fetchEntity(entityId)
const entityData = bot.registry.entities[entityType]
setEntityData(entity, entityType, entityData)
updateEntityPos(entity, pos)
entity.uuid = uuid
return entity
}
bot._client.on('named_entity_spawn', (packet) => {
if (packet.playerUUID in bot.uuidToUsername) {
const entity = addNewPlayer(packet.entityId, packet.playerUUID, packet, packet.metadata)
entity.dataBlobs = packet.data
entity.metadata = parseMetadata(packet.metadata, entity.metadata)
bot.emit('entitySpawn', entity)
}
})
bot._client.on('spawn_entity', (packet) => {
const entityData = entityDataByInternalId[packet.type]
const entity = entityData?.type === 'player'
? addNewPlayer(packet.entityId, packet.objectUUID, packet)
: addNewNonPlayer(packet.entityId, packet.objectUUID, packet.type, packet)
bot.emit('entitySpawn', entity)
})
bot._client.on('spawn_entity_experience_orb', (packet) => {
const entity = fetchEntity(packet.entityId)
entity.type = 'orb'
entity.name = 'experience_orb'
entity.width = 0.5
entity.height = 0.5
if (bot.supportFeature('fixedPointPosition')) {
entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32)
} else if (bot.supportFeature('doublePosition')) {
entity.position.set(packet.x, packet.y, packet.z)
}
entity.count = packet.count
bot.emit('entitySpawn', entity)
})
bot._client.on('spawn_entity_living', (packet) => {
const entity = fetchEntity(packet.entityId)
entity.type = 'mob'
entity.uuid = packet.entityUUID
const entityData = mobs[packet.type]
setEntityData(entity, packet.type, entityData)
if (bot.supportFeature('fixedPointPosition')) {
entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32)
} else if (bot.supportFeature('doublePosition')) {
entity.position.set(packet.x, packet.y, packet.z)
}
entity.yaw = conv.fromNotchianYawByte(packet.yaw)
entity.pitch = conv.fromNotchianPitchByte(packet.pitch)
entity.headPitch = conv.fromNotchianPitchByte(packet.headPitch)
const notchVel = new Vec3(packet.velocityX, packet.velocityY, packet.velocityZ)
entity.velocity.update(conv.fromNotchVelocity(notchVel))
entity.metadata = parseMetadata(packet.metadata, entity.metadata)
bot.emit('entitySpawn', entity)
})
bot._client.on('entity_velocity', (packet) => {
const entity = fetchEntity(packet.entityId)
const notchVel = new Vec3(packet.velocityX, packet.velocityY, packet.velocityZ)
entity.velocity.update(conv.fromNotchVelocity(notchVel))
})
bot._client.on('entity_destroy', (packet) => {
packet.entityIds.forEach((id) => {
const entity = fetchEntity(id)
bot.emit('entityGone', entity)
entity.isValid = false
if (entity.username && bot.players[entity.username]) {
bot.players[entity.username].entity = null
}
delete bot.entities[id]
})
})
bot._client.on('rel_entity_move', (packet) => {
const entity = fetchEntity(packet.entityId)
if (bot.supportFeature('fixedPointDelta')) {
entity.position.translate(packet.dX / 32, packet.dY / 32, packet.dZ / 32)
} else if (bot.supportFeature('fixedPointDelta128')) {
entity.position.translate(packet.dX / (128 * 32), packet.dY / (128 * 32), packet.dZ / (128 * 32))
}
bot.emit('entityMoved', entity)
})
bot._client.on('entity_look', (packet) => {
const entity = fetchEntity(packet.entityId)
entity.yaw = conv.fromNotchianYawByte(packet.yaw)
entity.pitch = conv.fromNotchianPitchByte(packet.pitch)
bot.emit('entityMoved', entity)
})
bot._client.on('entity_move_look', (packet) => {
const entity = fetchEntity(packet.entityId)
if (bot.supportFeature('fixedPointDelta')) {
entity.position.translate(packet.dX / 32, packet.dY / 32, packet.dZ / 32)
} else if (bot.supportFeature('fixedPointDelta128')) {
entity.position.translate(packet.dX / (128 * 32), packet.dY / (128 * 32), packet.dZ / (128 * 32))
}
entity.yaw = conv.fromNotchianYawByte(packet.yaw)
entity.pitch = conv.fromNotchianPitchByte(packet.pitch)
bot.emit('entityMoved', entity)
})
bot._client.on('entity_teleport', (packet) => {
const entity = fetchEntity(packet.entityId)
if (bot.supportFeature('fixedPointPosition')) {
entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32)
}
if (bot.supportFeature('doublePosition')) {
entity.position.set(packet.x, packet.y, packet.z)
}
entity.yaw = conv.fromNotchianYawByte(packet.yaw)
entity.pitch = conv.fromNotchianPitchByte(packet.pitch)
bot.emit('entityMoved', entity)
})
bot._client.on('sync_entity_position', (packet) => {
const entity = fetchEntity(packet.entityId)
entity.position.set(packet.x, packet.y, packet.z)
entity.velocity.update(packet.dx, packet.dy, packet.dz)
entity.yaw = packet.yaw
entity.pitch = packet.pitch
bot.emit('entityMoved', entity)
})
bot._client.on('entity_head_rotation', (packet) => {
const entity = fetchEntity(packet.entityId)
entity.headYaw = conv.fromNotchianYawByte(packet.headYaw)
bot.emit('entityMoved', entity)
})
bot._client.on('entity_status', (packet) => {
const entity = fetchEntity(packet.entityId)
const eventName = entityStatusEvents[packet.entityStatus]
if (eventName === 'entityHandSwap' && entity.equipment) {
[entity.equipment[0], entity.equipment[1]] = [entity.equipment[1], entity.equipment[0]]
entity.heldItem = entity.equipment[0]
}
if (eventName) bot.emit(eventName, entity)
})
bot._client.on('damage_event', (packet) => {
const entity = bot.entities[packet.entityId]
const source = bot.entities[packet.sourceCauseId - 1]
bot.emit('entityHurt', entity, source)
})
bot._client.on('attach_entity', (packet) => {
const entity = fetchEntity(packet.entityId)
if (packet.vehicleId === -1) {
const vehicle = entity.vehicle
delete entity.vehicle
bot.emit('entityDetach', entity, vehicle)
} else {
entity.vehicle = fetchEntity(packet.vehicleId)
bot.emit('entityAttach', entity, entity.vehicle)
}
})
bot.fireworkRocketDuration = 0
function setElytraFlyingState (entity, elytraFlying) {
let startedFlying = false
if (elytraFlying) {
startedFlying = !entity.elytraFlying
entity.elytraFlying = true
} else if (entity.elytraFlying) {
entity.elytraFlying = false
}
if (bot.fireworkRocketDuration !== 0 && entity.id === bot.entity?.id && !elytraFlying) {
bot.fireworkRocketDuration = 0
knownFireworks.clear()
}
if (startedFlying) {
bot.emit('entityElytraFlew', entity)
}
}
const knownFireworks = new Set()
function handleBotUsedFireworkRocket (fireworkEntityId, fireworkInfo) {
if (knownFireworks.has(fireworkEntityId)) return
knownFireworks.add(fireworkEntityId)
let flightDur = fireworkInfo?.nbtData?.value?.Fireworks?.value?.Flight.value ?? 1
if (typeof flightDur !== 'number') { flightDur = 1 }
const baseDuration = 10 * (flightDur + 1)
const randomDuration = Math.floor(Math.random() * 6) + Math.floor(Math.random() * 7)
bot.fireworkRocketDuration = baseDuration + randomDuration
bot.emit('usedFirework', fireworkEntityId)
}
let fireworkEntityName
if (bot.supportFeature('fireworkNamePlural')) {
fireworkEntityName = 'fireworks_rocket'
} else if (bot.supportFeature('fireworkNameSingular')) {
fireworkEntityName = 'firework_rocket'
}
let fireworkMetadataIdx
let fireworkMetadataIsOpt
if (bot.supportFeature('fireworkMetadataVarInt7')) {
fireworkMetadataIdx = 7
fireworkMetadataIsOpt = false
} else if (bot.supportFeature('fireworkMetadataOptVarInt8')) {
fireworkMetadataIdx = 8
fireworkMetadataIsOpt = true
} else if (bot.supportFeature('fireworkMetadataOptVarInt9')) {
fireworkMetadataIdx = 9
fireworkMetadataIsOpt = true
}
const hasFireworkSupport = fireworkEntityName !== undefined && fireworkMetadataIdx !== undefined && fireworkMetadataIsOpt !== undefined
bot._client.on('entity_metadata', (packet) => {
const entity = fetchEntity(packet.entityId)
const metadata = parseMetadata(packet.metadata, entity.metadata)
entity.metadata = metadata
bot.emit('entityUpdate', entity)
if (bot.supportFeature('mcDataHasEntityMetadata')) {
const metadataKeys = bot.registry.entitiesByName[entity.name]?.metadataKeys
const metas = metadataKeys ? Object.fromEntries(packet.metadata.map(e => [metadataKeys[e.key], e.value])) : {}
if (packet.metadata.some(m => m.type === 'item_stack')) {
bot.emit('itemDrop', entity)
}
if (metas.sleeping_pos || metas.pose === 2) {
bot.emit('entitySleep', entity)
}
if (hasFireworkSupport && fireworkEntityName === entity.name && metas.attached_to_target !== undefined) {
if (metas.attached_to_target !== 0) {
const entityId = metas.attached_to_target - 1
if (entityId === bot.entity?.id) {
handleBotUsedFireworkRocket(entity.id, metas.fireworks_item)
}
}
}
if (metas.shared_flags != null) {
if (bot.supportFeature('hasElytraFlying')) {
const elytraFlying = metas.shared_flags & 0x80
setElytraFlyingState(entity, Boolean(elytraFlying))
}
if (metas.shared_flags & 2) {
entity.crouching = true
bot.emit('entityCrouch', entity)
} else if (entity.crouching) {
entity.crouching = false
bot.emit('entityUncrouch', entity)
}
}
if (metas.air_supply != null) {
bot.oxygenLevel = Math.round(metas.air_supply / 15)
bot.emit('breath')
}
} else {
const typeSlot = (bot.supportFeature('itemsAreAlsoBlocks') ? 5 : 6) + (bot.supportFeature('entityMetadataHasLong') ? 1 : 0)
const slot = packet.metadata.find(e => e.type === typeSlot)
if (entity.name && (entity.name.toLowerCase() === 'item' || entity.name === 'item_stack') && slot) {
bot.emit('itemDrop', entity)
}
const typePose = bot.supportFeature('entityMetadataHasLong') ? 19 : 18
const pose = packet.metadata.find(e => e.type === typePose)
if (pose && pose.value === 2) {
bot.emit('entitySleep', entity)
}
if (hasFireworkSupport && fireworkEntityName === entity.name) {
const attachedToTarget = packet.metadata.find(e => e.key === fireworkMetadataIdx)
if (attachedToTarget !== undefined) {
let entityId
if (fireworkMetadataIsOpt) {
if (attachedToTarget.value !== 0) {
entityId = attachedToTarget.value - 1
}
} else {
entityId = attachedToTarget.value
}
if (entityId !== undefined && entityId === bot.entity?.id) {
const fireworksItem = packet.metadata.find(e => e.key === (fireworkMetadataIdx - 1))
handleBotUsedFireworkRocket(entity.id, fireworksItem?.value)
}
}
}
const bitField = packet.metadata.find(p => p.key === 0)
if (bitField !== undefined) {
if (bot.supportFeature('hasElytraFlying')) {
const elytraFlying = bitField.value & 0x80
setElytraFlyingState(entity, Boolean(elytraFlying))
}
if ((bitField.value & 2) !== 0) {
entity.crouching = true
bot.emit('entityCrouch', entity)
} else if (entity.crouching) {
entity.crouching = false
bot.emit('entityUncrouch', entity)
}
}
}
})
bot._client.on('entity_effect', (packet) => {
const entity = fetchEntity(packet.entityId)
const effect = {
id: packet.effectId,
amplifier: packet.amplifier,
duration: packet.duration
}
entity.effects[effect.id] = effect
bot.emit('entityEffect', entity, effect)
})
bot._client.on('remove_entity_effect', (packet) => {
const entity = fetchEntity(packet.entityId)
let effect = entity.effects[packet.effectId]
if (effect) {
delete entity.effects[effect.id]
} else {
effect = {
id: packet.effectId,
amplifier: -1,
duration: -1
}
}
bot.emit('entityEffectEnd', entity, effect)
})
const updateAttributes = (packet) => {
const entity = fetchEntity(packet.entityId)
if (!entity.attributes) entity.attributes = {}
for (const prop of packet.properties) {
entity.attributes[prop.key] = {
value: prop.value,
modifiers: prop.modifiers
}
}
bot.emit('entityAttributes', entity)
}
bot._client.on('update_attributes', updateAttributes)
bot._client.on('entity_update_attributes', updateAttributes)
bot._client.on('spawn_entity_weather', (packet) => {
const entity = fetchEntity(packet.entityId)
entity.type = 'global'
entity.globalType = 'thunderbolt'
entity.uuid = packet.entityUUID
entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32)
bot.emit('entitySpawn', entity)
})
bot.on('spawn', () => {
bot.emit('entitySpawn', bot.entity)
})
function handlePlayerInfoBitfield (packet) {
for (const item of packet.data) {
let player = bot._playerFromUUID(item.uuid)
const newPlayer = !player
if (newPlayer) {
player = { uuid: item.uuid }
}
if (packet.action.add_player) {
player.username = item.player.name
player.displayName = new ChatMessage({ text: '', extra: [{ text: item.player.name }] })
player.skinData = extractSkinInformation(item.player.properties)
}
if (packet.action.initialize_chat && item.chatSession) {
player.chatSession = {
publicKey: item.chatSession.publicKey,
sessionUuid: item.chatSession.uuid
}
}
if (packet.action.update_game_mode) {
player.gamemode = item.gamemode
}
if (packet.action.update_listed) {
player.listed = item.listed
}
if (packet.action.update_latency) {
player.ping = item.latency
}
if (packet.action.update_display_name) {
player.displayName = item.displayName ? ChatMessage.fromNotch(item.displayName) : new ChatMessage({ text: '', extra: [{ text: player.username }] })
}
if (newPlayer) {
if (!player.username) continue
bot.players[player.username] = player
bot.uuidToUsername[player.uuid] = player.username
}
const playerEntity = Object.values(bot.entities).find(e => e.type === 'player' && e.username === player.username)
player.entity = playerEntity
if (playerEntity === bot.entity) {
bot.player = player
}
if (newPlayer) {
bot.emit('playerJoined', player)
} else {
bot.emit('playerUpdated', player)
}
}
}
function handlePlayerInfoLegacy (packet) {
for (const item of packet.data) {
let player = bot._playerFromUUID(item.uuid)
switch (packet.action) {
case 'add_player': {
const newPlayer = !player
if (newPlayer) {
player = bot.players[item.name] = {
username: item.name,
uuid: item.uuid
}
bot.uuidToUsername[item.uuid] = item.name
}
player.ping = item.ping
player.gamemode = item.gamemode
player.displayName = item.displayName ? ChatMessage.fromNotch(item.displayName) : new ChatMessage({ text: '', extra: [{ text: item.name }] })
if (item.properties) {
player.skinData = extractSkinInformation(item.properties)
}
if (item.crypto) {
player.profileKeys = {
publicKey: item.crypto.publicKey,
signature: item.crypto.signature
}
}
const playerEntity = Object.values(bot.entities).find(e => e.type === 'player' && e.username === item.name)
player.entity = playerEntity
if (playerEntity === bot.entity) {
bot.player = player
}
if (newPlayer) bot.emit('playerJoined', player)
else bot.emit('playerUpdated', player)
break
}
case 'update_gamemode': {
if (player) {
player.gamemode = item.gamemode
bot.emit('playerUpdated', player)
}
break
}
case 'update_latency': {
if (player) {
player.ping = item.ping
bot.emit('playerUpdated', player)
}
break
}
case 'update_display_name': {
if (player) {
player.displayName = item.displayName ? ChatMessage.fromNotch(item.displayName) : new ChatMessage({ text: '', extra: [{ text: player.username }] })
bot.emit('playerUpdated', player)
}
break
}
case 'remove_player': {
if (player) {
if (player.entity === bot.entity) continue
player.entity = null
delete bot.players[player.username]
delete bot.uuidToUsername[item.uuid]
bot.emit('playerLeft', player)
}
break
}
}
}
}
bot._client.on('player_info', bot.supportFeature('playerInfoActionIsBitfield') ? handlePlayerInfoBitfield : handlePlayerInfoLegacy)
bot._client.on('player_remove', (packet) => {
for (const uuid of packet.players) {
const player = bot._playerFromUUID(uuid)
if (!player || player.entity === bot.entity) continue
player.entity = null
delete bot.players[player.username]
delete bot.uuidToUsername[uuid]
bot.emit('playerLeft', player)
}
})
bot._client.on('attach_entity', (packet) => {
const passenger = fetchEntity(packet.entityId)
const vehicle = packet.vehicleId === -1 ? null : fetchEntity(packet.vehicleId)
const originalVehicle = passenger.vehicle
if (originalVehicle) {
const index = originalVehicle.passengers.indexOf(passenger)
originalVehicle.passengers.splice(index, 1)
}
passenger.vehicle = vehicle
if (vehicle) {
vehicle.passengers.push(passenger)
}
if (packet.entityId === bot.entity.id) {
const vehicle = bot.vehicle
if (packet.vehicleId === -1) {
bot.vehicle = null
bot.emit('dismount', vehicle)
} else {
bot.vehicle = bot.entities[packet.vehicleId]
bot.emit('mount')
}
}
})
bot._client.on('set_passengers', ({ entityId, passengers }) => {
const passengerEntities = passengers.map((passengerId) => fetchEntity(passengerId))
const vehicle = entityId === -1 ? null : bot.entities[entityId]
for (const passengerEntity of passengerEntities) {
const originalVehicle = passengerEntity.vehicle
if (originalVehicle) {
const index = originalVehicle.passengers.indexOf(passengerEntity)
originalVehicle.passengers.splice(index, 1)
}
passengerEntity.vehicle = vehicle
if (vehicle) {
vehicle.passengers.push(passengerEntity)
}
}
if (passengers.includes(bot.entity.id)) {
const originalVehicle = bot.vehicle
if (entityId === -1) {
bot.vehicle = null
bot.emit('dismount', originalVehicle)
} else {
bot.vehicle = bot.entities[entityId]
bot.emit('mount')
}
}
})
bot._client.on('entityGone', (entity) => {
if (bot.vehicle === entity) {
bot.vehicle = null
bot.emit('dismount', (entity))
}
if (entity.passengers) {
for (const passenger of entity.passengers) {
passenger.vehicle = null
}
}
if (entity.vehicle) {
const index = entity.vehicle.passengers.indexOf(entity)
if (index !== -1) {
entity.vehicle.passengers.splice(index, 1)
}
}
})
bot.swingArm = swingArm
bot.attack = attack
bot.mount = mount
bot.dismount = dismount
bot.useOn = useOn
bot.moveVehicle = moveVehicle
function swingArm (arm = 'right', showHand = true) {
const hand = arm === 'right' ? 0 : 1
const packet = {}
if (showHand) packet.hand = hand
bot._client.write('arm_animation', packet)
}
function useOn (target) {
useEntity(target, 0)
}
function attack (target, swing = true) {
if (bot.supportFeature('armAnimationBeforeUse')) {
if (swing) {
swingArm()
}
useEntity(target, 1)
} else {
useEntity(target, 1)
if (swing) {
swingArm()
}
}
}
function mount (target) {
useEntity(target, 0)
}
function moveVehicle (left, forward) {
if (bot.supportFeature('newPlayerInputPacket')) {
bot._client.write('player_input', {
inputs: {
forward: forward > 0,
backward: forward < 0,
left: left > 0,
right: left < 0
}
})
} else {
bot._client.write('steer_vehicle', {
sideways: left,
forward,
jump: 0x01
})
}
}
function dismount () {
if (bot.vehicle) {
if (bot.supportFeature('newPlayerInputPacket')) {
bot._client.write('player_input', {
inputs: {
jump: true
}
})
} else {
bot._client.write('steer_vehicle', {
sideways: 0.0,
forward: 0.0,
jump: 0x02
})
}
} else {
bot.emit('error', new Error('dismount: not mounted'))
}
}
function useEntity (target, leftClick, x, y, z) {
const sneaking = bot.getControlState('sneak')
if (x && y && z) {
bot._client.write('use_entity', {
target: target.id,
mouse: leftClick,
x,
y,
z,
sneaking
})
} else {
bot._client.write('use_entity', {
target: target.id,
mouse: leftClick,
sneaking
})
}
}
function fetchEntity (id) {
return bot.entities[id] || (bot.entities[id] = new Entity(id))
}
}
function parseMetadata (metadata, entityMetadata = {}) {
if (metadata !== undefined) {
for (const { key, value } of metadata) {
entityMetadata[key] = value
}
}
return entityMetadata
}
function extractSkinInformation (properties) {
if (!properties) {
return undefined
}
const props = Object.fromEntries(properties.map((e) => [e.name, e]))
if (!props.textures || !props.textures.value) {
return undefined
}
const skinTexture = JSON.parse(Buffer.from(props.textures.value, 'base64').toString('utf8'))
const skinTextureUrl = skinTexture?.textures?.SKIN?.url ?? undefined
const skinTextureModel = skinTexture?.textures?.SKIN?.metadata?.model ?? undefined
if (!skinTextureUrl) {
return undefined
}
return { url: skinTextureUrl, model: skinTextureModel }
}