mineflayer 4.3.0 → 4.5.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.
Files changed (59) hide show
  1. package/.github/workflows/ci.yml +2 -0
  2. package/README.md +2 -2
  3. package/docs/README.md +2 -2
  4. package/docs/api.md +50 -20
  5. package/docs/es/api_es.md +1 -1
  6. package/docs/history.md +28 -0
  7. package/docs/ru/FAQ_RU.md +1 -1
  8. package/docs/ru/api_ru.md +2 -2
  9. package/docs/zh/README_ZH_CN.md +1 -1
  10. package/docs/zh/api.md +1 -1
  11. package/examples/anvil.js +4 -8
  12. package/examples/auto_totem.js +2 -3
  13. package/examples/block_entity.js +2 -7
  14. package/examples/blockfinder.js +2 -4
  15. package/examples/chatterbox.js +2 -3
  16. package/examples/chest.js +7 -12
  17. package/examples/collectblock.js +1 -7
  18. package/examples/command_block.js +1 -6
  19. package/examples/digger.js +1 -2
  20. package/examples/discord.js +1 -1
  21. package/examples/farmer.js +3 -8
  22. package/examples/fisherman.js +2 -7
  23. package/examples/graffiti.js +2 -7
  24. package/examples/guard.js +1 -2
  25. package/examples/inventory.js +4 -6
  26. package/examples/jumper.js +1 -1
  27. package/examples/pathfinder/gps.js +1 -2
  28. package/examples/place_end_crystal/index.js +2 -9
  29. package/examples/python/basic.py +1 -2
  30. package/examples/python/chatterbox.py +1 -1
  31. package/examples/trader.js +4 -8
  32. package/index.d.ts +23 -29
  33. package/lib/bossbar.js +2 -2
  34. package/lib/loader.js +2 -2
  35. package/lib/plugins/anvil.js +1 -1
  36. package/lib/plugins/bed.js +16 -1
  37. package/lib/plugins/block_actions.js +4 -3
  38. package/lib/plugins/blocks.js +3 -3
  39. package/lib/plugins/book.js +1 -1
  40. package/lib/plugins/boss_bar.js +1 -1
  41. package/lib/plugins/chat.js +63 -12
  42. package/lib/plugins/chest.js +6 -4
  43. package/lib/plugins/command_block.js +1 -1
  44. package/lib/plugins/craft.js +3 -3
  45. package/lib/plugins/creative.js +2 -2
  46. package/lib/plugins/entities.js +25 -8
  47. package/lib/plugins/game.js +14 -5
  48. package/lib/plugins/generic_place.js +1 -1
  49. package/lib/plugins/inventory.js +50 -25
  50. package/lib/plugins/place_entity.js +1 -1
  51. package/lib/plugins/ray_trace.js +36 -0
  52. package/lib/plugins/settings.js +1 -1
  53. package/lib/plugins/tablist.js +1 -1
  54. package/lib/plugins/team.js +1 -1
  55. package/lib/plugins/villager.js +9 -7
  56. package/lib/scoreboard.js +1 -1
  57. package/lib/team.js +2 -2
  58. package/lib/version.js +2 -2
  59. package/package.json +15 -16
@@ -23,11 +23,6 @@ const bot = mineflayer.createBot({
23
23
  password: process.argv[5]
24
24
  })
25
25
 
