mineflayer 4.19.0 → 4.20.1

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.
package/README.md CHANGED
@@ -17,7 +17,7 @@ First time using Node.js? You may want to start with the [tutorial](tutorial.md)
17
17
 
18
18
  ## Features
19
19
 
20
- * Supports Minecraft 1.8 to 1.20.2 (1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19 and 1.20)
20
+ * Supports Minecraft 1.8 to 1.20.4 (1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19 and 1.20)
21
21
  * Entity knowledge and tracking.
22
22
  * Block knowledge. You can query the world around you. Milliseconds to find any block.
23
23
  * Physics and movement - handle all bounding boxes
package/docs/README.md CHANGED
@@ -17,7 +17,7 @@ First time using Node.js? You may want to start with the [tutorial](tutorial.md)
17
17
 
18
18
  ## Features
19
19
 
20
- * Supports Minecraft 1.8 to 1.20.2 (1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19 and 1.20)
20
+ * Supports Minecraft 1.8 to 1.20.4 (1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19 and 1.20)
21
21
  * Entity knowledge and tracking.
22
22
  * Block knowledge. You can query the world around you. Milliseconds to find any block.
23
23
  * Physics and movement - handle all bounding boxes
package/docs/api.md CHANGED
@@ -1691,6 +1691,8 @@ the event will be called `"chat:name"`, with name being the name passed
1691
1691
 
1692
1692
  returns a number which can be used with bot.removeChatPattern() to only delete this pattern
1693
1693
 
