Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/test/internalTest.js
Views: 789
/* eslint-env mocha */12const mineflayer = require('../')3const vec3 = require('vec3')4const mc = require('minecraft-protocol')5const assert = require('assert')6const { sleep } = require('../lib/promise_utils')7const nbt = require('prismarine-nbt')89for (const supportedVersion of mineflayer.testedVersions) {10const registry = require('prismarine-registry')(supportedVersion)11const version = registry.version12const Chunk = require('prismarine-chunk')(supportedVersion)13const isNewPlayerInfoFormat = registry.version['>=']('1.21.3')14function wrapPlayerInfo (n) {15if (isNewPlayerInfoFormat) {16return {17_value: n,18add_player: (n & 1) !== 0,19initialize_chat: (n & 2) !== 0,20update_game_mode: (n & 4) !== 0,21update_listed: (n & 8) !== 0,22update_latency: (n & 16) !== 0,23update_display_name: (n & 32) !== 0,24update_priority: (n & 64) !== 025}26}27return n28}2930const hasSignedChat = registry.supportFeature('signedChat')31function chatText (text) {32// TODO: move this to prismarine-chat in a new ChatMessage(text).toNotch(asNbt) method33return registry.supportFeature('chatPacketsUseNbtComponents')34? nbt.comp({ text: nbt.string(text) })35: JSON.stringify({ text })36}3738function generateChunkPacket (chunk) {39const lights = chunk.dumpLight()40return {41x: 0,42z: 0,43groundUp: true,44biomes: chunk.dumpBiomes !== undefined ? chunk.dumpBiomes() : undefined,45heightmaps: {46type: 'compound',47name: '',48value: {49MOTION_BLOCKING: { type: 'longArray', value: new Array(36).fill([0, 0]) }50}51}, // send fake heightmap52bitMap: chunk.getMask(),53chunkData: chunk.dump(),54blockEntities: [],55trustEdges: false,56skyLightMask: lights?.skyLightMask,57blockLightMask: lights?.blockLightMask,58emptySkyLightMask: lights?.emptySkyLightMask,59emptyBlockLightMask: lights?.emptyBlockLightMask,60skyLight: lights?.skyLight,61blockLight: lights?.blockLight62}63}6465describe(`mineflayer_internal ${supportedVersion}v`, function () {66this.timeout(10 * 1000)67let bot68let server69beforeEach((done) => {70server = mc.createServer({71'online-mode': false,72version: supportedVersion,73// 25565 - local server, 25566 - proxy server74port: 2556775})76server.on('listening', () => {77bot = mineflayer.createBot({78username: 'player',79version: supportedVersion,80port: 2556781})82bot.test = {}8384bot.test.buildChunk = () => {85if (bot.supportFeature('tallWorld')) {86return new Chunk({ minY: -64, worldHeight: 384 })87} else {88return new Chunk()89}90}9192bot.test.generateLoginPacket = () => {93let loginPacket94if (bot.supportFeature('usesLoginPacket')) {95loginPacket = registry.loginPacket96loginPacket.entityId = 0 // Default login packet in minecraft-data 1.16.5 is 1, so set it to 097} else {98loginPacket = {99entityId: 0,100levelType: 'fogetaboutit',101gameMode: 0,102previousGameMode: 255,103worldNames: ['minecraft:overworld'],104dimension: 0,105worldName: 'minecraft:overworld',106hashedSeed: [0, 0],107difficulty: 0,108maxPlayers: 20,109reducedDebugInfo: 1,110enableRespawnScreen: true111}112}113return loginPacket114}115done()116})117})118afterEach((done) => {119bot.on('end', () => {120done()121})122server.close()123})124it('chat', (done) => {125bot.once('chat', (username, message) => {126assert.strictEqual(username, 'gary')127assert.strictEqual(message, 'hello')128bot.chat('hi')129})130server.on('playerJoin', (client) => {131client.write('login', bot.test.generateLoginPacket())132const message = hasSignedChat133? JSON.stringify({ text: 'hello' })134: JSON.stringify({135translate: 'chat.type.text',136with: [{137text: 'gary'138},139'hello'140]141})142143if (hasSignedChat) {144const uuid = 'd3527a0b-bc03-45d5-a878-2aafdd8c8a43' // random145const networkName = chatText('gary')146147if (registry.supportFeature('incrementedChatType')) {148client.write('player_chat', {149plainMessage: 'hello',150filterType: 0,151type: { registryIndex: 1 },152networkName,153previousMessages: [],154senderUuid: uuid,155timestamp: Date.now(),156index: 0,157salt: 0n158})159} else if (registry.supportFeature('useChatSessions')) {160client.write('player_chat', {161plainMessage: 'hello',162filterType: 0,163type: 0,164networkName,165previousMessages: [],166senderUuid: uuid,167timestamp: Date.now(),168index: 0,169salt: 0n170})171} else if (registry.supportFeature('chainedChatWithHashing')) {172client.write('player_chat', {173plainMessage: 'hello',174filterType: 0,175type: 0,176networkName,177previousMessages: [],178senderUuid: uuid,179timestamp: Date.now(),180salt: 0n,181signature: Buffer.alloc(0)182})183} else {184client.write('player_chat', {185signedChatContent: '',186unsignedChatContent: message,187type: 0,188senderUuid: uuid,189senderName: JSON.stringify({ text: 'gary' }),190senderTeam: undefined,191timestamp: Date.now(),192salt: 0n,193signature: Buffer.alloc(0)194})195}196} else {197client.write('chat', { message, position: 0, sender: '0' })198}199function onChat (packet) {200const msg = packet.message || packet.unsignedChatContent || packet.signedChatContent201assert.strictEqual(msg, 'hi')202done()203}204client.on('chat_message', onChat)205client.on('chat', onChat)206})207})208it('entity effects', (done) => {209bot.once('entityEffect', (entity, effect) => {210assert.strictEqual(entity.id, 8)211assert.strictEqual(effect.id, 10)212assert.strictEqual(effect.amplifier, 1)213assert.strictEqual(effect.duration, 11)214done()215})216// Versions prior to 1.11 have capital first letter217const entities = bot.registry.entitiesByName218const creeperId = entities.creeper ? entities.creeper.id : entities.Creeper.id219server.on('playerJoin', (client) => {220client.write(bot.registry.supportFeature('consolidatedEntitySpawnPacket') ? 'spawn_entity' : 'spawn_entity_living', {221entityId: 8, // random222entityUUID: '00112233-4455-6677-8899-aabbccddeeff',223objectUUID: '00112233-4455-6677-8899-aabbccddeeff',224type: creeperId,225x: 10,226y: 11,227z: 12,228yaw: 13,229pitch: 14,230headPitch: 14,231velocityX: 16,232velocityY: 17,233velocityZ: 18,234metadata: []235})236client.write('entity_effect', {237entityId: 8,238effectId: 10,239amplifier: 1,240duration: 11,241hideParticles: false242})243})244})245it('blockAt', (done) => {246const pos = vec3(1, 65, 1)247const goldId = bot.registry.blocksByName.gold_block.id248bot.on('chunkColumnLoad', (columnPoint) => {249assert.strictEqual(columnPoint.x, 0)250assert.strictEqual(columnPoint.z, 0)251assert.strictEqual(bot.blockAt(pos).type, goldId)252done()253})254server.on('playerJoin', (client) => {255client.write('login', bot.test.generateLoginPacket())256const chunk = bot.test.buildChunk()257chunk.setBlockType(pos, goldId)258client.write('map_chunk', generateChunkPacket(chunk))259})260})261262describe('physics', () => {263const pos = vec3(1, 65, 1)264const pos2 = vec3(2, 65, 1)265const goldId = 41266it('no physics if there is no chunk', (done) => {267let fail = 0268const basePosition = {269x: 1.5,270y: 66,271z: 1.5,272dx: 0, // 1.21.3273dy: 0, // 1.21.3274dz: 0, // 1.21.3275pitch: 0,276yaw: 0,277flags: bot.registry.version['>=']('1.21.3') ? {} : 0,278teleportId: 0279}280server.on('playerJoin', async (client) => {281await client.write('login', bot.test.generateLoginPacket())282await client.write('position', basePosition)283client.on('packet', (data, meta) => {284const packetName = meta.name285switch (packetName) {286case 'position':287fail++288break289case 'position_look':290fail++291break292case 'look':293fail++294break295}296if (fail > 1) assert.fail('position packet sent')297})298await sleep(2000)299done()300})301})302it('absolute position & relative position (velocity)', (done) => {303server.on('playerJoin', async (client) => {304await client.write('login', bot.test.generateLoginPacket())305const chunk = await bot.test.buildChunk()306307await chunk.setBlockType(pos, goldId)308await chunk.setBlockType(pos2, goldId)309await client.write('map_chunk', generateChunkPacket(chunk))310let check = true311let absolute = true312const basePosition = {313x: 1.5,314y: 66,315z: 1.5,316pitch: 0,317yaw: 0,318flags: 0,319teleportId: 0320}321client.on('packet', (data, meta) => {322const packetName = meta.name323switch (packetName) {324case 'teleport_confirm': {325assert.ok(basePosition.teleportId === data.teleportId)326break327}328case 'position_look': {329if (!check) return330if (absolute) {331assert.ok(bot.entity.velocity.y === 0)332} else {333assert.ok(bot.entity.velocity.y !== 0)334}335assert.ok(basePosition.x === data.x)336assert.ok(basePosition.y === data.y)337assert.ok(basePosition.z === data.z)338assert.ok(basePosition.yaw === data.yaw)339assert.ok(basePosition.pitch === data.pitch)340check = false341break342}343default:344break345}346})347// Absolute Position Tests348// absolute position test349check = true350await client.write('position', basePosition)351await bot.waitForTicks(5)352// absolute position test 2353basePosition.x = 2.5354basePosition.teleportId = 1355await bot.waitForTicks(1)356check = true357await client.write('position', basePosition)358await bot.waitForTicks(2)359// absolute position test 3360basePosition.x = 1.5361basePosition.teleportId = 2362await bot.waitForTicks(1)363check = true364await client.write('position', basePosition)365await bot.waitForTicks(2)366367// Relative Position Tests368const relativePosition = {369x: 1,370y: 0,371z: 0,372dx: 0, // 1.21.3373dy: 0, // 1.21.3374dz: 0, // 1.21.3375pitch: 0,376yaw: 0,377flags: bot.registry.version['>=']('1.21.3')378? {379// flags = ["x", "y", "z", "yaw", "pitch", "dx", "dy", "dz", "yawDelta"]380// 31 = 0b11111381x: true,382y: true,383z: true,384yaw: true,385pitch: true386}387: 31,388teleportId: 3389}390absolute = false391// relative position test 1392basePosition.x = 2.5393basePosition.teleportId = 3394relativePosition.x = 1395relativePosition.teleportId = 3396await bot.waitForTicks(1)397check = true398await client.write('position', relativePosition)399await bot.waitForTicks(2)400// relative position test 2401basePosition.x = 1.5402basePosition.teleportId = 4403relativePosition.x = -1404relativePosition.teleportId = 4405await bot.waitForTicks(1)406check = true407await client.write('position', relativePosition)408await bot.waitForTicks(2)409// relative position test 3410basePosition.x = 2.5411basePosition.teleportId = 5412relativePosition.x = 1413relativePosition.teleportId = 5414await bot.waitForTicks(1)415check = true416await client.write('position', relativePosition)417await bot.waitForTicks(2)418done()419})420})421it('gravity + land on solid block + jump', (done) => {422let y = 80423let landed = false424bot.on('move', () => {425if (landed) return426assert.ok(bot.entity.position.y <= y)427assert.ok(bot.entity.position.y >= pos.y)428y = bot.entity.position.y429if (bot.entity.position.y <= pos.y + 1) {430assert.strictEqual(bot.entity.position.y, pos.y + 1)431assert.strictEqual(bot.entity.onGround, true)432landed = true433done()434} else {435assert.strictEqual(bot.entity.onGround, false)436}437})438server.on('playerJoin', (client) => {439client.write('login', bot.test.generateLoginPacket())440const chunk = bot.test.buildChunk()441442chunk.setBlockType(pos, goldId)443client.write('map_chunk', generateChunkPacket(chunk))444client.write('position', {445x: 1.5,446y: 80,447z: 1.5,448pitch: 0,449yaw: 0,450flags: 0,451teleportId: 0452})453})454})455})456457describe('world', () => {458const pos = vec3(1, 65, 1)459const goldId = 41460it('switchWorld respawn', (done) => {461const loginPacket = bot.test.generateLoginPacket()462let respawnPacket463if (bot.supportFeature('usesLoginPacket')) {464loginPacket.worldName = 'minecraft:overworld'465loginPacket.hashedSeed = [0, 0]466loginPacket.entityId = 0467respawnPacket = {468// 1.19+ the `dimension` filed is a string in respawn packet and undefined in login packet, in previous versions it's same NBT data in login/respawn469dimension: bot.supportFeature('dimensionDataInCodec') ? 'minecraft:overworld' : loginPacket.dimension,470worldName: loginPacket.worldName,471hashedSeed: loginPacket.hashedSeed,472gamemode: 0,473previousGamemode: 255,474isDebug: false,475isFlat: false,476copyMetadata: true,477death: {478dimensionName: '',479location: {480x: 0,481y: 0,482z: 0483}484}485}486if (bot.supportFeature('spawnRespawnWorldDataField')) {487respawnPacket = {488worldState: respawnPacket489}490respawnPacket.worldState.name = loginPacket.worldName491respawnPacket.worldState.dimension = loginPacket.dimension492}493} else {494respawnPacket = {495dimension: 0,496hashedSeed: [0, 0],497gamemode: 0,498levelType: 'default'499}500}501const chunk = bot.test.buildChunk()502chunk.setBlockType(pos, goldId)503const chunkPacket = generateChunkPacket(chunk)504const positionPacket = {505x: 1.5,506y: 80,507z: 1.5,508pitch: 0,509yaw: 0,510flags: 0,511teleportId: 0512}513server.on('playerJoin', async (client) => {514bot.once('respawn', () => {515assert.ok(bot.world.getColumn(0, 0) !== undefined)516bot.once('respawn', () => {517assert.ok(bot.world.getColumn(0, 0) === undefined)518done()519})520if (bot.supportFeature('spawnRespawnWorldDataField')) {521respawnPacket.worldState.name = 'minecraft:nether'522} else {523respawnPacket.worldName = 'minecraft:nether'524}525if (bot.supportFeature('spawnRespawnWorldDataField')) {526respawnPacket.worldState.dimension = 1527} else if (bot.supportFeature('usesLoginPacket')) {528respawnPacket.dimension.name = 'e'529} else {530respawnPacket.dimension = 1531}532client.write('respawn', respawnPacket)533})534await client.write('login', loginPacket)535await client.write('map_chunk', chunkPacket)536await client.write('position', positionPacket)537await client.write('update_health', {538health: 20,539food: 20,540foodSaturation: 0541})542await bot.waitForTicks(1)543await client.write('respawn', respawnPacket)544})545})546})547548describe('game', () => {549it('responds to ping / transaction packets', (done) => { // only on 1.17550server.on('playerJoin', async (client) => {551if (bot.supportFeature('transactionPacketExists')) {552const transactionPacket = { windowId: 0, action: 42, accepted: false }553client.once('transaction', (data, meta) => {554assert.ok(meta.name === 'transaction')555assert.ok(data.action === 42)556assert.ok(data.accepted === true)557done()558})559client.write('transaction', transactionPacket)560} else {561client.once('pong', (data) => {562assert(data.id === 42)563done()564})565client.write('ping', { id: 42 })566}567})568})569})570571describe('entities', () => {572it('entity id changes on login', (done) => {573const loginPacket = bot.test.generateLoginPacket()574server.on('playerJoin', (client) => {575if (bot.supportFeature('usesLoginPacket')) {576loginPacket.entityId = 0 // Default login packet in minecraft-data 1.16.5 is 1, so set it to 0577}578client.write('login', loginPacket)579bot.once('login', () => {580assert.ok(bot.entity.id === 0)581loginPacket.entityId = 42582bot.once('login', () => {583assert.ok(bot.entity.id === 42)584done()585})586client.write('login', loginPacket)587})588})589})590591it('player displayName', (done) => {592server.on('playerJoin', (client) => {593bot.on('entitySpawn', (entity) => {594const player = bot.players[entity.username]595assert.strictEqual(player.username, entity.username)596// TODO: this test is broken as it updates the display name twice (once with, once without)597if (!isNewPlayerInfoFormat) assert.strictEqual(entity.username, player.displayName.toString())598if (registry.supportFeature('playerInfoActionIsBitfield')) {599client.write('player_info', {600action: wrapPlayerInfo(53),601data: [{602uuid: '1-2-3-4',603player: {604name: 'bot5',605properties: []606},607gamemode: 0,608latency: 0,609displayName: chatText('wvffle')610}]611})612} else {613client.write('player_info', {614id: 56,615state: 'play',616action: 3,617length: 1,618data: [{619UUID: '1-2-3-4',620name: 'bot5',621propertiesLength: 0,622properties: [],623gamemode: 0,624ping: 0,625hasDisplayName: true,626displayName: chatText('wvffle')627}]628})629}630})631632bot.once('playerUpdated', (player) => {633assert.strictEqual('wvffle', player.displayName.toString())634if (registry.supportFeature('playerInfoActionIsBitfield')) {635client.write('player_info', {636action: wrapPlayerInfo(53),637data: [{638uuid: '1-2-3-4',639player: {640name: 'bot5',641properties: []642},643gamemode: 0,644latency: 0645}]646})647} else {648client.write('player_info', {649id: 56,650state: 'play',651action: 3,652length: 1,653data: [{654UUID: '1-2-3-4',655name: 'bot5',656propertiesLength: 0,657properties: [],658gamemode: 0,659ping: 0,660hasDisplayName: false661}]662})663}664665bot.once('playerUpdated', (player) => {666// TODO: this test is broken as it updates the display name twice (once with, once without)667if (!isNewPlayerInfoFormat) assert.strictEqual(player.entity.username, player.displayName.toString())668done()669})670})671672if (registry.supportFeature('playerInfoActionIsBitfield')) {673client.write('player_info', {674action: wrapPlayerInfo(53),675data: [{676uuid: '1-2-3-4',677player: {678name: 'bot5',679properties: []680},681gamemode: 0,682latency: 0683}]684})685} else {686client.write('player_info', {687id: 56,688state: 'play',689action: 0,690length: 1,691data: [{692UUID: '1-2-3-4',693name: 'bot5',694propertiesLength: 0,695properties: [],696gamemode: 0,697ping: 0,698hasDisplayName: false699}]700})701}702703if (bot.registry.supportFeature('unifiedPlayerAndEntitySpawnPacket')) {704client.write('spawn_entity', {705entityId: 56,706objectUUID: '1-2-3-4',707type: bot.registry.entitiesByName.player.internalId,708x: 1,709y: 2,710z: 3,711pitch: 0,712yaw: 0,713headPitch: 0,714objectData: 1,715velocityX: 0,716velocityY: 0,717velocityZ: 0718})719} else {720client.write('named_entity_spawn', {721entityId: 56,722playerUUID: '1-2-3-4',723x: 1,724y: 2,725z: 3,726yaw: 0,727pitch: 0,728currentItem: -1,729metadata: []730})731}732})733})734735it('sets players[player].entity to null upon despawn', (done) => {736let serverClient = null737bot.once('entitySpawn', (entity) => {738if (bot.version !== '1.17') {739serverClient.write('entity_destroy', {740entityIds: [8]741})742} else {743serverClient.write('destroy_entity', {744entityIds: 8745})746}747})748bot.once('entityGone', (entity) => {749assert.strictEqual(bot.players[entity.username], undefined)750done()751})752server.on('playerJoin', (client) => {753serverClient = client754755if (registry.supportFeature('playerInfoActionIsBitfield')) {756client.write('player_info', {757action: wrapPlayerInfo(53),758data: [{759uuid: '1-2-3-4',760player: {761name: 'bot5',762properties: []763},764gamemode: 0,765latency: 0766}]767})768} else {769client.write('player_info', {770id: 56,771state: 'play',772action: 0,773length: 1,774data: [{775UUID: '1-2-3-4',776name: 'bot5',777propertiesLength: 0,778properties: [],779gamemode: 0,780ping: 0,781hasDisplayName: false782}]783})784}785786if (bot.registry.supportFeature('unifiedPlayerAndEntitySpawnPacket')) {787client.write('spawn_entity', {788entityId: 56,789objectUUID: '1-2-3-4',790type: bot.registry.entitiesByName.player.internalId,791x: 1,792y: 2,793z: 3,794pitch: 0,795yaw: 0,796headPitch: 0,797objectData: 1,798velocityX: 0,799velocityY: 0,800velocityZ: 0801})802} else {803client.write('named_entity_spawn', {804entityId: 56,805playerUUID: '1-2-3-4',806x: 1,807y: 2,808z: 3,809yaw: 0,810pitch: 0,811currentItem: -1,812metadata: []813})814}815})816})817818it('metadata', (done) => {819server.on('playerJoin', (client) => {820bot.on('entitySpawn', (entity) => {821assert.strictEqual(entity.displayName, 'Creeper')822823const lastMeta = entity.metadata824bot.on('entityUpdate', (entity) => {825assert.ok('0' in entity.metadata)826assert.strictEqual(entity.metadata[0], 1)827assert.strictEqual(entity.metadata[1], lastMeta[1])828done()829})830831client.write('entity_metadata', {832entityId: 8,833metadata: [834{ key: 0, type: bot.registry.supportFeature('mcDataHasEntityMetadata') ? 'int' : 0, value: 1 }835]836})837})838839// Versions prior to 1.11 have capital first letter840const entities = bot.registry.entitiesByName841const creeperId = entities.creeper ? entities.creeper.id : entities.Creeper.id842client.write(bot.registry.supportFeature('consolidatedEntitySpawnPacket') ? 'spawn_entity' : 'spawn_entity_living', {843entityId: 8, // random844entityUUID: '00112233-4455-6677-8899-aabbccddeeff',845objectUUID: '00112233-4455-6677-8899-aabbccddeeff',846type: creeperId,847x: 10,848y: 11,849z: 12,850yaw: 13,851pitch: 14,852headPitch: 14,853velocityX: 16,854velocityY: 17,855velocityZ: 18,856metadata: [857{ type: 0, key: bot.registry.supportFeature('mcDataHasEntityMetadata') ? 'byte' : 0, value: 0 },858{ type: 0, key: bot.registry.supportFeature('mcDataHasEntityMetadata') ? 'int' : 1, value: 1 }859]860})861})862})863864it('\'itemDrop\' event', function (done) {865const itemData = {866itemId: 149,867itemCount: 5868}869870server.on('playerJoin', (client) => {871bot.on('itemDrop', (entity) => {872const slotPosition = metadataPacket.metadata[0].key873874if (bot.supportFeature('itemsAreAlsoBlocks')) {875assert.strictEqual(entity.metadata[slotPosition].blockId, itemData.itemId)876} else if (bot.supportFeature('itemsAreNotBlocks')) {877assert.strictEqual(entity.metadata[slotPosition].itemId, itemData.itemId)878}879assert.strictEqual(entity.metadata[slotPosition].itemCount, itemData.itemCount)880881done()882})883884let entityType885if (['1.8', '1.9', '1.10', '1.11', '1.12'].includes(bot.majorVersion)) {886entityType = 2887} else {888entityType = bot.registry.entitiesArray.find(e => e.name.toLowerCase() === 'item' || e.name.toLowerCase() === 'item_stack').id889}890client.write('spawn_entity', {891entityId: 16,892objectUUID: '00112233-4455-6677-8899-aabbccddeeff',893type: Number(entityType),894x: 0,895y: 0,896z: 0,897pitch: 0,898yaw: 0,899headPitch: 0,900objectData: 1,901velocityX: 0,902velocityY: 0,903velocityZ: 0904})905906const metadataPacket = {907entityId: 16,908metadata: [909{ key: 7, type: 6, value: { itemCount: itemData.itemCount } }910]911}912// Versions prior to 1.13 use 5 as type field value of metadata for storing a slot. 1.13 and so on, use 6913// Also the structure of a slot changes from 1.12 to 1.13914if (bot.supportFeature('itemsAreAlsoBlocks')) {915metadataPacket.metadata[0].key = 6916metadataPacket.metadata[0].type = 5917metadataPacket.metadata[0].value.blockId = itemData.itemId918metadataPacket.metadata[0].value.itemDamage = 0919} else if (bot.supportFeature('itemsAreNotBlocks')) {920if (bot.majorVersion === '1.13') metadataPacket.metadata[0].key = 6921metadataPacket.metadata[0].value.itemId = itemData.itemId922metadataPacket.metadata[0].value.present = true923}924925if (bot.supportFeature('entityMetadataHasLong')) {926metadataPacket.metadata[0].type = 7927}928929if (bot.registry.supportFeature('mcDataHasEntityMetadata')) {930metadataPacket.metadata[0].type = 'item_stack'931}932metadataPacket.metadata[0].value.addedComponentCount = 0933metadataPacket.metadata[0].value.removedComponentCount = 0934metadataPacket.metadata[0].value.components = []935metadataPacket.metadata[0].value.removeComponents = []936937client.write('entity_metadata', metadataPacket)938})939})940})941942it('bed', (done) => {943const blocks = bot.registry.blocksByName944const entities = bot.registry.entitiesByName945946const playerPos = vec3(10, 0, 0)947const zombiePos = vec3(0, 0, 0)948const beds = [949{ head: vec3(10, 0, 3), foot: vec3(10, 0, 2), facing: 2, throws: false },950{ head: vec3(9, 0, 4), foot: vec3(10, 0, 4), facing: 3, throws: true, error: new Error('the bed is too far') },951{ head: vec3(8, 0, 0), foot: vec3(8, 0, 1), facing: 0, throws: true, error: new Error('there are monsters nearby') },952{ head: vec3(12, 0, 0), foot: vec3(11, 0, 0), facing: 1, throws: false }953]954955const zombieId = entities.zombie ? entities.zombie.id : entities.Zombie.id956let bedBlock957if (bot.supportFeature('oneBlockForSeveralVariations', version.majorVersion)) {958bedBlock = blocks.bed959} else if (bot.supportFeature('blockSchemeIsFlat', version.majorVersion)) {960bedBlock = blocks.red_bed961}962const bedId = bedBlock.id963964bot.once('chunkColumnLoad', (columnPoint) => {965for (const bed in beds) {966const bedBock = bot.blockAt(beds[bed].foot)967const bedBockMetadata = bot.parseBedMetadata(bedBock)968assert.strictEqual(bedBockMetadata.facing, beds[bed].facing, 'The facing property seems to be wrong')969assert.strictEqual(bedBockMetadata.part, false, 'The part property seems to be wrong') // Is the foot970971if (beds[bed].throws) {972bot.sleep(bedBock).catch(err => assert.strictEqual(err, beds[bed].error))973} else {974bot.sleep(bedBock).catch(err => assert.ifError(err))975}976}977978done()979})980981server.once('playerJoin', (client) => {982bot.time.timeOfDay = 18000983const loginPacket = bot.test.generateLoginPacket()984client.write('login', loginPacket)985986const chunk = bot.test.buildChunk()987988for (const bed in beds) {989chunk.setBlockType(beds[bed].head, bedId)990chunk.setBlockType(beds[bed].foot, bedId)991}992993if (bot.supportFeature('blockStateId', version.majorVersion)) {994chunk.setBlockStateId(beds[0].foot, 3 + bedBlock.minStateId) // { facing: north, occupied: false, part: foot }995chunk.setBlockStateId(beds[0].head, 2 + bedBlock.minStateId) // { facing:north, occupied: false, part: head }996997chunk.setBlockStateId(beds[1].foot, 15 + bedBlock.minStateId) // { facing: east, occupied:false, part:foot }998chunk.setBlockStateId(beds[1].head, 14 + bedBlock.minStateId) // { facing: east, occupied: false, part: head }9991000chunk.setBlockStateId(beds[2].foot, 7 + bedBlock.minStateId) // { facing: south, occupied: false, part: foot }1001chunk.setBlockStateId(beds[2].head, 6 + bedBlock.minStateId) // { facing: south, occupied: false, part: head }10021003chunk.setBlockStateId(beds[3].foot, 11 + bedBlock.minStateId) // { facing: west, occupied: false, part: foot }1004chunk.setBlockStateId(beds[3].head, 10 + bedBlock.minStateId) // { facing: west, occupied: false, part: head }1005} else if (bot.supportFeature('blockMetadata', version.majorVersion)) {1006chunk.setBlockData(beds[0].foot, 2) // { facing: north, occupied: false, part: foot }1007chunk.setBlockData(beds[0].head, 10) // { facing:north, occupied: false, part: head }10081009chunk.setBlockData(beds[1].foot, 3) // { facing: east, occupied:false, part:foot }1010chunk.setBlockData(beds[1].head, 11) // { facing: east, occupied: false, part: head }10111012chunk.setBlockData(beds[2].foot, 0) // { facing: south, occupied: false, part: foot }1013chunk.setBlockData(beds[2].head, 8) // { facing: south, occupied: false, part: head }10141015chunk.setBlockData(beds[3].foot, 1) // { facing: west, occupied: false, part: foot }1016chunk.setBlockData(beds[3].head, 9) // { facing: west, occupied: false, part: head }1017}10181019client.write('position', {1020x: playerPos.x,1021y: playerPos.y,1022z: playerPos.z,1023yaw: 0,1024pitch: 0,1025flags: 0,1026teleportId: 11027})10281029client.write(bot.registry.supportFeature('consolidatedEntitySpawnPacket') ? 'spawn_entity' : 'spawn_entity_living', {1030entityId: 8,1031entityUUID: '00112233-4455-6677-8899-aabbccddeeff',1032objectUUID: '00112233-4455-6677-8899-aabbccddeeff',1033type: zombieId,1034x: zombiePos.x,1035y: zombiePos.y,1036z: zombiePos.z,1037yaw: 0,1038pitch: 0,1039headPitch: 0,1040velocityX: 0,1041velocityY: 0,1042velocityZ: 0,1043metadata: []1044})10451046client.write('map_chunk', generateChunkPacket(chunk))1047})1048})10491050describe('tablist', () => {1051it('handles newlines in header and footer', (done) => {1052const HEADER = 'asd\ndsa'1053const FOOTER = '\nas\nas\nas\n'1054bot._client.on('playerlist_header', (packet) => {1055setImmediate(() => {1056assert.strictEqual(bot.tablist.header.toString(), HEADER)1057assert.strictEqual(bot.tablist.footer.toString(), FOOTER)1058done()1059})1060})1061// TODO: figure out how the "extra" should be encoded in NBT so this branch can be removed1062if (registry.supportFeature('chatPacketsUseNbtComponents')) {1063server.on('playerJoin', (client) => {1064client.write('playerlist_header', {1065header: chatText(HEADER),1066footer: chatText(FOOTER)1067})1068})1069} else {1070server.on('playerJoin', (client) => {1071client.write('playerlist_header', {1072header: JSON.stringify({ text: '', extra: [{ text: HEADER, color: 'yellow' }] }),1073footer: JSON.stringify({ text: '', extra: [{ text: FOOTER, color: 'yellow' }] })1074})1075})1076}1077})1078})1079})1080}108110821083