26
- let mcData
27
- bot.once('inject_allowed', () => {
28
- mcData = require('minecraft-data')(bot.version)
29
- })
30
-
31
26
  bot.on('chat', (username, message) => {
32
27
  if (username === bot.username) return
33
28
  switch (true) {
@@ -49,7 +44,7 @@ function watchPaintingOrSign () {
49
44
  }
50
45
  })
51
46
  const signBlock = bot.findBlock({
52
- matching: ['painting', 'sign'].map(name => mcData.blocksByName[name].id)
47
+ matching: ['painting', 'sign'].map(name => bot.registry.blocksByName[name].id)
53
48
  })
54
49
  if (signBlock) {
55
50
  bot.chat(`The sign says: ${signBlock.signText}`)
@@ -62,7 +57,7 @@ function watchPaintingOrSign () {
62
57
 
63
58
  function updateSign (message) {
64
59
  const signBlock = bot.findBlock({
65
- matching: ['painting', 'sign'].map(name => mcData.blocksByName[name].id)
60
+ matching: ['painting', 'sign'].map(name => bot.registry.blocksByName[name].id)
66
61
  })
67
62
  if (signBlock) {
68
63
  bot.updateSign(signBlock, message.split(' ').slice(1).join(' '))
package/examples/guard.js CHANGED
@@ -42,8 +42,7 @@ function stopGuarding () {
42
42
 
43
43
  // Pathfinder to the guard position
44
44
  function moveToGuardPos () {
45
- const mcData = require('minecraft-data')(bot.version)
46
- bot.pathfinder.setMovements(new Movements(bot, mcData))
45
+ bot.pathfinder.setMovements(new Movements(bot))
47
46
  bot.pathfinder.setGoal(new goals.GoalBlock(guardPos.x, guardPos.y, guardPos.z))
48
47
  }
49
48
 
@@ -67,7 +67,7 @@ bot.on('chat', async (username, message) => {
67
67
  function sayItems (items = null) {
68
68
  if (!items) {
69
69
  items = bot.inventory.items()
70
- if (require('minecraft-data')(bot.version).isNewerOrEqualTo('1.9') && bot.inventory.slots[45]) items.push(bot.inventory.slots[45])
70
+ if (bot.registry.isNewerOrEqualTo('1.9') && bot.inventory.slots[45]) items.push(bot.inventory.slots[45])
71
71
  }
72
72
  const output = items.map(itemToString).join(', ')
73
73
  if (output) {
@@ -127,10 +127,8 @@ function useEquippedItem () {
127
127
 
128
128
  async function craftItem (name, amount) {
129
129
  amount = parseInt(amount, 10)
130
- const mcData = require('minecraft-data')(bot.version)
131
-
132
- const item = mcData.itemsByName[name]
133
- const craftingTableID = mcData.blocksByName.crafting_table.id
130
+ const item = bot.registry.itemsByName[name]
131
+ const craftingTableID = bot.registry.blocksByName.crafting_table.id
134
132
 
135
133
  const craftingTable = bot.findBlock({
136
134
  matching: craftingTableID
@@ -164,6 +162,6 @@ function itemToString (item) {
164
162
 
165
163
  function itemByName (name) {
166
164
  const items = bot.inventory.items()
167
- if (require('minecraft-data')(bot.version).isNewerOrEqualTo('1.9') && bot.inventory.slots[45]) items.push(bot.inventory.slots[45])
165
+ if (bot.registry.isNewerOrEqualTo('1.9') && bot.inventory.slots[45]) items.push(bot.inventory.slots[45])
168
166
  return items.filter(item => item.name === name)[0]
169
167
  }
@@ -63,7 +63,7 @@ bot.on('chat', (username, message) => {
63
63
  }
64
64
  break
65
65
  case 'mount':
66
- entity = bot.nearestEntity((entity) => { return entity.type === 'object' })
66
+ entity = bot.nearestEntity((entity) => { return entity.name === 'minecart' })
67
67
  if (entity) {
68
68
  bot.mount(entity)
69
69
  } else {
@@ -20,8 +20,7 @@ const RANGE_GOAL = 1 // get within this radius of the player
20
20
  bot.loadPlugin(pathfinder)
21
21
 
22
22
  bot.once('spawn', () => {
23
- const mcData = require('minecraft-data')(bot.version)
24
- const defaultMove = new Movements(bot, mcData)
23
+ const defaultMove = new Movements(bot)
25
24
 
26
25
  bot.on('chat', (username, message) => {
27
26
  if (username === bot.username) return
@@ -22,14 +22,7 @@ const bot = mineflayer.createBot({
22
22
 
23
23
  const MAX_DIST_FROM_BLOCK_TO_PLACE = 4
24
24
 
25
- let mcData = null
26
- bot.once('spawn', () => {
27
- mcData = require('minecraft-data')(bot.version)
28
- })
29
-
30
25
  bot.on('chat', async (ign, msg) => {
31
- if (!mcData) mcData = require('minecraft-data')(bot.version)
32
-
33
26
  // solve where the bot will place the crystal near
34
27
  let findBlocksNearPoint = null
35
28
  if (msg === 'place crystal near bot') {
@@ -44,13 +37,13 @@ bot.on('chat', async (ign, msg) => {
44
37
  }
45
38
 
46
39
  // find end crystal(s) in inventory
47
- const item = bot.inventory.findInventoryItem(mcData.itemsByName.end_crystal.id)
40
+ const item = bot.inventory.findInventoryItem(bot.registry.itemsByName.end_crystal.id)
48
41
  if (!item) bot.chat("I don't have any ender crystals")
49
42
 
50
43
  // find the crystal
51
44
  const block = bot.findBlock({
52
45
  point: findBlocksNearPoint,
53
- matching: ['bedrock', 'obsidian'].map(blockName => mcData.blocksByName[blockName].id),
46
+ matching: ['bedrock', 'obsidian'].map(blockName => bot.registry.blocksByName[blockName].id),
54
47
  useExtraInfo: block => {
55
48
  const hasAirAbove = bot.blockAt(block.position.offset(0, 1, 0)).name === 'air'
56
49
  const botNotStandingOnBlock = block.position.xzDistanceTo(bot.entity.position) > 2
@@ -17,8 +17,7 @@ print("Started mineflayer")
17
17
  @On(bot, 'spawn')
18
18
  def handle(*args):
19
19
  print("I spawned 👋")
20
- mcData = require('minecraft-data')(bot.version)
21
- movements = pathfinder.Movements(bot, mcData)
20
+ movements = pathfinder.Movements(bot)
22
21
 
23
22
  @On(bot, 'chat')
24
23
  def handleMsg(this, sender, message, *args):
@@ -34,7 +34,7 @@ bot = mineflayer.createBot({
34
34
  "port": port
35
35
  })
36
36
 
37
- Item = require("prismarine-item")(bot.version)
37
+ Item = require("prismarine-item")(bot.registry)
38
38
 
39
39
 
40
40
  @On(bot, "chat")
@@ -25,10 +25,6 @@ const bot = mineflayer.createBot({
25
25
  username: process.argv[4] ? process.argv[4] : 'trader',
26
26
  password: process.argv[5]
27
27
  })
28
- let mcData
29
- bot.once('inject_allowed', () => {
30
- mcData = require('minecraft-data')(bot.version)
31
- })
32
28
 
33
29
  bot.on('chat', (username, message) => {
34
30
  if (username === bot.username) return
@@ -50,7 +46,7 @@ bot.on('chat', (username, message) => {
50
46
  })
51
47
 
52
48
  function showVillagers () {
53
- const villagers = Object.keys(bot.entities).map(id => bot.entities[id]).filter(e => e.entityType === mcData.entitiesByName.villager.id)
49
+ const villagers = Object.keys(bot.entities).map(id => bot.entities[id]).filter(e => e.entityType === bot.registry.entitiesByName.villager.id)
54
50
  const closeVillagersId = villagers.filter(e => bot.entity.position.distanceTo(e.position) < 3).map(e => e.id)
55
51
  bot.chat(`found ${villagers.length} villagers`)
56
52
  bot.chat(`villager(s) you can trade with: ${closeVillagersId.join(', ')}`)
@@ -69,7 +65,7 @@ async function showTrades (id) {
69
65
  case !e:
70
66
  bot.chat(`cant find entity with id ${id}`)
71
67
  break
72
- case e.entityType !== mcData.entitiesByName.villager.id:
68
+ case e.entityType !== bot.registry.entitiesByName.villager.id:
73
69
  bot.chat('entity is not a villager')
74
70
  break
75
71
  case bot.entity.position.distanceTo(e.position) > 3:
@@ -91,7 +87,7 @@ async function trade (id, index, count) {
91
87
  case !e:
92
88
  bot.chat(`cant find entity with id ${id}`)
93
89
  break
94
- case e.entityType !== mcData.entitiesByName.villager.id:
90
+ case e.entityType !== bot.registry.entitiesByName.villager.id:
95
91
  bot.chat('entity is not a villager')
96
92
  break
97
93
  case bot.entity.position.distanceTo(e.position) > 3:
@@ -168,7 +164,7 @@ function stringifyItem (item) {
168
164
  text += ` enchanted with ${(ench || StoredEnchantments).value.value.map((e) => {
169
165
  const lvl = e.lvl.value
170
166
  const id = e.id.value
171
- return mcData.enchantments[id].displayName + ' ' + lvl
167
+ return bot.registry.enchantments[id].displayName + ' ' + lvl
172
168
  }).join(' ')}`
173
169
  }
174
170
  }
package/index.d.ts CHANGED
@@ -126,7 +126,7 @@ interface BotEvents {
126
126
  blockBreakProgressEnd: (block: Block) => Promise<void> | void
127
127
  diggingCompleted: (block: Block) => Promise<void> | void
128
128
  diggingAborted: (block: Block) => Promise<void> | void
129
- move: () => Promise<void> | void
129
+ move: (position: Vec3) => Promise<void> | void
130
130
  forcedMove: () => Promise<void> | void
131
131
  mount: () => Promise<void> | void
132
132
  dismount: (vehicle: Entity) => Promise<void> | void
@@ -165,6 +165,7 @@ export interface Bot extends TypedEmitter<BotEvents> {
165
165
  player: Player
166
166
  players: { [username: string]: Player }
167
167
  isRaining: boolean
168
+ thunderState: number
168
169
  chatPatterns: ChatPattern[]
169
170
  settings: GameSettings
170
171
  experience: Experience
@@ -176,7 +177,7 @@ export interface Bot extends TypedEmitter<BotEvents> {
176
177
  physicsEnabled: boolean
177
178
  time: Time
178
179
  quickBarSlot: number
179
- inventory: Window
180
+ inventory: Window<StorageEvents>
180
181
  targetDigBlock: Block
181
182
  isSleeping: boolean
182
183
  scoreboards: { [name: string]: ScoreBoard }
@@ -249,7 +250,7 @@ export interface Bot extends TypedEmitter<BotEvents> {
249
250
 
250
251
  sleep: (bedBlock: Block) => Promise<void>
251
252
 
252
- isABed: (bedBlock: Block) => void
253
+ isABed: (bedBlock: Block) => boolean
253
254
 
254
255
  wake: () => Promise<void>
255
256
 
@@ -298,11 +299,11 @@ export interface Bot extends TypedEmitter<BotEvents> {
298
299
 
299
300
  placeEntity: (referenceBlock: Block, faceVector: Vec3) => Promise<Entity>
300
301
 
301
- activateBlock: (block: Block) => Promise<void>
302
+ activateBlock: (block: Block, direction?: Vec3, cursorPos?: Vec3) => Promise<void>
302
303
 
303
- activateEntity: (block: Entity) => Promise<void>
304
+ activateEntity: (entity: Entity) => Promise<void>
304
305
 
305
- activateEntityAt: (block: Entity, position: Vec3) => Promise<void>
306
+ activateEntityAt: (entity: Entity, position: Vec3) => Promise<void>
306
307
 
307
308
  consume: () => Promise<void>
308
309
 
@@ -337,9 +338,9 @@ export interface Bot extends TypedEmitter<BotEvents> {
337
338
  pages: string[]
338
339
  ) => Promise<void>
339
340
 
340
- openContainer: (chest: Block | Entity) => Promise<Chest | Furnace | Dispenser>
341
+ openContainer: (chest: Block | Entity, direction?: Vec3, cursorPos?: Vec3) => Promise<Chest | Dispenser>
341
342
 
342
- openChest: (chest: Block | Entity) => Promise<Chest>
343
+ openChest: (chest: Block | Entity, direction?: number, cursorPos?: Vec3) => Promise<Chest>
343
344
 
344
345
  openFurnace: (furnace: Block) => Promise<Furnace>
345
346
 
@@ -380,9 +381,9 @@ export interface Bot extends TypedEmitter<BotEvents> {
380
381
 
381
382
  transfer: (options: TransferOptions) => Promise<void>
382
383
 
383
- openBlock: (block: Block, Class: new () => EventEmitter) => Promise<void>
384
+ openBlock: (block: Block, direction?: Vec3, cursorPos?: Vec3) => Promise<Window>
384
385
 
385
- openEntity: (block: Entity, Class: new () => EventEmitter) => Promise<void>
386
+ openEntity: (block: Entity, Class: new () => EventEmitter) => Promise<Window>
386
387
 
387
388
  moveSlotItem: (
388
389
  sourceSlot: number,
@@ -444,8 +445,8 @@ export type LevelType =
444
445
  | 'customized'
445
446
  | 'buffet'
446
447
  | 'default_1_1'
447
- export type GameMode = 'survival' | 'creative' | 'adventure'
448
- export type Dimension = 'minecraft:nether' | 'minecraft:overworld' | 'minecraft:end'
448
+ export type GameMode = 'survival' | 'creative' | 'adventure' | 'spectator'
449
+ export type Dimension = 'minecraft:the_nether' | 'minecraft:overworld' | 'minecraft:the_end'
449
450
  export type Difficulty = 'peaceful' | 'easy' | 'normal' | 'hard'
450
451
 
451
452
  export interface Player {
@@ -455,7 +456,10 @@ export interface Player {
455
456
  gamemode: number
456
457
  ping: number
457
458
  entity: Entity
458
- }
459
+ profileKeys?: {
460
+ publicKey: Buffer
461
+ signature: Buffer
462
+ }
459
463
 
460
464
  export interface ChatPattern {
461
465
  pattern: RegExp
@@ -605,7 +609,7 @@ export class Painting {
605
609
  interface StorageEvents {
606
610
  open: () => void
607
611
  close: () => void
608
- updateSlot: (oldItem: Item | null, newItem: Item) => void
612
+ updateSlot: (oldItem: Item | null, newItem: Item | null) => void
609
613
  }
610
614
 
611
615
  interface FurnaceEvents extends StorageEvents {
@@ -616,9 +620,7 @@ interface ConditionalStorageEvents extends StorageEvents {
616
620
  ready: () => void
617
621
  }
618
622
 
619
- export class Chest extends (EventEmitter as new () => TypedEmitter<StorageEvents>) {
620
- window: object | /* prismarine-windows ChestWindow */ null
621
-
623
+ export class Chest extends Window<StorageEvents> {
622
624
  constructor ();
623
625
 
624
626
  close (): void;
@@ -634,13 +636,9 @@ export class Chest extends (EventEmitter as new () => TypedEmitter<StorageEvents
634
636
  metadata: number | null,
635
637
  count: number | null
636
638
  ): Promise<void>;
637
-
638
- count (itemType: number, metadata: number | null): number;
639
-
640
- items (): Item[];
641
639
  }
642
640
 
643
- export class Furnace extends (EventEmitter as new () => TypedEmitter<FurnaceEvents>) {
641
+ export class Furnace extends Window<FurnaceEvents> {
644
642
  fuel: number
645
643
  progress: number
646
644
 
@@ -673,7 +671,7 @@ export class Furnace extends (EventEmitter as new () => TypedEmitter<FurnaceEven
673
671
  outputItem (): Item;
674
672
  }
675
673
 
676
- export class Dispenser extends (EventEmitter as new () => TypedEmitter<StorageEvents>) {
674
+ export class Dispenser extends Window<StorageEvents> {
677
675
  constructor ();
678
676
 
679
677
  close (): void;
@@ -689,13 +687,9 @@ export class Dispenser extends (EventEmitter as new () => TypedEmitter<StorageEv
689
687
  metadata: number | null,
690
688
  count: number | null
691
689
  ): Promise<void>;
692
-
693
- count (itemType: number, metadata: number | null): number;
694
-
695
- items (): Item[];
696
690
  }
697
691
 
698
- export class EnchantmentTable extends (EventEmitter as new () => TypedEmitter<ConditionalStorageEvents>) {
692
+ export class EnchantmentTable extends Window<ConditionalStorageEvents> {
699
693
  enchantments: Enchantment[]
700
694
 
701
695
  constructor ();
@@ -724,7 +718,7 @@ export interface Enchantment {
724
718
  level: number
725
719
  }
726
720
 
727
- export class Villager extends (EventEmitter as new () => TypedEmitter<ConditionalStorageEvents>) {
721
+ export class Villager extends Window<ConditionalStorageEvents> {
728
722
  trades: VillagerTrade[]
729
723
 
730
724
  constructor ();
package/lib/bossbar.js CHANGED
@@ -4,8 +4,8 @@ const divisions = [0, 6, 10, 12, 20]
4
4
 
5
5
  module.exports = loader
6
6
 
7
- function loader (mcVersion) {
8
- ChatMessage = require('prismarine-chat')(mcVersion) // TODO: update for prismarine-registry
7
+ function loader (registry) {
8
+ ChatMessage = require('prismarine-chat')(registry)
9
9
  return BossBar
10
10
  }
11
11
 
package/lib/loader.js CHANGED
@@ -55,7 +55,7 @@ module.exports = {
55
55
  BossBar: require('./bossbar'),
56
56
  supportedVersions,
57
57
  testedVersions,
58
- supportFeature: (feature, version) => require('minecraft-data')(version).supportFeature(feature)
58
+ supportFeature: (feature, version) => require('prismarine-registry')(version).supportFeature(feature)
59
59
  }
60
60
 
61
61
  function createBot (options = {}) {
@@ -111,7 +111,7 @@ function createBot (options = {}) {
111
111
  }
112
112
 
113
113
  const latestTestedVersion = testedVersions[testedVersions.length - 1]
114
- const latestProtocolVersion = require('minecraft-data')(latestTestedVersion).protocolVersion
114
+ const latestProtocolVersion = require('prismarine-registry')(latestTestedVersion).protocolVersion
115
115
  if (version.protocolVersion > latestProtocolVersion) {
116
116
  throw new Error(`Version ${version.minecraftVersion} is not supported. Latest supported version is ${latestTestedVersion}.`)
117
117
  }
@@ -5,7 +5,7 @@ const { once } = require('events')
5
5
  module.exports = inject
6
6
 
7
7
  function inject (bot) {
8
- const Item = require('prismarine-item')(bot.version)
8
+ const Item = require('prismarine-item')(bot.registry)
9
9
 
10
10
  const matchWindowType = window => /minecraft:(?:chipped_|damaged_)?anvil/.test(window.type)
11
11
 
@@ -146,10 +146,25 @@ function inject (bot) {
146
146
  }
147
147
  }
148
148
 
149
- await bot.activateBlock(bedBlock)
149
+ bot.activateBlock(bedBlock)
150
+
151
+ await waitUntilSleep()
150
152
  }
151
153
  }
152
154
 
155
+ async function waitUntilSleep () {
156
+ return new Promise((resolve, reject) => {
157
+ const timeoutForSleep = setTimeout(() => {
158
+ reject(new Error('bot is not sleeping'))
159
+ }, 3000)
160
+
161
+ bot.once('sleep', () => {
162
+ clearTimeout(timeoutForSleep)
163
+ resolve()
164
+ })
165
+ })
166
+ }
167
+
153
168
  bot._client.on('game_state_change', (packet) => {
154
169
  if (packet.reason === 0) {
155
170
  // occurs when you can't spawn in your bed and your spawn point gets reset
@@ -44,7 +44,7 @@ function inject (bot) {
44
44
  const perpendicularCardinals = Object.keys(FACING_MAP[facing])
45
45
  for (const cardinal of perpendicularCardinals) {
46
46
  const cardinalOffset = CARDINALS[cardinal]
47
- if (bot.blockAt(chestBlock.position.plus(cardinalOffset)).type === chestBlock.type) {
47
+ if (bot.blockAt(chestBlock.position.plus(cardinalOffset))?.type === chestBlock.type) {
48
48
  return FACING_MAP[cardinal][facing]
49
49
  }
50
50
  }
@@ -100,13 +100,14 @@ function inject (bot) {
100
100
  const destroyStage = packet.destroyStage
101
101
  const pt = new Vec3(packet.location.x, packet.location.y, packet.location.z)
102
102
  const block = bot.blockAt(pt)
103
+ const entity = bot.entities[packet.entityId]
103
104
 
104
105
  if (destroyStage < 0 || destroyStage > 9) {
105
106
  // http://wiki.vg/Protocol#Block_Break_Progress
106
107
  // "0-9 to set it, any other value to remove it"
107
- bot.emit('blockBreakProgressEnd', block)
108
+ bot.emit('blockBreakProgressEnd', block, entity)
108
109
  } else {
109
- bot.emit('blockBreakProgressObserved', block, destroyStage)
110
+ bot.emit('blockBreakProgressObserved', block, destroyStage, entity)
110
111
  }
111
112
  })
112
113
  }
@@ -21,9 +21,9 @@ const dimensionNames = {
21
21
  }
22
22
 
23
23
  function inject (bot, { version, storageBuilder }) {
24
- const Block = require('prismarine-block')(version)
25
- const Chunk = require('prismarine-chunk')(version)
26
- const World = require('prismarine-world')(version)
24
+ const Block = require('prismarine-block')(bot.registry)
25
+ const Chunk = require('prismarine-chunk')(bot.registry)
26
+ const World = require('prismarine-world')(bot.registry)
27
27
  const paintingsByPos = {}
28
28
  const paintingsById = {}
29
29
 
@@ -4,7 +4,7 @@ const { once } = require('events')
4
4
  module.exports = inject
5
5
 
6
6
  function inject (bot) {
7
- const Item = require('prismarine-item')(bot.version)
7
+ const Item = require('prismarine-item')(bot.registry)
8
8
 
9
9
  let editBook
10
10
  if (bot.supportFeature('editBookIsPluginChannel')) {
@@ -1,7 +1,7 @@
1
1
  module.exports = inject
2
2
 
3
3
  function inject (bot, { version }) {
4
- const BossBar = require('../bossbar')(version)
4
+ const BossBar = require('../bossbar')(bot.registry)
5
5
  const bars = {}
6
6
 
7
7
  bot._client.on('boss_bar', (packet) => {
@@ -1,12 +1,15 @@
1
1
  const { once } = require('events')
2
2
 
3
+ const USERNAME_REGEX = '(?:\\(.{1,15}\\)|\\[.{1,15}\\]|.){0,5}?(\\w+)'
4
+ const LEGACY_VANILLA_CHAT_REGEX = new RegExp(`^${USERNAME_REGEX}\\s?[>:\\-»\\]\\)~]+\\s(.*)$`)
5
+
3
6
  module.exports = inject
4
7
 
5
8
  function inject (bot, options) {
6
9
  const CHAT_LENGTH_LIMIT = options.chatLengthLimit ?? (bot.supportFeature('lessCharsInChat') ? 100 : 256)
7
10
  const defaultChatPatterns = options.defaultChatPatterns ?? true
8
11
 
9
- const ChatMessage = require('prismarine-chat')(bot.version)
12
+ const ChatMessage = require('prismarine-chat')(bot.registry)
10
13
  // chat.pattern.type will emit an event for bot.on() of the same type, eg chatType = whisper will trigger bot.on('whisper')
11
14
  const _patterns = {}
12
15
  let _length = 0
@@ -105,22 +108,43 @@ function inject (bot, options) {
105
108
 
106
109
  addDefaultPatterns()
107
110
 
111
+ // Pre 1.19
108
112
  bot._client.on('chat', (packet) => {
109
113
  const msg = ChatMessage.fromNotch(packet.message)
110
-
111
- const ChatPositions = {
114
+ const chatPositions = {
112
115
  0: 'chat',
113
116
  1: 'system',
114
117
  2: 'game_info'
115
118
  }
119
+ bot.emit('message', msg, chatPositions[packet.position], packet.sender, /* verified */ null)
120
+ bot.emit('messagestr', msg.toString(), chatPositions[packet.position], msg, packet.sender, /* verified */ null)
121
+ // Position 2 is the action bar
122
+ if (packet.position === 2) bot.emit('actionBar', msg, null)
123
+ })
116
124
 
117
- const chatPosition = ChatPositions[packet.position]
118
-
119
- bot.emit('message', msg, chatPosition)
120
- bot.emit('messagestr', msg.toString(), chatPosition, msg)
125
+ // 1.19+
126
+ bot._client.on('player_chat', (packet) => {
127
+ const message = packet.unsignedChatContent || packet.signedChatContent
128
+ let verified = false
129
+ const sender = bot.uuidToUsername[packet.senderUuid]
130
+ if (sender) {
131
+ const { profileKeys } = bot.players[sender]
132
+ if (profileKeys) verified = bot._client.verifyMessage(profileKeys.publicKey, packet)
133
+ }
134
+ const parameters = {
135
+ sender: JSON.parse(packet.senderName),
136
+ content: JSON.parse(message)
137
+ }
138
+ const msg = ChatMessage.fromNetwork(packet.type, parameters)
139
+ Object.assign(msg, parameters)
140
+ bot.emit('message', msg, 'chat', packet.senderUuid, verified)
141
+ bot.emit('messagestr', msg.toString(), 'chat', msg, packet.senderUuid, verified)
142
+ })
121
143
 
122
- // Position 2 is the action bar
123
- if (packet.position === 2) bot.emit('actionBar', msg)
144
+ bot._client.on('system_chat', (packet) => {
145
+ const msg = ChatMessage.fromNotch(packet.content)
146
+ bot.emit('message', msg, 'system', null)
147
+ bot.emit('messagestr', msg.toString(), 'system', msg, null)
124
148
  })
125
149
 
126
150
  function chatWithHeader (header, message) {
@@ -129,6 +153,22 @@ function inject (bot, options) {
129
153
  throw new Error('Incorrect type! Should be a string or number.')
130
154
  }
131
155
 
156
+ if (bot.supportFeature('signedChat')) {
157
+ if (message.startsWith('/')) {
158
+ // We send commands as Chat Command packet in 1.19+
159
+ const command = message.slice(1)
160
+ const timestamp = BigInt(Date.now())
161
+ bot._client.write('chat_command', {
162
+ command,
163
+ timestamp,
164
+ salt: 0n,
165
+ argumentSignatures: [],
166
+ signedPreview: false
167
+ })
168
+ return
169
+ }
170
+ }
171
+
132
172
  const lengthLimit = CHAT_LENGTH_LIMIT - header.length
133
173
  message.split('\n').forEach((subMessage) => {
134
174
  if (!subMessage) return
@@ -136,7 +176,17 @@ function inject (bot, options) {
136
176
  let smallMsg
137
177
  for (i = 0; i < subMessage.length; i += lengthLimit) {
138
178
  smallMsg = header + subMessage.substring(i, i + lengthLimit)
139
- bot._client.write('chat', { message: smallMsg })
179
+ if (bot.supportFeature('signedChat')) {
180
+ const timestamp = BigInt(Date.now())
181
+ bot._client.write('chat_message', {
182
+ message: smallMsg,
183
+ timestamp,
184
+ salt: 0,
185
+ signature: bot._client.profileKeys ? bot._client.signMessage(smallMsg, timestamp) : Buffer.alloc(0)
186
+ })
187
+ } else {
188
+ bot._client.write('chat', { message: smallMsg })
189
+ }
140
190
  }
141
191
  })
142
192
  }
@@ -172,11 +222,12 @@ function inject (bot, options) {
172
222
  bot.tabComplete = tabComplete
173
223
 
174
224
  function addDefaultPatterns () {
225
+ // 1.19 changes the chat format to move <sender> prefix from message contents to a seperate field.
226
+ // TODO: new chat lister to handle this
175
227
  if (!defaultChatPatterns) return
176
- const USERNAME_REGEX = '(?:\\(.+\\)|\\[.+\\]|.)*?(\\w+)'
177
228
  bot.addChatPattern('whisper', new RegExp(`^${USERNAME_REGEX} whispers(?: to you)?:? (.*)$`), { deprecated: true })
178
229
  bot.addChatPattern('whisper', new RegExp(`^\\[${USERNAME_REGEX} -> \\w+\\s?\\] (.*)$`), { deprecated: true })
179
- bot.addChatPattern('chat', new RegExp(`^${USERNAME_REGEX}\\s?[>:\\-»\\]\\)~]+\\s(.*)$`), { deprecated: true })
230
+ bot.addChatPattern('chat', LEGACY_VANILLA_CHAT_REGEX, { deprecated: true })
180
231
  }
181
232
 
182
233
  function awaitMessage (...args) {
@@ -1,9 +1,9 @@
1
+ const { Vec3 } = require('vec3')
1
2
 
2
3
  module.exports = inject
3
4
 
4
5
  function inject (bot) {
5
- const allowedWindowTypes = ['minecraft:generic', 'minecraft:chest', 'minecraft:dispenser', 'minecraft:shulker_box', 'minecraft:hopper', 'minecraft:container', 'minecraft:dropper', 'minecraft:trapped_chest']
6
-
6
+ const allowedWindowTypes = ['minecraft:generic', 'minecraft:chest', 'minecraft:dispenser', 'minecraft:ender_chest', 'minecraft:shulker_box', 'minecraft:hopper', 'minecraft:container', 'minecraft:dropper', 'minecraft:trapped_chest', 'minecraft:barrel', 'minecraft:white_shulker_box', 'minecraft:orange_shulker_box', 'minecraft:magenta_shulker_box', 'minecraft:light_blue_shulker_box', 'minecraft:yellow_shulker_box', 'minecraft:lime_shulker_box', 'minecraft:pink_shulker_box', 'minecraft:gray_shulker_box', 'minecraft:light_gray_shulker_box', 'minecraft:cyan_shulker_box', 'minecraft:purple_shulker_box', 'minecraft:blue_shulker_box', 'minecraft:brown_shulker_box', 'minecraft:green_shulker_box', 'minecraft:red_shulker_box', 'minecraft:black_shulker_box']
7
7
  function matchWindowType (window) {
8
8
  for (const type of allowedWindowTypes) {
9
9
  if (window.type.startsWith(type)) return true
@@ -11,10 +11,12 @@ function inject (bot) {
11
11
  return false
12
12
  }
13
13
 
14
- async function openContainer (containerToOpen) {
14
+ async function openContainer (containerToOpen, direction, cursorPos) {
15
+ direction = direction ?? new Vec3(0, 1, 0)
16
+ cursorPos = cursorPos ?? new Vec3(0.5, 0.5, 0.5)
15
17
  let chest
16
18
  if (containerToOpen.constructor.name === 'Block' && allowedWindowTypes.map(name => name.replace('minecraft:', '')).includes(containerToOpen.name)) {
17
- chest = await bot.openBlock(containerToOpen)
19
+ chest = await bot.openBlock(containerToOpen, direction, cursorPos)
18
20
  } else if (containerToOpen.constructor.name === 'Entity') {
19
21
  chest = await bot.openEntity(containerToOpen)
20
22
  } else {
@@ -8,7 +8,7 @@ function inject (bot) {
8
8
  assert.strictEqual(bot.player.gamemode, 1, new Error('The bot has to be in creative mode to open the command block window'))
9
9
  assert.notStrictEqual(pos, null)
10
10
  assert.notStrictEqual(command, null)
11
- assert.strictEqual(bot.blockAt(pos).name, 'command_block', new Error("The block isn't a command block"))
11
+ assert.strictEqual(bot.blockAt(pos).name.includes('command_block'), true, new Error("The block isn't a command block"))
12
12
 
13
13
  // Default values when a command block is placed in vanilla minecraft
14
14
  options.trackOutput = options.trackOutput ?? false
@@ -3,9 +3,9 @@ const { once } = require('events')
3
3
 
4
4
  module.exports = inject
5
5
 
6
- function inject (bot, { version }) {
7
- const Item = require('prismarine-item')(bot.version)
8
- const Recipe = require('prismarine-recipe')(version).Recipe // TODO: update for prismarine-registry
6
+ function inject (bot) {
7
+ const Item = require('prismarine-item')(bot.registry)
8
+ const Recipe = require('prismarine-recipe')(bot.registry).Recipe
9
9
 
10
10
  async function craft (recipe, count, craftingTable) {
11
11
  assert.ok(recipe)