mineflayer 3.11.2 → 3.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1 @@
1
1
  # These are supported funding model platforms
2
-
3
- issuehunt: PrismarineJS/mineflayer
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Mineflayer
2
2
 
3
- [![NPM version](https://badge.fury.io/js/mineflayer.svg)](http://badge.fury.io/js/mineflayer)
3
+ [![NPM version](https://badge.fury.io/js/mineflayer.svg)](https://www.npmjs.com/package/mineflayer)
4
4
  [![Build Status](https://github.com/PrismarineJS/mineflayer/workflows/CI/badge.svg)](https://github.com/PrismarineJS/mineflayer/actions?query=workflow%3A%22CI%22)
5
5
  [![Discord](https://img.shields.io/badge/chat-on%20discord-brightgreen.svg)](https://discord.gg/GsEFRM8)
6
6
  [![Issue Hunt](https://github.com/BoostIO/issuehunt-materials/blob/master/v1/issuehunt-shield-v1.svg)](https://issuehunt.io/r/PrismarineJS/mineflayer)
@@ -188,6 +188,8 @@ The most updated and useful are :
188
188
  * [Auto Crystal](https://github.com/link-discord/mineflayer-autocrystal) - Automatic placing & breaking of end crystals.
189
189
  * [Tool](https://github.com/TheDudeFromCI/mineflayer-tool) - A utility for automatic tool/weapon selection with a high level API.
190
190
  * [Hawkeye](https://github.com/sefirosweb/minecraftHawkEye) - A utility for using auto-aim with bows.
191
+ * [GUI](https://github.com/firejoust/mineflayer-GUI) - Eased navigation & management of nested chest-GUI windows
192
+ * [Projectile](https://github.com/firejoust/mineflayer-projectile) - Configurable tool for projectile based combat
191
193
 
192
194
 
193
195
  But also check out :
@@ -202,6 +204,7 @@ The most updated and useful are :
202
204
  * [Bloodhound](https://github.com/Nixes/mineflayer-bloodhound) - determine who and what is responsible for damage to another entity
203
205
  * [tps](https://github.com/SiebeDW/mineflayer-tps) - get the current tps (processed tps)
204
206
  * [panorama](https://github.com/IceTank/mineflayer-panorama) - take Panorama Images of your world
207
+ * [player-death-event](https://github.com/tuanzisama/mineflayer-death-event) - emit player death event in Mineflayer.
205
208
 
206
209
  ## Projects Using Mineflayer
207
210
 
package/docs/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Mineflayer
2
2
 
3
- [![NPM version](https://badge.fury.io/js/mineflayer.svg)](http://badge.fury.io/js/mineflayer)
3
+ [![NPM version](https://badge.fury.io/js/mineflayer.svg)](https://www.npmjs.com/package/mineflayer)
4
4
  [![Build Status](https://github.com/PrismarineJS/mineflayer/workflows/CI/badge.svg)](https://github.com/PrismarineJS/mineflayer/actions?query=workflow%3A%22CI%22)
5
5
  [![Discord](https://img.shields.io/badge/chat-on%20discord-brightgreen.svg)](https://discord.gg/GsEFRM8)
6
6
  [![Issue Hunt](https://github.com/BoostIO/issuehunt-materials/blob/master/v1/issuehunt-shield-v1.svg)](https://issuehunt.io/r/PrismarineJS/mineflayer)
@@ -188,6 +188,8 @@ The most updated and useful are :
188
188
  * [Auto Crystal](https://github.com/link-discord/mineflayer-autocrystal) - Automatic placing & breaking of end crystals.
189
189
  * [Tool](https://github.com/TheDudeFromCI/mineflayer-tool) - A utility for automatic tool/weapon selection with a high level API.
190
190
  * [Hawkeye](https://github.com/sefirosweb/minecraftHawkEye) - A utility for using auto-aim with bows.
191
+ * [GUI](https://github.com/firejoust/mineflayer-GUI) - Eased navigation & management of nested chest-GUI windows
192
+ * [Projectile](https://github.com/firejoust/mineflayer-projectile) - Configurable tool for projectile based combat
191
193
 
192
194
 
193
195
  But also check out :
@@ -202,6 +204,7 @@ The most updated and useful are :
202
204
  * [Bloodhound](https://github.com/Nixes/mineflayer-bloodhound) - determine who and what is responsible for damage to another entity
203
205
  * [tps](https://github.com/SiebeDW/mineflayer-tps) - get the current tps (processed tps)
204
206
  * [panorama](https://github.com/IceTank/mineflayer-panorama) - take Panorama Images of your world
207
+ * [player-death-event](https://github.com/tuanzisama/mineflayer-death-event) - emit player death event in Mineflayer.
205
208
 
206
209
  ## Projects Using Mineflayer
207
210
 
package/docs/api.md CHANGED
@@ -158,7 +158,7 @@
158
158
  - ["weatherUpdate"](#weatherupdate)
159
159
  - ["time"](#time)
160
160
  - ["kicked" (reason, loggedIn)](#kicked-reason-loggedin)
161
- - ["end"](#end)
161
+ - ["end" (reason)](#end-reason)
162
162
  - ["error" (err)](#error-err)
163
163
  - ["spawnReset"](#spawnreset)
164
164
  - ["death"](#death)
@@ -238,6 +238,7 @@
238
238
  - [bot.waitForChunksToLoad(cb)](#botwaitforchunkstoloadcb)
239
239
  - [bot.blockInSight(maxSteps, vectorLength)](#botblockinsightmaxsteps-vectorlength)
240
240
  - [bot.blockAtCursor(maxDistance=256)](#botblockatcursormaxdistance256)
241
+ - [bot.blockAtEntityCursor(entity=bot.entity, maxDistance=256)](#botblockatentitycursorentitybotentity-maxdistance256)
241
242
  - [bot.canSeeBlock(block)](#botcanseeblockblock)
242
243
  - [bot.findBlocks(options)](#botfindblocksoptions)
243
244
  - [bot.findBlock(options)](#botfindblockoptions)
@@ -246,7 +247,7 @@
246
247
  - [bot.recipesAll(itemType, metadata, craftingTable)](#botrecipesallitemtype-metadata-craftingtable)
247
248
  - [bot.nearestEntity(match = (entity) => { return true })](#botnearestentitymatch--entity---return-true-)
248
249
  - [Methods](#methods)
249
- - [bot.end()](#botend)
250
+ - [bot.end(reason)](#botendreason)
250
251
  - [bot.quit(reason)](#botquitreason)
251
252
  - [bot.tabComplete(str, cb, [assumeCommand], [sendBlockInSight])](#bottabcompletestr-cb-assumecommand-sendblockinsight)
252
253
  - [bot.chat(message)](#botchatmessage)
@@ -289,7 +290,7 @@
289
290
  - [bot.activateItem(offHand=false)](#botactivateitemoffhandfalse)
290
291
  - [bot.deactivateItem()](#botdeactivateitem)
291
292
  - [bot.useOn(targetEntity)](#botuseontargetentity)
292
- - [bot.attack(entity)](#botattackentity)
293
+ - [bot.attack(entity, swing = true)](#botattackentity-swing--true)
293
294
  - [bot.swingArm([hand], showHand)](#botswingarmhand-showhand)
294
295
  - [bot.mount(entity)](#botmountentity)
295
296
  - [bot.dismount()](#botdismount)
@@ -756,6 +757,7 @@ Create and return an instance of the class bot.
756
757
  * [difficulty](#bot.settings.difficulty)
757
758
  * [skinParts](#bot.settings.skinParts)
758
759
  * chatLengthLimit : the maximum amount of characters that can be sent in a single message. If this is not set, it will be 100 in < 1.11 and 256 in >= 1.11.
760
+ * defaultChatPatterns: defaults to true, set to false to not add the patterns such as chat and whisper
759
761
 
760
762
  ### Properties
761
763
 
@@ -1154,9 +1156,10 @@ is a chat message explaining why you were kicked. `loggedIn`
1154
1156
  is `true` if the client was kicked after successfully logging in,
1155
1157
  or `false` if the kick occurred in the login phase.
1156
1158
 
1157
- #### "end"
1159
+ #### "end" (reason)
1158
1160
 
1159
1161
  Emitted when you are no longer connected to the server.
1162
+ `reason` is a string explaining why the client was disconnected. (defaults to 'socketClosed')
1160
1163
 
1161
1164
  #### "error" (err)
1162
1165
 
@@ -1448,6 +1451,12 @@ Returns the block at which bot is looking at or `null`
1448
1451
  Returns the block at which bot is looking at or `null`
1449
1452
  * `maxDistance` - The maximum distance the block can be from the eye, defaults to 256.
1450
1453
 
1454
+ #### bot.blockAtEntityCursor(entity=bot.entity, maxDistance=256)
1455
+
1456
+ Returns the block at which specific entity is looking at or `null`
1457
+ * `entity` - Entity data as `Object`
1458
+ * `maxDistance` - The maximum distance the block can be from the eye, defaults to 256.
1459
+
1451
1460
  #### bot.canSeeBlock(block)
1452
1461
 
1453
1462
  Returns true or false depending on whether the bot can see the specified `block`.
@@ -1503,9 +1512,10 @@ const cow = bot.nearestEntity(entity => entity.name.toLowerCase() === 'cow') //
1503
1512
 
1504
1513
  ### Methods
1505
1514
 
1506
- #### bot.end()
1515
+ #### bot.end(reason)
1507
1516
 
1508
1517
  End the connection with the server.
1518
+ * `reason` - Optional string that states the reason of the end.
1509
1519
 
1510
1520
  #### bot.quit(reason)
1511
1521
 
@@ -1853,10 +1863,13 @@ Deactivates the currently held item. This is how you release an arrow, stop eati
1853
1863
  Use the currently held item on an `Entity` instance. This is how you apply a saddle and
1854
1864
  use shears.
1855
1865
 
1856
- #### bot.attack(entity)
1866
+ #### bot.attack(entity, swing = true)
1857
1867
 
1858
1868
  Attack a player or a mob.
1859
1869
 
1870
+ * `entity` is a type of entity. To get a specific entity use [bot.nearestEntity()](#botnearestentitymatch--entity---return-true-) or [bot.entities](#botentities).
1871
+ * `swing` Default `true`. If false the bot does not swing is arm when attacking.
1872
+
1860
1873
  #### bot.swingArm([hand], showHand)
1861
1874
 
1862
1875
  Play an arm swing animation.
package/docs/history.md CHANGED
@@ -1,3 +1,33 @@
1
+ ## 3.14.0
2
+
3
+ * Make prismarine-entity versioned (@u9g)
4
+ * fix(typings): Added OpenContainer (@SaubereSache)
5
+
6
+ ## 3.13.1
7
+
8
+ * Fix bug with force lastSentPitch in bot.look (@KadaverBrutalo10)
9
+ * Fix typo harming type safety (@Eagle-Anne)
10
+
11
+ ## 3.13.0
12
+
13
+ * compute scoreboard displayName dynamically (@U9G)
14
+ * SkinsRestorer fix (@U5B)
15
+ * Fix bot not swinging arm on block place (@IceTank)
16
+
17
+ ## 3.12.0
18
+
19
+ * Bypass anticheats that detect sensitivity (@mat-1)
20
+ * Fix removing many players at once from tab list (@mat-1)
21
+ * Added blockAtEntityCursor function (@DatArnoGuy)
22
+ * add option to disable default chat patterns (@U5B)
23
+ * Fixed wrong arm swinging (@IceTank)
24
+ * Add pitch speed to look (@IceTank)
25
+ * Console spam fix (@IceTank)
26
+ * Update openVillager function to return a promise (@amoraschi)
27
+ * Send arm_animation before use_entity (@aesthetic0001)
28
+ * Add reason for the end of a mineflayer bot (@U5B)
29
+ * added rejection of invalid transaction packets (anticheat fix) (@U5B)
30
+
1
31
  ## 3.11.2
2
32
  * Remove unnecessary and buggy inventory check in place block (@Karang)
3
33
  * Make all events allow async cb typings (@u9g)
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * This example is a very simple way how to connect a discord bot with a mineflayer bot.
3
3
  * For this example you will need discord.js installed. You can install with: npm install discord.js
4
- * Note that discord.js v12 or newer is required.
4
+ * This example uses discord.js v13
5
5
  * You need to do this before running this example:
6
6
  * - You need to get a discord token
7
7
  * - You need to get the id of the channel you want to use
@@ -12,9 +12,18 @@ if (process.argv.length < 6 || process.argv.length > 8) {
12
12
  process.exit(1)
13
13
  }
14
14
 
15
- // Load discord
16
- const Discord = require('discord.js')
17
- const client = new Discord.Client()
15
+ // Load discord.js
16
+ const {
17
+ Client,
18
+ Intents
19
+ } = require('discord.js')
20
+ // Create Discord intentions, required in v13
21
+ const intents = new Intents(['GUILDS', 'GUILD_MESSAGES'])
22
+ // Create Discord client
23
+ const client = new Client({
24
+ intents: intents
25
+ })
26
+
18
27
  let channel = process.argv[3]
19
28
 
20
29
  // Load mineflayer
@@ -28,6 +37,7 @@ const bot = mineflayer.createBot({
28
37
 
29
38
  client.on('ready', () => {
30
39
  console.log(`The discord bot logged in! Username: ${client.user.username}!`)
40
+ // Find the Discord channel messages will be sent to
31
41
  channel = client.channels.cache.get(channel)
32
42
  if (!channel) {
33
43
  console.log(`I could not find the channel (${process.argv[3]})!\nUsage : node discord.js <discord bot token> <channel id> <host> <port> [<name>] [<password>]`)
@@ -36,7 +46,7 @@ client.on('ready', () => {
36
46
  })
37
47
 
38
48
  // Redirect Discord messages to in-game chat
39
- client.on('message', message => {
49
+ client.on('messageCreate', message => {
40
50
  // Only handle messages in specified channel
41
51
  if (message.channel.id !== channel.id) return
42
52
  // Ignore messages from the bot itself
@@ -21,7 +21,7 @@ def handle(*args):
21
21
  movements = pathfinder.Movements(bot, mcData)
22
22
 
23
23
  @On(bot, 'chat')
24
- def handleMsg(sender, message, *args):
24
+ def handleMsg(this, sender, message, *args):
25
25
  print("Got message", sender, message)
26
26
  if sender and (sender != BOT_USERNAME):
27
27
  bot.chat('Hi, you said ' + message)
@@ -39,4 +39,4 @@ def handle(*args):
39
39
 
40
40
  @On(bot, "end")
41
41
  def handle(*args):
42
- print("Bot ended!", args)
42
+ print("Bot ended!", args)
@@ -13,7 +13,7 @@
13
13
  # a few informations while you are in game.
14
14
  # ===========================================================================
15
15
  import sys, re
16
- from javascript import require, On, Once
16
+ from javascript import require, On, Once, console
17
17
 
18
18
  mineflayer = require("mineflayer", "latest")
19
19
  Vec3 = require("vec3").Vec3
package/index.d.ts CHANGED
@@ -26,6 +26,7 @@ export interface BotOptions extends ClientOptions {
26
26
  physicsEnabled?: boolean
27
27
  client?: Client
28
28
  brand?: string
29
+ defaultChatPatterns?: boolean
29
30
  }
30
31
 
31
32
  export type ChatLevel = 'enabled' | 'commandsOnly' | 'disabled'
@@ -67,7 +68,7 @@ interface BotEvents {
67
68
  rain: () => Promise<void> | void
68
69
  time: () => Promise<void> | void
69
70
  kicked: (reason: string, loggedIn: boolean) => Promise<void> | void
70
- end: () => Promise<void> | void
71
+ end: (reason: string) => Promise<void> | void
71
72
  spawnReset: () => Promise<void> | void
72
73
  death: () => Promise<void> | void
73
74
  health: () => Promise<void> | void
@@ -129,8 +130,8 @@ interface BotEvents {
129
130
  forcedMove: () => Promise<void> | void
130
131
  mount: () => Promise<void> | void
131
132
  dismount: (vehicle: Entity) => Promise<void> | void
132
- windowOpen: (vehicle: Window) => Promise<void> | void
133
- windowClose: (vehicle: Window) => Promise<void> | void
133
+ windowOpen: (window: Window) => Promise<void> | void
134
+ windowClose: (window: Window) => Promise<void> | void
134
135
  sleep: () => Promise<void> | void
135
136
  wake: () => Promise<void> | void
136
137
  experience: () => Promise<void> | void
@@ -193,13 +194,14 @@ export interface Bot extends TypedEmitter<BotEvents> {
193
194
 
194
195
  supportFeature: (feature: string) => boolean
195
196
 
196
- end: () => void
197
+ end: (reason?: string) => void
197
198
 
198
199
  blockAt: (point: Vec3) => Block | null
199
200
 
200
201
  blockInSight: (maxSteps: number, vectorLength: number) => Block | null
201
202
 
202
203
  blockAtCursor: (maxDistance?: number, matcher?: Function) => Block | null
204
+ blockAtEntityCursor: (entity?: Entity, maxDistance?: number, matcher?: Function) => Block | null
203
205
 
204
206
  canSeeBlock: (block: Block) => boolean
205
207
 
@@ -341,6 +343,8 @@ export interface Bot extends TypedEmitter<BotEvents> {
341
343
  callback?: (err?: Error) => void
342
344
  ) => Promise<void>
343
345
 
346
+ openContainer: (chest: Block | Entity) => Promise<Chest | Furnace | Dispenser>
347
+
344
348
  openChest: (chest: Block | Entity) => Promise<Chest>
345
349
 
346
350
  openFurnace: (furnace: Block) => Promise<Furnace>
@@ -353,7 +357,7 @@ export interface Bot extends TypedEmitter<BotEvents> {
353
357
 
354
358
  openVillager: (
355
359
  villager: Entity
356
- ) => Villager
360
+ ) => Promise<Villager>
357
361
 
358
362
  trade: (
359
363
  villagerInstance: Villager,
@@ -831,7 +835,7 @@ export class BossBar {
831
835
  );
832
836
  }
833
837
 
834
- export var supportedVersions: string[]
835
- export var testedVersions: string[]
838
+ export let supportedVersions: string[]
839
+ export let testedVersions: string[]
836
840
 
837
841
  export function supportFeature (feature: string, version: string): boolean
package/lib/loader.js CHANGED
@@ -70,7 +70,7 @@ function createBot (options = {}) {
70
70
  options.brand = options.brand ?? 'vanilla'
71
71
  const bot = new EventEmitter()
72
72
  bot._client = options.client
73
- bot.end = () => bot._client.end()
73
+ bot.end = (reason) => bot._client.end(reason)
74
74
  if (options.logErrors) {
75
75
  bot.on('error', err => {
76
76
  if (!options.hideErrors) {
@@ -99,8 +99,8 @@ function createBot (options = {}) {
99
99
  bot._client.on('error', (err) => {
100
100
  bot.emit('error', err)
101
101
  })
102
- bot._client.on('end', () => {
103
- bot.emit('end')
102
+ bot._client.on('end', (reason) => {
103
+ bot.emit('end', reason)
104
104
  })
105
105
  if (!bot._client.wait_connect) next()
106
106
  else bot._client.once('connect_allowed', next)
@@ -476,6 +476,7 @@ function inject (bot, { version, storageBuilder }) {
476
476
  // if we get a respawn packet and the dimension is changed,
477
477
  // unload all chunks from memory.
478
478
  let dimension
479
+ let worldName
479
480
  function dimensionToFolderName (dimension) {
480
481
  if (bot.supportFeature('dimensionIsAnInt')) {
481
482
  return dimensionNames[dimension]
@@ -511,13 +512,26 @@ function inject (bot, { version, storageBuilder }) {
511
512
  }
512
513
 
513
514
  bot._client.on('login', (packet) => {
514
- dimension = packet.dimension
515
+ if (bot.supportFeature('dimensionIsAnInt')) {
516
+ dimension = packet.dimension
517
+ } else {
518
+ dimension = packet.dimension
519
+ worldName = packet.worldName
520
+ }
515
521
  switchWorld()
516
522
  })
517
523
 
518
524
  bot._client.on('respawn', (packet) => {
519
- if (dimension === packet.dimension) return
520
- dimension = packet.dimension
525
+ if (bot.supportFeature('dimensionIsAnInt')) { // <=1.15.2
526
+ if (dimension === packet.dimension) return
527
+ dimension = packet.dimension
528
+ } else { // >= 1.15.2
529
+ if (dimension === packet.dimension) return
530
+ if (worldName === packet.worldName && packet.copyMetadata === true) return // don't unload chunks if in same world and metaData is true
531
+ // Metadata is true when switching dimensions however, then the world name is different
532
+ dimension = packet.dimension
533
+ worldName = packet.worldName
534
+ }
521
535
  switchWorld()
522
536
  })
523
537
 
@@ -4,6 +4,7 @@ module.exports = inject
4
4
 
5
5
  function inject (bot, options) {
6
6
  const CHAT_LENGTH_LIMIT = options.chatLengthLimit ?? (bot.supportFeature('lessCharsInChat') ? 100 : 256)
7
+ const defaultChatPatterns = options.defaultChatPatterns ?? true
7
8
 
8
9
  const ChatMessage = require('prismarine-chat')(bot.version)
9
10
  // chat.pattern.type will emit an event for bot.on() of the same type, eg chatType = whisper will trigger bot.on('whisper')
@@ -171,6 +172,7 @@ function inject (bot, options) {
171
172
  bot.tabComplete = callbackify(tabComplete)
172
173
 
173
174
  function addDefaultPatterns () {
175
+ if (!defaultChatPatterns) return
174
176
  const USERNAME_REGEX = '(?:\\(.+\\)|\\[.+\\]|.)*?(\\w+)'
175
177
  bot.addChatPattern('whisper', new RegExp(`^${USERNAME_REGEX} whispers(?: to you)?:? (.*)$`), { deprecated: true })
176
178
  bot.addChatPattern('whisper', new RegExp(`^\\[${USERNAME_REGEX} -> \\w+\\s?\\] (.*)$`), { deprecated: true })
@@ -1,5 +1,4 @@
1
1
  const { Vec3 } = require('vec3')
2
- const Entity = require('prismarine-entity')
3
2
  const conv = require('../conversions')
4
3
  const NAMED_ENTITY_HEIGHT = 1.62
5
4
  const NAMED_ENTITY_WIDTH = 0.6
@@ -26,6 +25,7 @@ const entityStatusEvents = {
26
25
  }
27
26
 
28
27
  function inject (bot, { version }) {
28
+ const Entity = require('prismarine-entity')(version)
29
29
  const objects = require('minecraft-data')(version).objects
30
30
  const mobs = require('minecraft-data')(version).mobs
31
31
  const entitiesArray = require('minecraft-data')(version).entitiesArray
@@ -89,7 +89,11 @@ function inject (bot, { version }) {
89
89
  return best
90
90
  }
91
91
 
92
- bot._client.once('login', (packet) => {
92
+ // Reset list of players and entities on login
93
+ bot._client.on('login', (packet) => {
94
+ bot.players = {}
95
+ bot.uuidToUsername = {}
96
+ bot.entities = {}
93
97
  // login
94
98
  bot.entity = fetchEntity(packet.entityId)
95
99
  bot.username = bot._client.username
@@ -483,15 +487,15 @@ function inject (bot, { version }) {
483
487
  } else if (packet.action === 3 && item.displayName) {
484
488
  player.displayName = new ChatMessage(JSON.parse(item.displayName))
485
489
  } else if (packet.action === 4) {
486
- if (player.entity === bot.entity) return
490
+ if (player.entity === bot.entity) continue
487
491
 
488
492
  player.entity = null
489
493
  delete bot.players[player.username]
490
494
  delete bot.uuidToUsername[item.UUID]
491
495
  bot.emit('playerLeft', player)
492
- return
496
+ continue
493
497
  } else {
494
- return
498
+ continue
495
499
  }
496
500
 
497
501
  bot.emit('playerUpdated', player)
@@ -531,8 +535,8 @@ function inject (bot, { version }) {
531
535
  bot.useOn = useOn
532
536
  bot.moveVehicle = moveVehicle
533
537
 
534
- function swingArm (arm = 'left', showHand = true) {
535
- const hand = arm === 'left' ? 0 : 1
538
+ function swingArm (arm = 'right', showHand = true) {
539
+ const hand = arm === 'right' ? 0 : 1
536
540
  const packet = {}
537
541
  if (showHand) packet.hand = hand
538
542
  bot._client.write('arm_animation', packet)
@@ -544,11 +548,11 @@ function inject (bot, { version }) {
544
548
  }
545
549
 
546
550
  function attack (target, swing = true) {
547
- useEntity(target, 1)
548
-
551
+ // arm animation comes before the use_entity packet
549
552
  if (swing) {
550
553
  swingArm()
551
554
  }
555
+ useEntity(target, 1)
552
556
  }
553
557
 
554
558
  function mount (target) {
@@ -90,4 +90,11 @@ function inject (bot, options) {
90
90
  bot._client.on(brandChannel, (serverBrand) => {
91
91
  bot.game.serverBrand = serverBrand
92
92
  })
93
+
94
+ // mimic the vanilla 1.17 client to prevent anticheat kicks
95
+ bot._client.on('ping', (data) => {
96
+ bot._client.write('pong', {
97
+ id: data.id
98
+ })
99
+ })
93
100
  }
@@ -7,7 +7,7 @@ function inject (bot, { version }) {
7
7
  *
8
8
  * @param {import('prismarine-block').Block} referenceBlock
9
9
  * @param {import('vec3').Vec3} faceVector
10
- * @param {{half?: 'top'|'bottom', delta?: import('vec3').Vec3, forceLook?: boolean | 'ignore', offhand?: boolean}} options
10
+ * @param {{half?: 'top'|'bottom', delta?: import('vec3').Vec3, forceLook?: boolean | 'ignore', offhand?: boolean, swingArm?: 'right' | 'left', showHand?: boolean}} options
11
11
  */
12
12
  async function _genericPlace (referenceBlock, faceVector, options) {
13
13
  let handToPlaceWith = 0
@@ -39,6 +39,10 @@ function inject (bot, { version }) {
39
39
  // TODO: tell the server that we are sneaking while doing this
40
40
  const pos = referenceBlock.position
41
41
 
42
+ if (options.swingArm) {
43
+ bot.swingArm(options.swingArm, options.showHand)
44
+ }
45
+
42
46
  if (bot.supportFeature('blockPlaceHasHeldItem')) {
43
47
  const packet = {
44
48
  location: pos,
@@ -427,13 +427,24 @@ function inject (bot, { version, hideErrors }) {
427
427
  function confirmTransaction (windowId, actionId, accepted) {
428
428
  // drop the queue entries for all the clicks that the server did not send
429
429
  // transaction packets for.
430
- let click = windowClickQueue.shift()
431
- if (click === undefined) {
430
+ // Also reject transactions that aren't sent from mineflayer
431
+ let click = windowClickQueue[0]
432
+ if (click === undefined || !windowClickQueue.some(clicks => clicks.id === actionId)) {
433
+ // mimic vanilla client and send a rejection for faulty transaction packets
434
+ bot._client.write('transaction', {
435
+ windowId: windowId,
436
+ action: actionId,
437
+ accepted: true
438
+ // bot.emit(`confirmTransaction${click.id}`, false)
439
+ })
432
440
  if (!hideErrors) {
433
441
  console.log(`WARNING : unknown transaction confirmation for window ${windowId}, action ${actionId} and accepted ${accepted}`)
434
442
  }
435
443
  return
436
444
  }
445
+ // shift it later if packets are sent out of order
446
+ click = windowClickQueue.shift()
447
+
437
448
  assert.ok(click.id <= actionId)
438
449
  while (actionId > click.id) {
439
450
  onAccepted()
@@ -534,7 +545,7 @@ function inject (bot, { version, hideErrors }) {
534
545
  }
535
546
  const [success] = await response
536
547
  if (!success) {
537
- throw new Error(`Server rejected transaction for clicking on slot ${slot}, on window ${JSON.stringify(window.slots, null, 2)}.`)
548
+ throw new Error(`Server rejected transaction for clicking on slot ${slot}, on window with id ${window?.id}.`)
538
549
  }
539
550
  } else {
540
551
  await waitForWindowUpdate(window, slot)
@@ -564,9 +575,6 @@ function inject (bot, { version, hideErrors }) {
564
575
 
565
576
  bot._client.on('transaction', (packet) => {
566
577
  // confirm transaction
567
- if (packet.action < 0) {
568
- return
569
- }
570
578
  confirmTransaction(packet.windowId, packet.action, packet.accepted)
571
579
  })
572
580
 
@@ -33,6 +33,7 @@ function inject (bot, { physicsEnabled }) {
33
33
  sneak: false
34
34
  }
35
35
  let lastSentYaw = null
36
+ let lastSentPitch = null
36
37
  let doPhysicsTimer = null
37
38
  let lastPhysicsFrameTime = null
38
39
  let shouldUsePhysics = false
@@ -122,13 +123,16 @@ function inject (bot, { physicsEnabled }) {
122
123
 
123
124
  // Increment the yaw in baby steps so that notchian clients (not the server) can keep up.
124
125
  const dYaw = deltaYaw(bot.entity.yaw, lastSentYaw)
126
+ const dPitch = bot.entity.pitch - (lastSentPitch || 0)
125
127
 
126
128
  // Vanilla doesn't clamp yaw, so we don't want to do it either
127
129
  const maxDeltaYaw = dt * physics.yawSpeed
130
+ const maxDeltaPitch = dt * physics.pitchSpeed
128
131
  lastSentYaw += math.clamp(-maxDeltaYaw, dYaw, maxDeltaYaw)
132
+ lastSentPitch += math.clamp(-maxDeltaPitch, dPitch, maxDeltaPitch)
129
133
 
130
- const yaw = conv.toNotchianYaw(lastSentYaw)
131
- const pitch = conv.toNotchianPitch(bot.entity.pitch)
134
+ const yaw = Math.fround(conv.toNotchianYaw(lastSentYaw))
135
+ const pitch = Math.fround(conv.toNotchianPitch(lastSentPitch))
132
136
  const position = bot.entity.position
133
137
  const onGround = bot.entity.onGround
134
138
 
@@ -225,10 +229,18 @@ function inject (bot, { physicsEnabled }) {
225
229
  }
226
230
  lookingTask = createTask()
227
231
 
228
- bot.entity.yaw = yaw
229
- bot.entity.pitch = pitch
232
+ // this is done to bypass certain anticheat checks that detect the player's sensitivity
233
+ // by calculating the gcd of how much they move the mouse each tick
234
+ const sensitivity = conv.fromNotchianPitch(0.15) // this is equal to 100% sensitivity in vanilla
235
+ const yawChange = Math.round((yaw - bot.entity.yaw) / sensitivity) * sensitivity
236
+ const pitchChange = Math.round((pitch - bot.entity.pitch) / sensitivity) * sensitivity
237
+
238
+ bot.entity.yaw += yawChange
239
+ bot.entity.pitch += pitchChange
240
+
230
241
  if (force) {
231
242
  lastSentYaw = yaw
243
+ lastSentPitch = pitch
232
244
  return
233
245
  }
234
246
 
@@ -21,7 +21,7 @@ function inject (bot) {
21
21
  }
22
22
 
23
23
  async function placeBlock (referenceBlock, faceVector) {
24
- await placeBlockWithOptions(referenceBlock, faceVector, {})
24
+ await placeBlockWithOptions(referenceBlock, faceVector, { swingArm: 'right' })
25
25
  }
26
26
 
27
27
  bot.placeBlock = callbackify(placeBlock)
@@ -5,6 +5,12 @@ module.exports = inject
5
5
  function inject (bot, { version }) {
6
6
  const Item = require('prismarine-item')(version)
7
7
 
8
+ /**
9
+ *
10
+ * @param {import('prismarine-block').Block} referenceBlock
11
+ * @param {import('vec3').Vec3} faceVector
12
+ * @param {{forceLook?: boolean | 'ignore', offhand?: boolean, swingArm?: 'right' | 'left', showHand?: boolean}} options
13
+ */
8
14
  async function placeEntityWithOptions (referenceBlock, faceVector, options) {
9
15
  if (!bot.heldItem) throw new Error('must be holding an item to place an entity')
10
16
 
@@ -20,14 +26,14 @@ function inject (bot, { version }) {
20
26
  name = bot.heldItem.spawnEggMobName
21
27
  }
22
28
 
23
- const pos = await bot._genericPlace(referenceBlock, faceVector, options)
24
-
25
29
  if (type === 'spawn_egg') {
26
- bot.swingArm(undefined, false)
27
- } else {
28
- bot.swingArm()
30
+ options.showHand = false
29
31
  }
30
32
 
33
+ if (!options.swingArm) options.swingArm = options.offhand ? 'left' : 'right'
34
+
35
+ const pos = await bot._genericPlace(referenceBlock, faceVector, options)
36
+
31
37
  if (type === 'boat') {
32
38
  if (bot.supportFeature('useItemWithOwnPacket')) {
33
39
  bot._client.write('use_item', {
@@ -23,4 +23,14 @@ module.exports = (bot) => {
23
23
 
24
24
  return bot.world.raycast(eyePosition, viewDirection, maxDistance, matcher)
25
25
  }
26
+
27
+ bot.blockAtEntityCursor = (entity = bot.entity, maxDistance = 256, matcher = null) => {
28
+ if (!entity.position || !entity.height || !entity.pitch || !entity.yaw) return null
29
+ const { position, height, pitch, yaw } = entity
30
+
31
+ const eyePosition = position.offset(0, height, 0)
32
+ const viewDirection = getViewDirection(pitch, yaw)
33
+
34
+ return bot.world.raycast(eyePosition, viewDirection, maxDistance, matcher)
35
+ }
26
36
  }
@@ -1,9 +1,7 @@
1
- const ScoreBoard = require('../scoreboard')
2
-
3
1
  module.exports = inject
4
2
 
5
3
  function inject (bot) {
6
- const ChatMessage = require('prismarine-chat')(bot.version)
4
+ const ScoreBoard = require('../scoreboard')(bot)
7
5
  const scoreboards = {}
8
6
 
9
7
  bot._client.on('scoreboard_objective', (packet) => {
@@ -29,12 +27,7 @@ function inject (bot) {
29
27
  bot._client.on('scoreboard_score', (packet) => {
30
28
  const scoreboard = scoreboards[packet.scoreName]
31
29
  if (scoreboard !== undefined && packet.action === 0) {
32
- const { itemName, value } = packet
33
- let displayName = new ChatMessage(itemName)
34
- if (itemName in bot.teamMap) {
35
- displayName = bot.teamMap[itemName].displayName(itemName)
36
- }
37
- const updated = scoreboard.add(itemName, value, displayName)
30
+ const updated = scoreboard.add(packet.itemName, packet.value)
38
31
  bot.emit('scoreUpdated', scoreboard, updated)
39
32
  }
40
33
 
@@ -118,7 +118,6 @@ function inject (bot, { version }) {
118
118
  if (!ready) {
119
119
  ready = true
120
120
  villager.emit('ready')
121
- console.log('emit ready')
122
121
  }
123
122
  }
124
123
  }
package/lib/scoreboard.js CHANGED
@@ -4,49 +4,62 @@ const sortItems = (a, b) => {
4
4
  return 1
5
5
  }
6
6
 
7
- class ScoreBoard {
8
- constructor (packet) {
9
- this.name = packet.name
10
- this.setTitle(packet.displayText)
11
- this.itemsMap = {}
12
- }
7
+ module.exports = (bot) => {
8
+ const ChatMessage = require('prismarine-chat')(bot.version)
13
9
 
14
- setTitle (title) {
15
- try {
16
- this.title = JSON.parse(title).text // version>1.13
17
- } catch {
18
- this.title = title
10
+ class ScoreBoard {
11
+ constructor (packet) {
12
+ this.name = packet.name
13
+ this.setTitle(packet.displayText)
14
+ this.itemsMap = {}
19
15
  }
20
- }
21
16
 
22
- add (name, value, displayName) {
23
- this.itemsMap[name] = { name, value, displayName }
24
- return this.itemsMap[name]
25
- }
17
+ setTitle (title) {
18
+ try {
19
+ this.title = JSON.parse(title).text // version>1.13
20
+ } catch {
21
+ this.title = title
22
+ }
23
+ }
26
24
 
27
- remove (name) {
28
- const removed = this.itemsMap[name]
29
- delete this.itemsMap[name]
30
- return removed
31
- }
25
+ add (name, value) {
26
+ this.itemsMap[name] = { name, value }
27
+ this.itemsMap[name] = {
28
+ name,
29
+ value,
30
+ get displayName () {
31
+ if (name in bot.teamMap) {
32
+ return bot.teamMap[name].displayName(name)
33
+ }
34
+ return new ChatMessage(name)
35
+ }
36
+ }
37
+ return this.itemsMap[name]
38
+ }
32
39
 
33
- get items () {
34
- return Object.values(this.itemsMap).sort(sortItems)
40
+ remove (name) {
41
+ const removed = this.itemsMap[name]
42
+ delete this.itemsMap[name]
43
+ return removed
44
+ }
45
+
46
+ get items () {
47
+ return Object.values(this.itemsMap).sort(sortItems)
48
+ }
35
49
  }
36
- }
37
50
 
38
- ScoreBoard.positions = {
39
- get list () {
40
- return this[0]
41
- },
51
+ ScoreBoard.positions = {
52
+ get list () {
53
+ return this[0]
54
+ },
42
55
 
43
- get sidebar () {
44
- return this[1]
45
- },
56
+ get sidebar () {
57
+ return this[1]
58
+ },
46
59
 
47
- get belowName () {
48
- return this[2]
60
+ get belowName () {
61
+ return this[2]
62
+ }
49
63
  }
64
+ return ScoreBoard
50
65
  }
51
-
52
- module.exports = ScoreBoard
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mineflayer",
3
- "version": "3.11.2",
3
+ "version": "3.14.0",
4
4
  "description": "create minecraft bots with a stable, high level API",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -21,31 +21,32 @@
21
21
  },
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
- "minecraft-data": "^2.79.0",
25
- "minecraft-protocol": "^1.17.0",
26
- "prismarine-biome": "^1.1.0",
27
- "prismarine-block": "^1.6.0",
28
- "prismarine-chat": "^1.1.0",
29
- "prismarine-chunk": "^1.24.0",
30
- "prismarine-entity": "^1.0.0",
31
- "prismarine-item": "^1.6.0",
32
- "prismarine-physics": "^1.0.4",
24
+ "minecraft-data": "^2.95.0",
25
+ "minecraft-protocol": "^1.26.5",
26
+ "prismarine-biome": "^1.1.1",
27
+ "prismarine-block": "^1.10.3",
28
+ "prismarine-chat": "^1.3.3",
29
+ "prismarine-chunk": "^1.26.0",
30
+ "prismarine-entity": "^2.0.0",
31
+ "prismarine-item": "^1.11.0",
32
+ "prismarine-nbt": "^2.0.0",
33
+ "prismarine-physics": "^1.3.1",
33
34
  "prismarine-recipe": "^1.1.0",
34
- "prismarine-windows": "^2.3.0",
35
+ "prismarine-windows": "^2.4.2",
35
36
  "prismarine-world": "^3.6.0",
36
- "protodef": "^1.8.0",
37
- "typed-emitter": "^1.2.0",
38
- "vec3": "^0.1.6"
37
+ "protodef": "^1.14.0",
38
+ "typed-emitter": "^1.3.1",
39
+ "vec3": "^0.1.7"
39
40
  },
40
41
  "devDependencies": {
41
- "@types/node": "^16.0.1",
42
- "doctoc": "^2.0.0",
43
- "minecraft-wrap": "^1.2.1",
42
+ "@types/node": "^17.0.0",
43
+ "doctoc": "^2.0.1",
44
+ "minecraft-wrap": "^1.3.0",
44
45
  "mineflayer": "file:.",
45
- "mocha": "^9.0.0",
46
- "standard": "^16.0.1",
47
- "standard-markdown": "^7.0.0",
48
- "ts-standard": "^10.0.0",
49
- "typescript": "^4.3.2"
46
+ "mocha": "^9.1.2",
47
+ "standard": "^16.0.4",
48
+ "standard-markdown": "^7.1.0",
49
+ "ts-standard": "^11.0.0",
50
+ "typescript": "^4.4.3"
50
51
  }
51
52
  }