1694
+ - :eyes: cf. [examples/chat_parsing](https://github.com/PrismarineJS/mineflayer/blob/master/examples/chat_parsing.js#L17-L36)
1695
+
1694
1696
  #### bot.addChatPatternSet(name, patterns, chatPatternOptions)
1695
1697
 
1696
1698
  make an event that is called every time all patterns havee been matched to messages,
@@ -1703,6 +1705,8 @@ the event will be called `"chat:name"`, with name being the name passed
1703
1705
 
1704
1706
  returns a number which can be used with bot.removeChatPattern() to only delete this patternset
1705
1707
 
1708
+ - :eyes: cf. [examples/chat_parsing](https://github.com/PrismarineJS/mineflayer/blob/master/examples/chat_parsing.js#L17-L36)
1709
+
1706
1710
  #### bot.removeChatPattern(name)
1707
1711
 
1708
1712
  removes a chat pattern(s)
package/docs/history.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 4.20.1
2
+ * [Add bossBarCreated event in index.d.ts (#3340)](https://github.com/PrismarineJS/mineflayer/commit/8299288526cd7ff24bcd87511814221f8ad62507) (thanks @gguio)
3
+ * [Update scoreboard.js (#3318)](https://github.com/PrismarineJS/mineflayer/commit/195b3cbd70a110080af9b77a4659991c5d9e484a) (thanks @vicdum)
4
+ * [Fix hardcoded diggingface for cancel digging (#3322)](https://github.com/PrismarineJS/mineflayer/commit/ab78bf855929a476386b5eb6efcf3b271d02455e) (thanks @Vinciepincie)
5
+ * [Fix 1.20.4 server resource pack error (#3320)](https://github.com/PrismarineJS/mineflayer/commit/7c01eeb970647ed2933c10cb2b94fd7b44c777f5) (thanks @TerminalCalamitas)
6
+ * [Fix scoreboard delete handler not first checking if scoreboard exists (#3324)](https://github.com/PrismarineJS/mineflayer/commit/d9e9e15aeb646d81da2a3e2987566de47e3bae04) (thanks @Ynfuien)
7
+
8
+ ## 4.20.0
9
+ * [Update api.md - addChatPattern[Set] link to example of usage (#3304)](https://github.com/PrismarineJS/mineflayer/commit/bb3e5877b7b3b8ab063b39a5b47d103b819da1c2) (thanks @boly38)
10
+ * [Fixed deleted scoreboards not being removed from ScoreBoard.positions (#3306)](https://github.com/PrismarineJS/mineflayer/commit/643023df91bf428d3e7d30e8f2eab97e3238b0b2) (thanks @Ynfuien)
11
+ * [Support 1.20.4 (#3310)](https://github.com/PrismarineJS/mineflayer/commit/aa99daa7d63ee9549f2dda5a79c140e30e19a89b) (thanks @rom1504)
12
+
1
13
  ## 4.19.0
2
14
  * [Clarify readme createBot username handling (#3300)](https://github.com/PrismarineJS/mineflayer/commit/7a2680bc07f53d16626679537ea1f07aae180549) (thanks @extremeheat)
3
15
  * [fix world typing (#3302)](https://github.com/PrismarineJS/mineflayer/commit/5dc36d6cdeaf4be72ea023827d45b9d78e575f66) (thanks @GenerelSchwerz)
package/index.d.ts CHANGED
@@ -158,9 +158,10 @@ export interface BotEvents {
158
158
  teamUpdated: (team: Team) => Promise<void> | void
159
159
  teamMemberAdded: (team: Team) => Promise<void> | void
160
160
  teamMemberRemoved: (team: Team) => Promise<void> | void
161
+ bossBarCreated: (bossBar: BossBar) => Promise<void> | void
161
162
  bossBarDeleted: (bossBar: BossBar) => Promise<void> | void
162
163
  bossBarUpdated: (bossBar: BossBar) => Promise<void> | void
163
- resourcePack: (url: string, hash: string) => Promise<void> | void
164
+ resourcePack: (url: string, hash?: string, uuid?: string) => Promise<void> | void
164
165
  particle: (particle: Particle) => Promise<void> | void
165
166
  }
166
167
 
package/lib/bossbar.js CHANGED
@@ -1,101 +1,98 @@
1
- let ChatMessage
2
1
  const colors = ['pink', 'blue', 'red', 'green', 'yellow', 'purple', 'white']
3
2
  const divisions = [0, 6, 10, 12, 20]
4
3
 
5
- module.exports = loader
6
-
7
4
  function loader (registry) {
8
- ChatMessage = require('prismarine-chat')(registry)
9
- return BossBar
10
- }
11
-
12
- class BossBar {
13
- constructor (uuid, title, health, dividers, color, flags) {
14
- this._entityUUID = uuid
15
- this._title = new ChatMessage(JSON.parse(title))
16
- this._health = health
17
- this._dividers = divisions[dividers]
18
- this._color = colors[color]
19
- this._shouldDarkenSky = flags & 0x1
20
- this._isDragonBar = flags & 0x2
21
- this._createFog = flags & 0x4
22
- }
23
-
24
- set entityUUID (uuid) {
25
- this._entityUUID = uuid
26
- }
27
-
28
- set title (title) {
29
- this._title = new ChatMessage(JSON.parse(title))
30
- }
31
-
32
- set health (health) {
33
- this._health = health
34
- }
35
-
36
- set dividers (dividers) {
37
- this._dividers = divisions[dividers]
38
- }
39
-
40
- set color (color) {
41
- this._color = colors[color]
42
- }
43
-
44
- set flags (flags) {
45
- this._shouldDarkenSky = flags & 0x1
46
- this._isDragonBar = flags & 0x2
47
- this._createFog = flags & 0x4
48
- }
49
-
50
- get flags () {
51
- return (this._shouldDarkenSky) | (this._isDragonBar << 1) | (this._createFog << 2)
52
- }
53
-
54
- set shouldDarkenSky (darkenSky) {
55
- this._shouldDarkenSky = darkenSky
56
- }
57
-
58
- set isDragonBar (dragonBar) {
59
- this._isDragonBar = dragonBar
60
- }
61
-
62
- get createFog () {
63
- return this._createFog
64
- }
65
-
66
- set createFog (createFog) {
67
- this._createFog = createFog
68
- }
69
-
70
- get entityUUID () {
71
- return this._entityUUID
72
- }
73
-
74
- get title () {
75
- return this._title
76
- }
77
-
78
- get health () {
79
- return this._health
80
- }
81
-
82
- get dividers () {
83
- return this._dividers
84
- }
85
-
86
- get color () {
87
- return this._color
88
- }
89
-
90
- get shouldDarkenSky () {
91
- return this._shouldDarkenSky
92
- }
93
-
94
- get isDragonBar () {
95
- return this._isDragonBar
96
- }
97
-
98
- get shouldCreateFog () {
99
- return this._createFog
5
+ const ChatMessage = require('prismarine-chat')(registry)
6
+ return class BossBar {
7
+ constructor (uuid, title, health, dividers, color, flags) {
8
+ this._entityUUID = uuid
9
+ this._title = ChatMessage.fromNotch(title)
10
+ this._health = health
11
+ this._dividers = divisions[dividers]
12
+ this._color = colors[color]
13
+ this._shouldDarkenSky = flags & 0x1
14
+ this._isDragonBar = flags & 0x2
15
+ this._createFog = flags & 0x4
16
+ }
17
+
18
+ set entityUUID (uuid) {
19
+ this._entityUUID = uuid
20
+ }
21
+
22
+ set title (title) {
23
+ this._title = ChatMessage.fromNotch(title)
24
+ }
25
+
26
+ set health (health) {
27
+ this._health = health
28
+ }
29
+
30
+ set dividers (dividers) {
31
+ this._dividers = divisions[dividers]
32
+ }
33
+
34
+ set color (color) {
35
+ this._color = colors[color]
36
+ }
37
+
38
+ set flags (flags) {
39
+ this._shouldDarkenSky = flags & 0x1
40
+ this._isDragonBar = flags & 0x2
41
+ this._createFog = flags & 0x4
42
+ }
43
+
44
+ get flags () {
45
+ return (this._shouldDarkenSky) | (this._isDragonBar << 1) | (this._createFog << 2)
46
+ }
47
+
48
+ set shouldDarkenSky (darkenSky) {
49
+ this._shouldDarkenSky = darkenSky
50
+ }
51
+
52
+ set isDragonBar (dragonBar) {
53
+ this._isDragonBar = dragonBar
54
+ }
55
+
56
+ get createFog () {
57
+ return this._createFog
58
+ }
59
+
60
+ set createFog (createFog) {
61
+ this._createFog = createFog
62
+ }
63
+
64
+ get entityUUID () {
65
+ return this._entityUUID
66
+ }
67
+
68
+ get title () {
69
+ return this._title
70
+ }
71
+
72
+ get health () {
73
+ return this._health
74
+ }
75
+
76
+ get dividers () {
77
+ return this._dividers
78
+ }
79
+
80
+ get color () {
81
+ return this._color
82
+ }
83
+
84
+ get shouldDarkenSky () {
85
+ return this._shouldDarkenSky
86
+ }
87
+
88
+ get isDragonBar () {
89
+ return this._isDragonBar
90
+ }
91
+
92
+ get shouldCreateFog () {
93
+ return this._createFog
94
+ }
100
95
  }
101
96
  }
97
+
98
+ module.exports = loader
@@ -1,6 +1,6 @@
1
1
  const assert = require('assert')
2
2
  const { sleep } = require('../promise_utils')
3
- const { once } = require('events')
3
+ const { once } = require('../promise_utils')
4
4
 
5
5
  module.exports = inject
6
6
 
@@ -12,7 +12,7 @@ function inject (bot) {
12
12
  async function openAnvil (anvilBlock) {
13
13
  const anvil = await bot.openBlock(anvilBlock)
14
14
  if (!matchWindowType(anvil)) {
15
- throw new Error('This is not a anvil-like window')
15
+ throw new Error('Not a anvil-like window: ' + JSON.stringify(anvil))
16
16
  }
17
17
 
18
18
  function err (name) {
@@ -1,5 +1,5 @@
1
1
  const assert = require('assert')
2
- const { once } = require('events')
2
+ const { once } = require('../promise_utils')
3
3
 
4
4
  module.exports = inject
5
5
 
@@ -144,7 +144,7 @@ function inject (bot, options) {
144
144
  function chatWithHeader (header, message) {
145
145
  if (typeof message === 'number') message = message.toString()
146
146
  if (typeof message !== 'string') {
147
- throw new Error('Incorrect type! Should be a string or number.')
147
+ throw new Error('Chat message type must be a string or number: ' + typeof message)
148
148
  }
149
149
 
150
150
  if (!header && message.startsWith('/')) {
@@ -23,7 +23,7 @@ function inject (bot) {
23
23
  throw new Error('containerToOpen is neither a block nor an entity')
24
24
  }
25
25
 
26
- if (!matchWindowType(chest)) { throw new Error('Non-container window used as a container') }
26
+ if (!matchWindowType(chest)) { throw new Error('Non-container window used as a container: ' + JSON.stringify(chest)) }
27
27
  return chest
28
28
  }
29
29
 
@@ -1,5 +1,5 @@
1
1
  const assert = require('assert')
2
- const { once } = require('events')
2
+ const { once } = require('../promise_utils')
3
3
 
4
4
  module.exports = inject
5
5
 
@@ -12,7 +12,7 @@ function inject (bot) {
12
12
  assert.ok(recipe)
13
13
  count = parseInt(count ?? 1, 10)
14
14
  if (recipe.requiresTable && !craftingTable) {
15
- throw new Error('recipe requires craftingTable')
15
+ throw new Error('Recipe requires craftingTable, but one was not supplied: ' + JSON.stringify(recipe))
16
16
  }
17
17
 
18
18
  try {
@@ -41,7 +41,7 @@ function inject (bot) {
41
41
  windowCraftingTable = window
42
42
  }
43
43
  if (!windowCraftingTable.type.startsWith('minecraft:crafting')) {
44
- throw new Error('crafting: non craftingTable used as craftingTable')
44
+ throw new Error('crafting: non craftingTable used as craftingTable: ' + windowCraftingTable.type)
45
45
  }
46
46
  await startClicking(windowCraftingTable, 3, 3)
47
47
  } else {
@@ -1,7 +1,7 @@
1
1
  const assert = require('assert')
2
2
  const { Vec3 } = require('vec3')
3
3
  const { sleep, onceWithCleanup } = require('../promise_utils')
4
- const { once } = require('events')
4
+ const { once } = require('../promise_utils')
5
5
 
6
6
  module.exports = inject
7
7
 
@@ -70,7 +70,7 @@ function inject (bot) {
70
70
 
71
71
  // last step
72
72
  bot.entity.position = destination
73
- await once(bot, 'move')
73
+ await once(bot, 'move', /* no timeout */ 0)
74
74
  }
75
75
 
76
76
  function startFlying () {
@@ -12,29 +12,29 @@ function inject (bot) {
12
12
  let diggingTask = createDoneTask()
13
13
 
14
14
  bot.targetDigBlock = null
15
+ bot.targetDigFace = null
15
16
  bot.lastDigTime = null
16
17
 
17
18
  async function dig (block, forceLook, digFace) {
18
19
  if (block === null || block === undefined) {
19
20
  throw new Error('dig was called with an undefined or null block')
20
21
  }
22
+
21
23
  if (!digFace || typeof digFace === 'function') {
22
24
  digFace = 'auto'
23
25
  }
24
26
 
25
- if (bot.targetDigBlock) bot.stopDigging()
26
-
27
- let diggingFace = 1 // Default (top)
27
+ bot.targetDigFace = 1 // Default (top)
28
28
 
29
29
  if (forceLook !== 'ignore') {
30
30
  if (digFace?.x || digFace?.y || digFace?.z) {
31
31
  // Determine the block face the bot should mine
32
32
  if (digFace.x) {
33
- diggingFace = digFace.x > 0 ? BlockFaces.EAST : BlockFaces.WEST
33
+ bot.targetDigFace = digFace.x > 0 ? BlockFaces.EAST : BlockFaces.WEST
34
34
  } else if (digFace.y) {
35
- diggingFace = digFace.y > 0 ? BlockFaces.TOP : BlockFaces.BOTTOM
35
+ bot.targetDigFace = digFace.y > 0 ? BlockFaces.TOP : BlockFaces.BOTTOM
36
36
  } else if (digFace.z) {
37
- diggingFace = digFace.z > 0 ? BlockFaces.SOUTH : BlockFaces.NORTH
37
+ bot.targetDigFace = digFace.z > 0 ? BlockFaces.SOUTH : BlockFaces.NORTH
38
38
  }
39
39
  await bot.lookAt(
40
40
  block.position.offset(0.5, 0.5, 0.5).offset(digFace.x * 0.5, digFace.y * 0.5, digFace.z * 0.5),
@@ -76,8 +76,8 @@ function inject (bot) {
76
76
  const rayPos = rayBlock.position
77
77
  if (
78
78
  rayPos.x === block.position.x &&
79
- rayPos.y === block.position.y &&
80
- rayPos.z === block.position.z
79
+ rayPos.y === block.position.y &&
80
+ rayPos.z === block.position.z
81
81
  ) {
82
82
  validFaces.push({
83
83
  face: rayBlock.face,
@@ -86,6 +86,7 @@ function inject (bot) {
86
86
  }
87
87
  }
88
88
  }
89
+
89
90
  if (validFaces.length > 0) {
90
91
  // Chose closest valid face
91
92
  let closest
@@ -101,11 +102,11 @@ function inject (bot) {
101
102
  }
102
103
  }
103
104
  await bot.lookAt(closest.targetPos, forceLook)
104
- diggingFace = closest.face
105
+ bot.targetDigFace = closest.face
105
106
  } else if (closerBlocks.length === 0 && block.shapes.length === 0) {
106
107
  // no other blocks were detected and the block has no shapes.
107
108
  // The block in question is replaceable (like tall grass) so we can just dig it
108
- // TODO: do AABB + ray intercept check to this position for diggingFace.
109
+ // TODO: do AABB + ray intercept check to this position for digFace.
109
110
  await bot.lookAt(block.position.offset(0.5, 0.5, 0.5), forceLook)
110
111
  } else {
111
112
  // Block is obstructed return error?
@@ -116,11 +117,15 @@ function inject (bot) {
116
117
  }
117
118
  }
118
119
 
120
+ // In vanilla the client will cancel digging the current block once the other block is at the crosshair.
121
+ // Todo: don't wait until lookAt is at middle of the block, but at the edge of it.
122
+ if (bot.targetDigBlock) bot.stopDigging()
123
+
119
124
  diggingTask = createTask()
120
125
  bot._client.write('block_dig', {
121
126
  status: 0, // start digging
122
127
  location: block.position,
123
- face: diggingFace // default face is 1 (top)
128
+ face: bot.targetDigFace // default face is 1 (top)
124
129
  })
125
130
  const waitTime = bot.digTime(block)
126
131
  waitTimeout = setTimeout(finishDigging, waitTime)
@@ -140,10 +145,11 @@ function inject (bot) {
140
145
  bot._client.write('block_dig', {
141
146
  status: 2, // finish digging
142
147
  location: bot.targetDigBlock.position,
143
- face: diggingFace // hard coded to always dig from the top
148
+ face: bot.targetDigFace // always the same as the start face
144
149
  })
145
150
  }
146
151
  bot.targetDigBlock = null
152
+ bot.targetDigFace = null
147
153
  bot.lastDigTime = performance.now()
148
154
  bot._updateBlockState(block.position, 0)
149
155
  }
@@ -151,8 +157,15 @@ function inject (bot) {
151
157
  const eventName = `blockUpdate:${block.position}`
152
158
  bot.on(eventName, onBlockUpdate)
153
159
 
160
+ const currentBlock = block
154
161
  bot.stopDigging = () => {
155
162
  if (!bot.targetDigBlock) return
163
+
164
+ // Replicate the odd vanilla cancellation face value.
165
+ // When the cancellation is because of a new dig request on another block it's the same as the new dig start face. In all other cases it's 0.
166
+ const stoppedBecauseOfNewDigRequest = !currentBlock.position.equals(bot.targetDigBlock.position)
167
+ const cancellationDiggingFace = !stoppedBecauseOfNewDigRequest ? bot.targetDigFace : 0
168
+
156
169
  bot.removeListener(eventName, onBlockUpdate)
157
170
  clearInterval(swingInterval)
158
171
  clearTimeout(waitTimeout)
@@ -161,10 +174,11 @@ function inject (bot) {
161
174
  bot._client.write('block_dig', {
162
175
  status: 1, // cancel digging
163
176
  location: bot.targetDigBlock.position,
164
- face: 1 // hard coded to always dig from the top
177
+ face: cancellationDiggingFace
165
178
  })
166
179
  const block = bot.targetDigBlock
167
180
  bot.targetDigBlock = null
181
+ bot.targetDigFace = null
168
182
  bot.lastDigTime = performance.now()
169
183
  bot.emit('diggingAborted', block)
170
184
  bot.stopDigging = noop
@@ -182,6 +196,7 @@ function inject (bot) {
182
196
  swingInterval = null
183
197
  waitTimeout = null
184
198
  bot.targetDigBlock = null
199
+ bot.targetDigFace = null
185
200
  bot.lastDigTime = performance.now()
186
201
  bot.emit('diggingCompleted', newBlock)
187
202
  diggingTask.finish()
@@ -199,8 +214,8 @@ function inject (bot) {
199
214
  function canDigBlock (block) {
200
215
  return (
201
216
  block &&
202
- block.diggable &&
203
- block.position.offset(0.5, 0.5, 0.5).distanceTo(bot.entity.position.offset(0, 1.65, 0)) <= 5.1
217
+ block.diggable &&
218
+ block.position.offset(0.5, 0.5, 0.5).distanceTo(bot.entity.position.offset(0, 1.65, 0)) <= 5.1
204
219
  )
205
220
  }
206
221
 
@@ -1,5 +1,5 @@
1
1
  const assert = require('assert')
2
- const { once } = require('events')
2
+ const { once } = require('../promise_utils')
3
3
 
4
4
  module.exports = inject
5
5
 
@@ -9,7 +9,7 @@ function inject (bot) {
9
9
  let ready = false
10
10
  const enchantmentTable = await bot.openBlock(enchantmentTableBlock)
11
11
  if (!enchantmentTable.type.startsWith('minecraft:enchant')) {
12
- throw new Error('This is not an enchantment table')
12
+ throw new Error('Expected minecraft:enchant when opening table but got ' + enchantmentTable.type)
13
13
  }
14
14
 
15
15
  resetEnchantmentOptions()
@@ -597,7 +597,7 @@ function inject (bot) {
597
597
  }
598
598
 
599
599
  if (item.displayName) {
600
- obj.displayName = new ChatMessage(JSON.parse(item.displayName))
600
+ obj.displayName = ChatMessage.fromNotch(item.displayName)
601
601
  } else if (packet.action & 32) obj.displayName = new ChatMessage({ text: '', extra: [{ text: player.username || obj.username }] })
602
602
 
603
603
  if (newPlayer) {
@@ -660,7 +660,7 @@ function inject (bot) {
660
660
  }
661
661
 
662
662
  if (item.displayName) {
663
- player.displayName = new ChatMessage(JSON.parse(item.displayName))
663
+ player.displayName = ChatMessage.fromNotch(item.displayName)
664
664
  }
665
665
 
666
666
  const playerEntity = Object.values(bot.entities).find(e => e.type === 'player' && e.username === item.name)
@@ -681,7 +681,7 @@ function inject (bot) {
681
681
  } else if (packet.action === 3 && !item.displayName) {
682
682
  player.displayName = new ChatMessage({ text: '', extra: [{ text: player.username }] })
683
683
  } else if (packet.action === 3 && item.displayName) {
684
- player.displayName = new ChatMessage(JSON.parse(item.displayName))
684
+ player.displayName = ChatMessage.fromNotch(item.displayName)
685
685
  } else if (packet.action === 4) {
686
686
  if (player.entity === bot.entity) continue
687
687
 
@@ -1,7 +1,6 @@
1
1
  const assert = require('assert')
2
2
  const { Vec3 } = require('vec3')
3
- const { once } = require('events')
4
- const { sleep, createDoneTask, createTask, withTimeout } = require('../promise_utils')
3
+ const { once, sleep, createDoneTask, createTask, withTimeout } = require('../promise_utils')
5
4
 
6
5
  module.exports = inject
7
6
 
@@ -1,7 +1,12 @@
1
+ const UUID = require('uuid-1345')
2
+
1
3
  module.exports = inject
2
4
 
3
5
  function inject (bot) {
6
+ let uuid
4
7
  let latestHash
8
+ let latestUUID
9
+ let activeResourcePacks = {}
5
10
  const TEXTURE_PACK_RESULTS = {
6
11
  SUCCESSFULLY_LOADED: 0,
7
12
  DECLINED: 1,
@@ -9,9 +14,38 @@ function inject (bot) {
9
14
  ACCEPTED: 3
10
15
  }
11
16
 
17
+ bot._client.on('add_resource_pack', (data) => { // Emits the same as resource_pack_send but sends uuid rather than hash because that's how active packs are tracked
18
+ const uuid = new UUID(data.uuid)
19
+ // Adding the pack to a set by uuid
20
+ latestUUID = uuid
21
+ activeResourcePacks[uuid] = data.url
22
+
23
+ bot.emit('resourcePack', data.url, uuid)
24
+ })
25
+
26
+ bot._client.on('remove_resource_pack', (data) => { // Doesn't emit anything because it is removing rather than adding
27
+ // if uuid isn't provided remove all packs
28
+ if (data.uuid === undefined) {
29
+ activeResourcePacks = {}
30
+ } else {
31
+ // Try to remove uuid from set
32
+ try {
33
+ delete activeResourcePacks[new UUID(data.uuid)]
34
+ } catch (error) {
35
+ console.error('Tried to remove UUID but it was not in the active list.')
36
+ }
37
+ }
38
+ })
39
+
12
40
  bot._client.on('resource_pack_send', (data) => {
13
- bot.emit('resourcePack', data.url, data.hash)
14
- latestHash = data.hash
41
+ if (bot.supportFeature('resourcePackUsesUUID')) {
42
+ uuid = new UUID(data.uuid)
43
+ bot.emit('resourcePack', uuid, data.url)
44
+ latestUUID = uuid
45
+ } else {
46
+ bot.emit('resourcePack', data.url, data.hash)
47
+ latestHash = data.hash
48
+ }
15
49
  })
16
50
 
17
51
  function acceptResourcePack () {
@@ -24,6 +58,15 @@ function inject (bot) {
24
58
  result: TEXTURE_PACK_RESULTS.SUCCESSFULLY_LOADED,
25
59
  hash: latestHash
26
60
  })
61
+ } else if (bot.supportFeature('resourcePackUsesUUID')) {
62
+ bot._client.write('resource_pack_receive', {
63
+ uuid: latestUUID,
64
+ result: TEXTURE_PACK_RESULTS.ACCEPTED
65
+ })
66
+ bot._client.write('resource_pack_receive', {
67
+ uuid: latestUUID,
68
+ result: TEXTURE_PACK_RESULTS.SUCCESSFULLY_LOADED
69
+ })
27
70
  } else {
28
71
  bot._client.write('resource_pack_receive', {
29
72
  result: TEXTURE_PACK_RESULTS.ACCEPTED
@@ -35,6 +78,12 @@ function inject (bot) {
35
78
  }
36
79
 
37
80
  function denyResourcePack () {
81
+ if (bot.supportFeature('resourcePackUsesUUID')) {
82
+ bot._client.write('resource_pack_receive', {
83
+ uuid: latestUUID,
84
+ result: TEXTURE_PACK_RESULTS.DECLINED
85
+ })
86
+ }
38
87
  bot._client.write('resource_pack_receive', {
39
88
  result: TEXTURE_PACK_RESULTS.DECLINED
40
89
  })
@@ -16,6 +16,16 @@ function inject (bot) {
16
16
  if (packet.action === 1) {
17
17
  bot.emit('scoreboardDeleted', scoreboards[packet.name])
18
18
  delete scoreboards[packet.name]
19
+
20
+ for (const position in ScoreBoard.positions) {
21
+ if (!ScoreBoard.positions[position]) continue
22
+ const scoreboard = ScoreBoard.positions[position]
23
+
24
+ if (scoreboard && scoreboard.name === packet.name) {
25
+ delete ScoreBoard.positions[position]
26
+ break
27
+ }
28
+ }
19
29
  }
20
30
 
21
31
  if (packet.action === 2) {
@@ -13,14 +13,19 @@ function inject (bot) {
13
13
  }
14
14
 
15
15
  bot._client.on('playerlist_header', (packet) => {
16
- if (packet.header) {
17
- const header = escapeValueNewlines(packet.header)
18
- bot.tablist.header = new ChatMessage(JSON.parse(header))
19
- }
16
+ if (bot.supportFeature('chatPacketsUseNbtComponents')) { // 1.20.3+
17
+ bot.tablist.header = ChatMessage.fromNotch(packet.header)
18
+ bot.tablist.footer = ChatMessage.fromNotch(packet.footer)
19
+ } else {
20
+ if (packet.header) {
21
+ const header = escapeValueNewlines(packet.header)
22
+ bot.tablist.header = ChatMessage.fromNotch(header)
23
+ }
20
24
 
21
- if (packet.footer) {
22
- const footer = escapeValueNewlines(packet.footer)
23
- bot.tablist.footer = new ChatMessage(JSON.parse(footer))
25
+ if (packet.footer) {
26
+ const footer = escapeValueNewlines(packet.footer)
27
+ bot.tablist.footer = ChatMessage.fromNotch(footer)
28
+ }
24
29
  }
25
30
  })
26
31
  }
@@ -1,5 +1,5 @@
1
1
  const assert = require('assert')
2
- const { once } = require('events')
2
+ const { once } = require('../promise_utils')
3
3
 
4
4
  module.exports = inject
5
5
 
@@ -78,7 +78,7 @@ function inject (bot, { version }) {
78
78
  bot._client.on(tradeListPacket, gotTrades)
79
79
  const villager = await villagerPromise
80
80
  if (villager.type !== 'minecraft:villager' && villager.type !== 'minecraft:merchant') {
81
- throw new Error('This is not a villager')
81
+ throw new Error('Expected minecraft:villager or minecraft:mechant type, but got ' + villager.type)
82
82
  }
83
83
 
84
84
  villager.trades = null
@@ -72,6 +72,10 @@ function onceWithCleanup (emitter, event, { timeout = 0, checkCondition = undefi
72
72
  return task.promise
73
73
  }
74
74
 
75
+ function once (emitter, event, timeout = 20000) {
76
+ return onceWithCleanup(emitter, event, { timeout })
77
+ }
78
+
75
79
  function withTimeout (promise, timeout) {
76
80
  return Promise.race([
77
81
  promise,
@@ -82,6 +86,7 @@ function withTimeout (promise, timeout) {
82
86
  }
83
87
 
84
88
  module.exports = {
89
+ once,
85
90
  sleep,
86
91
  createTask,
87
92
  createDoneTask,
package/lib/team.js CHANGED
@@ -1,14 +1,3 @@
1
- let ChatMessage
2
- let MessageBuilder
3
-
4
- module.exports = loader
5
-
6
- function loader (registry) {
7
- ChatMessage = require('prismarine-chat')(registry)
8
- MessageBuilder = ChatMessage.MessageBuilder
9
- return Team
10
- }
11
-
12
1
  function colorString (color) {
13
2
  const formatting = [
14
3
  'black',
@@ -38,56 +27,60 @@ function colorString (color) {
38
27
  return formatting[color]
39
28
  }
40
29
 
41
- class Team {
42
- constructor (team, name, friendlyFire, nameTagVisibility, collisionRule, formatting, prefix, suffix) {
43
- this.team = team
44
- this.update(name, friendlyFire, nameTagVisibility, collisionRule, formatting, prefix, suffix)
45
- this.membersMap = {}
46
- }
30
+ function loader (registry) {
31
+ const ChatMessage = require('prismarine-chat')(registry)
32
+ const MessageBuilder = ChatMessage.MessageBuilder
33
+ return class Team {
34
+ constructor (team, name, friendlyFire, nameTagVisibility, collisionRule, formatting, prefix, suffix) {
35
+ this.team = team
36
+ this.update(name, friendlyFire, nameTagVisibility, collisionRule, formatting, prefix, suffix)
37
+ this.membersMap = {}
38
+ }
47
39
 
48
- parseMessage (value) {
49
- let result
50
- try {
51
- result = new ChatMessage(JSON.parse(value)) // version>1.13-pre7
52
- } catch {
53
- result = MessageBuilder.fromString(value, { colorSeparator: '§' })
54
- if (result === null) {
55
- return new ChatMessage('')
40
+ parseMessage (value) {
41
+ if (registry.supportFeature('teamUsesChatComponents')) { // 1.13+
42
+ return ChatMessage.fromNotch(value)
43
+ } else {
44
+ const result = MessageBuilder.fromString(value, { colorSeparator: '§' })
45
+ if (result === null) {
46
+ return new ChatMessage('')
47
+ }
48
+ return new ChatMessage(result.toJSON())
56
49
  }
57
- return new ChatMessage(result.toJSON())
58
50
  }
59
- return result
60
- }
61
51
 
62
- add (name) {
63
- this.membersMap[name] = ''
64
- return this.membersMap[name]
65
- }
52
+ add (name) {
53
+ this.membersMap[name] = ''
54
+ return this.membersMap[name]
55
+ }
66
56
 
67
- remove (name) {
68
- const removed = this.membersMap[name]
69
- delete this.membersMap[name]
70
- return removed
71
- }
57
+ remove (name) {
58
+ const removed = this.membersMap[name]
59
+ delete this.membersMap[name]
60
+ return removed
61
+ }
72
62
 
73
- update (name, friendlyFire, nameTagVisibility, collisionRule, formatting, prefix, suffix) {
74
- this.name = this.parseMessage(name)
75
- this.friendlyFire = friendlyFire
76
- this.nameTagVisibility = nameTagVisibility
77
- this.collisionRule = collisionRule
78
- this.color = colorString(formatting)
79
- this.prefix = this.parseMessage(prefix)
80
- this.suffix = this.parseMessage(suffix)
81
- }
63
+ update (name, friendlyFire, nameTagVisibility, collisionRule, formatting, prefix, suffix) {
64
+ this.name = this.parseMessage(name)
65
+ this.friendlyFire = friendlyFire
66
+ this.nameTagVisibility = nameTagVisibility
67
+ this.collisionRule = collisionRule
68
+ this.color = colorString(formatting)
69
+ this.prefix = this.parseMessage(prefix)
70
+ this.suffix = this.parseMessage(suffix)
71
+ }
82
72
 
83
- // Return a chat component with prefix + color + name + suffix
84
- displayName (member) {
85
- const name = this.prefix.clone()
86
- name.append(new ChatMessage({ text: member, color: this.color }), this.suffix)
87
- return name
88
- }
73
+ // Return a chat component with prefix + color + name + suffix
74
+ displayName (member) {
75
+ const name = this.prefix.clone()
76
+ name.append(new ChatMessage({ text: member, color: this.color }), this.suffix)
77
+ return name
78
+ }
89
79
 
90
- get members () {
91
- return Object.keys(this.membersMap)
80
+ get members () {
81
+ return Object.keys(this.membersMap)
82
+ }
92
83
  }
93
84
  }
85
+
86
+ module.exports = loader
package/lib/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const testedVersions = ['1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20.1', '1.20.2']
1
+ const testedVersions = ['1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20.1', '1.20.2', '1.20.4']
2
2
  module.exports = {
3
3
 
4
4
  testedVersions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mineflayer",
3
- "version": "4.19.0",
3
+ "version": "4.20.1",
4
4
  "description": "create minecraft bots with a stable, high level API",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -22,7 +22,7 @@
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
24
  "minecraft-data": "^3.56.0",
25
- "minecraft-protocol": "^1.44.0",
25
+ "minecraft-protocol": "^1.47.0",
26
26
  "prismarine-biome": "^1.1.1",
27
27
  "prismarine-block": "^1.17.0",
28
28
  "prismarine-chat": "^1.7.1",
@@ -33,7 +33,7 @@
33
33
  "prismarine-physics": "^1.8.0",
34
34
  "prismarine-recipe": "^1.3.0",
35
35
  "prismarine-registry": "^1.5.0",
36
- "prismarine-windows": "^2.8.0",
36
+ "prismarine-windows": "^2.9.0",
37
37
  "prismarine-world": "^3.6.0",
38
38
  "protodef": "^1.14.0",
39
39
  "typed-emitter": "^1.0.0",