mineflayer 4.12.0 → 4.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.
@@ -12,10 +12,10 @@ jobs:
12
12
 
13
13
  steps:
14
14
  - uses: actions/checkout@v2
15
- - name: Use Node.js 14.x
15
+ - name: Use Node.js 18.x
16
16
  uses: actions/setup-node@v1.4.4
17
17
  with:
18
- node-version: 14.x
18
+ node-version: 18.x
19
19
  - run: npm i && npm run lint
20
20
 
21
21
  MinecraftServer:
@@ -46,7 +46,7 @@ jobs:
46
46
  - name: Use Node.js ${{ matrix.node-version }}
47
47
  uses: actions/setup-node@v1.4.4
48
48
  with:
49
- node-version: 14.x
49
+ node-version: 18.x
50
50
  - name: Setup Java JDK
51
51
  uses: actions/setup-java@v1.4.3
52
52
  with:
@@ -13,7 +13,7 @@ jobs:
13
13
  - name: Set up Node.js
14
14
  uses: actions/setup-node@master
15
15
  with:
16
- node-version: 14.0.0
16
+ node-version: 18.0.0
17
17
  - id: publish
18
18
  uses: JS-DevTools/npm-publish@v1
19
19
  with:
package/.gitpod.yml CHANGED
@@ -1,4 +1,2 @@
1
- image:
2
- file: .gitpod.DockerFile
3
1
  tasks:
4
2
  - command: npm install
package/README.md CHANGED
@@ -80,11 +80,11 @@ const mineflayer = require('mineflayer')
80
80
 
81
81
  const bot = mineflayer.createBot({
82
82
  host: 'localhost', // minecraft server ip
83
- username: 'email@example.com', // minecraft username
83
+ username: 'Bot', // username or email, switch if you want to change accounts
84
84
  auth: 'microsoft' // for offline mode servers, you can set this to 'offline'
85
85
  // port: 25565, // only set if you need a port that isn't 25565
86
86
  // version: false, // only set if you need a specific version or snapshot (ie: "1.8.9" or "1.16.5"), otherwise it's set automatically
87
- // password: '12345678' // set if you want to use password-based auth (may be unreliable)
87
+ // password: '12345678' // set if you want to use password-based auth (may be unreliable). If specified, the `username` must be an email
88
88
  })
89
89
 
90
90
  bot.on('chat', (username, message) => {
@@ -97,6 +97,26 @@ bot.on('kicked', console.log)
97
97
  bot.on('error', console.log)
98
98
  ```
99
99
 
100
+ If `auth` is set to `microsoft`, you will be prompted to login to microsoft.com with a code in your browser. After signing in on your browser,
101
+ the bot will automatically obtain and cache authentication tokens in the local file system so you don't have to sign-in again.
102
+ To switch the account, update the supplied `username`. By default, cached tokens will be stored in your user's .minecraft folder.
103
+ For more information on these options and others, see node-minecraft-protocol's [API doc](https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/docs/API.md#mccreateclientoptions).
104
+
105
+ #### Connecting to a Realm
106
+
107
+ To join a Realm that your Minecraft account has been invited to, you can pass a `realms` object with a selector function like below.
108
+
109
+ ```js
110
+ const client = mineflayer.createBot({
111
+ username: 'email@example.com', // minecraft username
112
+ realms: {
113
+ // This function is called with an array of Realms the account can join. It should return the one it wants to join.
114
+ pickRealm: (realms) => realms[0]
115
+ },
116
+ auth: 'microsoft'
117
+ })
118
+ ```
119
+
100
120
  ### See what your bot is doing
101
121
 
102
122
  Thanks to the [prismarine-viewer](https://github.com/PrismarineJS/prismarine-viewer) project, it's possible to display in a browser window what your bot is doing.
@@ -220,6 +240,7 @@ The most updated and useful are :
220
240
  * [Chaoscraft](https://github.com/schematical/chaoscraft) - Minecraft bot using genetic algorithms, see [its youtube videos](https://www.youtube.com/playlist?list=PLLkpLgU9B5xJ7Qy4kOyBJl5J6zsDIMceH)
221
241
  * [hexatester/minetelegram](https://github.com/hexatester/minetelegram) - Minecraft - Telegram bridge, build on top of mineflayer & telegraf.
222
242
  * [PrismarineJS/mineflayer-builder](https://github.com/PrismarineJS/mineflayer-builder) - Prints minecraft schematics in survival, keeping orientation
243
+ * [SilkePilon/OpenDeliveryBot](https://github.com/SilkePilon/OpenDeliveryBot) - Minecraft bot in python to deliver items from place to place.
223
244
  * [and hundreds more](https://github.com/PrismarineJS/mineflayer/network/dependents) - All the projects that github detected are using mineflayer
224
245
 
225
246
 
package/docs/README.md CHANGED
@@ -80,11 +80,11 @@ const mineflayer = require('mineflayer')
80
80
 
81
81
  const bot = mineflayer.createBot({
82
82
  host: 'localhost', // minecraft server ip
83
- username: 'email@example.com', // minecraft username
83
+ username: 'Bot', // username or email, switch if you want to change accounts
84
84
  auth: 'microsoft' // for offline mode servers, you can set this to 'offline'
85
85
  // port: 25565, // only set if you need a port that isn't 25565
86
86
  // version: false, // only set if you need a specific version or snapshot (ie: "1.8.9" or "1.16.5"), otherwise it's set automatically
87
- // password: '12345678' // set if you want to use password-based auth (may be unreliable)
87
+ // password: '12345678' // set if you want to use password-based auth (may be unreliable). If specified, the `username` must be an email
88
88
  })
89
89
 
90
90
  bot.on('chat', (username, message) => {
@@ -97,6 +97,26 @@ bot.on('kicked', console.log)
97
97
  bot.on('error', console.log)
98
98
  ```
99
99
 
100
+ If `auth` is set to `microsoft`, you will be prompted to login to microsoft.com with a code in your browser. After signing in on your browser,
101
+ the bot will automatically obtain and cache authentication tokens in the local file system so you don't have to sign-in again.
102
+ To switch the account, update the supplied `username`. By default, cached tokens will be stored in your user's .minecraft folder.
103
+ For more information on these options and others, see node-minecraft-protocol's [API doc](https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/docs/API.md#mccreateclientoptions).
104
+
105
+ #### Connecting to a Realm
106
+
107
+ To join a Realm that your Minecraft account has been invited to, you can pass a `realms` object with a selector function like below.
108
+
109
+ ```js
110
+ const client = mineflayer.createBot({
111
+ username: 'email@example.com', // minecraft username
112
+ realms: {
113
+ // This function is called with an array of Realms the account can join. It should return the one it wants to join.
114
+ pickRealm: (realms) => realms[0]
115
+ },
116
+ auth: 'microsoft'
117
+ })
118
+ ```
119
+
100
120
  ### See what your bot is doing
101
121
 
102
122
  Thanks to the [prismarine-viewer](https://github.com/PrismarineJS/prismarine-viewer) project, it's possible to display in a browser window what your bot is doing.
@@ -220,6 +240,7 @@ The most updated and useful are :
220
240
  * [Chaoscraft](https://github.com/schematical/chaoscraft) - Minecraft bot using genetic algorithms, see [its youtube videos](https://www.youtube.com/playlist?list=PLLkpLgU9B5xJ7Qy4kOyBJl5J6zsDIMceH)
221
241
  * [hexatester/minetelegram](https://github.com/hexatester/minetelegram) - Minecraft - Telegram bridge, build on top of mineflayer & telegraf.
222
242
  * [PrismarineJS/mineflayer-builder](https://github.com/PrismarineJS/mineflayer-builder) - Prints minecraft schematics in survival, keeping orientation
243
+ * [SilkePilon/OpenDeliveryBot](https://github.com/SilkePilon/OpenDeliveryBot) - Minecraft bot in python to deliver items from place to place.
223
244
  * [and hundreds more](https://github.com/PrismarineJS/mineflayer/network/dependents) - All the projects that github detected are using mineflayer
224
245
 
225
246
 
package/docs/api.md CHANGED
@@ -136,6 +136,7 @@
136
136
  - [bot.foodSaturation](#botfoodsaturation)
137
137
  - [bot.oxygenLevel](#botoxygenlevel)
138
138
  - [bot.physics](#botphysics)
139
+ - [bot.fireworkRocketDuration](#botfireworkrocketduration)
139
140
  - [bot.simpleClick.leftMouse (slot)](#botsimpleclickleftmouse-slot)
140
141
  - [bot.simpleClick.rightMouse (slot)](#botsimpleclickrightmouse-slot)
141
142
  - [bot.time.doDaylightCycle](#bottimedodaylightcycle)
@@ -197,6 +198,7 @@
197
198
  - ["entityEquip" (entity)](#entityequip-entity)
198
199
  - ["entitySleep" (entity)](#entitysleep-entity)
199
200
  - ["entitySpawn" (entity)](#entityspawn-entity)
201
+ - ["entityElytraFlew" (entity)](#entityelytraflew-entity)
200
202
  - ["itemDrop" (entity)](#itemdrop-entity)
201
203
  - ["playerCollect" (collector, collected)](#playercollect-collector-collected)
202
204
  - ["entityGone" (entity)](#entitygone-entity)
@@ -223,6 +225,7 @@
223
225
  - ["blockBreakProgressEnd" (block, entity)](#blockbreakprogressend-block-entity)
224
226
  - ["diggingCompleted" (block)](#diggingcompleted-block)
225
227
  - ["diggingAborted" (block)](#diggingaborted-block)
228
+ - ["usedFirework"](#usedfirework)
226
229
  - ["move"](#move)
227
230
  - ["forcedMove"](#forcedmove)
228
231
  - ["mount"](#mount)
@@ -293,6 +296,7 @@
293
296
  - [bot.unequip(destination)](#botunequipdestination)
294
297
  - [bot.tossStack(item)](#bottossstackitem)
295
298
  - [bot.toss(itemType, metadata, count)](#bottossitemtype-metadata-count)
299
+ - [bot.elytraFly()](#botelytrafly)
296
300
  - [bot.dig(block, [forceLook = true], [digFace])](#botdigblock-forcelook--true-digface)
297
301
  - [bot.stopDigging()](#botstopdigging)
298
302
  - [bot.digTime(block)](#botdigtimeblock)
@@ -1037,6 +1041,10 @@ Number in the range [0, 20] respresenting the number of water-icons known as oxy
1037
1041
  Edit these numbers to tweak gravity, jump speed, terminal velocity, etc.
1038
1042
  Do this at your own risk.
1039
1043
 
1044
+ #### bot.fireworkRocketDuration
1045
+
1046
+ How many physics ticks worth of firework rocket boost are left.
1047
+
1040
1048
  #### bot.simpleClick.leftMouse (slot)
1041
1049
 
1042
1050
  abstraction over `bot.clickWindow(slot, 0, 0)`
@@ -1301,6 +1309,10 @@ Fires when an attribute of an entity changes.
1301
1309
  #### "entityEquip" (entity)
1302
1310
  #### "entitySleep" (entity)
1303
1311
  #### "entitySpawn" (entity)
1312
+ #### "entityElytraFlew" (entity)
1313
+
1314
+ An entity started elytra flying.
1315
+
1304
1316
  #### "itemDrop" (entity)
1305
1317
  #### "playerCollect" (collector, collected)
1306
1318
 
@@ -1417,6 +1429,10 @@ This occurs whether the process was completed or aborted.
1417
1429
 
1418
1430
  * `block` - the block that still exists
1419
1431
 
1432
+ #### "usedfirework"
1433
+
1434
+ Fires when the bot uses a firework while elytra flying.
1435
+
1420
1436
  #### "move"
1421
1437
 
1422
1438
  Fires when the bot moves. If you want the current position, use
@@ -1851,6 +1867,11 @@ This function returns a `Promise`, with `void` as its argument once tossing is c
1851
1867
  to match any metadata
1852
1868
  * `count` - how many you want to toss. `null` is an alias for `1`.
1853
1869
 
1870
+ #### bot.elytraFly()
1871
+
1872
+ This function returns a `Promise`, with `void` as its argument once activating
1873
+ elytra flight is complete. It will throw an Error if it fails.
1874
+
1854
1875
  #### bot.dig(block, [forceLook = true], [digFace])
1855
1876
 
1856
1877
  This function returns a `Promise`, with `void` as its argument when the block is broken or you are interrupted.
@@ -1946,7 +1967,9 @@ Use fishing rod
1946
1967
 
1947
1968
  #### bot.activateItem(offHand=false)
1948
1969
 
1949
- Activates the currently held item. This is how you eat, shoot bows, throw an egg, etc.
1970
+ Activates the currently held item. This is how you eat, shoot bows, throw an
1971
+ egg, activate firework rockets, etc.
1972
+
1950
1973
  Optional parameter is `false` for main hand and `true` for off hand.
1951
1974
 
1952
1975
  #### bot.deactivateItem()
package/docs/history.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 4.14.0
2
+ * [Update Jupyter notebook to install node 18, update the server in example (#3176)](https://github.com/PrismarineJS/mineflayer/commit/e8a967d4e832f72d665781492c037d26169ae5a0) (thanks @extremeheat)
3
+ * [Update index.d.ts (#3175)](https://github.com/PrismarineJS/mineflayer/commit/d4db3991c135344180937b69621c0ee31daa39f0) (thanks @StayWithMeSenpai)
4
+ * [Add elytra flying support and rocket support (#3163)](https://github.com/PrismarineJS/mineflayer/commit/010460e9dd752a56195d8a48f35a62e704dcf99f) (thanks @lkwilson)
5
+
6
+ ## 4.13.0
7
+ * [Switch to entity.displayName (#3168)](https://github.com/PrismarineJS/mineflayer/commit/2409ad458b952173de669a7d9cfaeb770effe3ae) (thanks @lkwilson)
8
+ * [Update readme auth doc (#3169)](https://github.com/PrismarineJS/mineflayer/commit/f5d4a288a768ca6717fa4d22c72fb0267428c684) (thanks @extremeheat)
9
+ * [Add OpenDeliveryBot to "Projects Using Mineflayer" (#3162)](https://github.com/PrismarineJS/mineflayer/commit/ab3c0cf25d0cc28ccba89640b2ceff6ab6b4dace) (thanks @SilkePilon)
10
+ * [Use node 18 on CI (#3157)](https://github.com/PrismarineJS/mineflayer/commit/d3df34dcaa804a71bf0d8cc50a419990d4a2dce3) (thanks @extremeheat)
11
+ * [Fix ambigious function naming (#3161)](https://github.com/PrismarineJS/mineflayer/commit/9ecdf201794bfa350486839a01e318dfd94b3bfb) (thanks @frej4189)
12
+
1
13
  ## 4.12.0
2
14
  * [Mineflayer physics refactor (#2492)](https://github.com/PrismarineJS/mineflayer/commit/d0eb3a1afe6cda7b04ae2f88052cd868ba0c0c4f) (thanks @U5B)
3
15
 
@@ -36,7 +36,7 @@
36
36
  "source": [
37
37
  "# Using mineflayer in Python\n",
38
38
  "\n",
39
- "This is a tutorial on how to use mineflayer in Python. This example will connect you to the PrismarineJS test server. You can join it with prismarine-viewer or your Minecraft client at server IP **95.111.249.143:10000**.\n",
39
+ "This is a tutorial on how to use mineflayer in Python. This example will connect you to the PrismarineJS test server. You can join it with prismarine-viewer or your Minecraft client at server IP **pjs.deptofcraft.com:25565**.\n",
40
40
  "\n",
41
41
  "If you're new to Jupyter Notebooks, you can press the \"Play\" button at the left of each code block to run it. Make sure that you run the blocks in a correct order."
42
42
  ]
@@ -56,32 +56,35 @@
56
56
  "id": "K2ol06QOhL6s"
57
57
  },
58
58
  "source": [
59
- "First, make sure you have Python version 3.7 and Node.js version 14 or newer installed"
59
+ "First, make sure you have Python version 3.10 and Node.js version 18 or newer installed. You can get Node.js 18 it from https://nodejs.org/en/download or use [Node.js version managers](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm#using-a-node-version-manager-to-install-nodejs-and-npm) like [`nvm`](https://github.com/creationix/nvm) or [`n`](https://github.com/tj/n) to install via the command line. Here we'll use `n` to install Node.js v18, then check our Node and Python versions:"
60
60
  ]
61
61
  },
62
62
  {
63
63
  "cell_type": "code",
64
+ "execution_count": 1,
64
65
  "metadata": {
65
66
  "colab": {
66
67
  "base_uri": "https://localhost:8080/"
67
68
  },
68
69
  "id": "8zCSpx8Bif5m",
69
- "outputId": "f8888a33-03e7-4497-9776-ef6c34d9d337"
70
+ "outputId": "90ebac14-fc75-4136-f81d-34c5b2033da0"
70
71
  },
71
- "source": [
72
- "!python --version\n",
73
- "!node --version"
74
- ],
75
- "execution_count": null,
76
72
  "outputs": [
77
73
  {
74
+ "name": "stdout",
78
75
  "output_type": "stream",
79
76
  "text": [
80
- "Python 3.7.11\n",
81
- "v14.16.0\n"
82
- ],
83
- "name": "stdout"
77
+ "v18.17.1\n",
78
+ "Python 3.10.12\n"
79
+ ]
84
80
  }
81
+ ],
82
+ "source": [
83
+ "# Use `n` to install nodejs 18, if it's not already installed:\n",
84
+ "!curl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n | bash -s lts > /dev/null\n",
85
+ "# Now write the Node.js and Python version to the console\n",
86
+ "!node --version\n",
87
+ "!python --version"
85
88
  ]
86
89
  },
87
90
  {
@@ -121,7 +124,7 @@
121
124
  "source": [
122
125
  "If all is well, we can import the `javascript` library. We can then import the `require` function which works similarly to the `require` function in Node.js, but does the dependency management for us.\n",
123
126
  "\n",
124
- "You may notice the extra imports : On, Once, off and AsyncTask. These will be discussed later on. \n",
127
+ "You may notice the extra imports : On, Once, off and AsyncTask. These will be discussed later on.\n",
125
128
  "\n",
126
129
  "\n"
127
130
  ]
@@ -170,21 +173,21 @@
170
173
  },
171
174
  {
172
175
  "cell_type": "code",
176
+ "execution_count": null,
173
177
  "metadata": {
174
178
  "id": "1gfZSAUCDVMg"
175
179
  },
180
+ "outputs": [],
176
181
  "source": [
177
182
  "random_number = id([]) % 1000 # Give us a random number upto 1000\n",
178
183
  "BOT_USERNAME = f'colab_{random_number}'\n",
179
184
  "\n",
180
- "bot = mineflayer.createBot({ 'host': '95.111.249.143', 'port': 10000, 'username': BOT_USERNAME, 'hideErrors': False })\n",
185
+ "bot = mineflayer.createBot({ 'host': 'pjs.deptofcraft.com', 'port': 25565, 'username': BOT_USERNAME, 'hideErrors': False })\n",
181
186
  "\n",
182
- "# The spawn event \n",
187
+ "# The spawn event\n",
183
188
  "once(bot, 'login')\n",
184
189
  "bot.chat('I spawned')"
185
- ],
186
- "execution_count": null,
187
- "outputs": []
190
+ ]
188
191
  },
189
192
  {
190
193
  "cell_type": "markdown",
@@ -26,7 +26,7 @@ bot.on('chat', async (username, message) => {
26
26
  const [mainCommand, subCommand] = message.split(' ')
27
27
  if (mainCommand !== 'equip' && mainCommand !== 'unequip') return
28
28
 
29
- const armorStand = bot.nearestEntity(e => e.mobType === 'Armor Stand' && bot.entity.position.distanceTo(e.position) < 4)
29
+ const armorStand = bot.nearestEntity(e => e.displayName === 'Armor Stand' && bot.entity.position.distanceTo(e.position) < 4)
30
30
  if (!armorStand) {
31
31
  bot.chat('No armor stands nearby!')
32
32
  return
@@ -176,11 +176,11 @@ bot.on('playerCollect', (collector, collected) => {
176
176
 
177
177
  bot.on('entitySpawn', (entity) => {
178
178
  if (entity.type === 'mob') {
179
- console.log(`Look out! A ${entity.mobType} spawned at ${entity.position}`)
179
+ console.log(`Look out! A ${entity.displayName} spawned at ${entity.position}`)
180
180
  } else if (entity.type === 'player') {
181
181
  bot.chat(`Look who decided to show up: ${entity.username}`)
182
182
  } else if (entity.type === 'object') {
183
- console.log(`There's a ${entity.objectType} at ${entity.position}`)
183
+ console.log(`There's a ${entity.displayName} at ${entity.position}`)
184
184
  } else if (entity.type === 'global') {
185
185
  bot.chat('Ooh lightning!')
186
186
  } else if (entity.type === 'orb') {
@@ -189,7 +189,7 @@ bot.on('entitySpawn', (entity) => {
189
189
  })
190
190
  bot.on('entityHurt', (entity) => {
191
191
  if (entity.type === 'mob') {
192
- bot.chat(`Haha! The ${entity.mobType} got hurt!`)
192
+ bot.chat(`Haha! The ${entity.displayName} got hurt!`)
193
193
  } else if (entity.type === 'player') {
194
194
  bot.chat(`Aww, poor ${entity.username} got hurt. Maybe you shouldn't have a ping of ${bot.players[entity.username].ping}`)
195
195
  }
@@ -214,12 +214,12 @@ bot.on('entityEat', (entity) => {
214
214
  })
215
215
  bot.on('entityAttach', (entity, vehicle) => {
216
216
  if (entity.type === 'player' && vehicle.type === 'object') {
217
- bot.chat(`Sweet, ${entity.username} is riding that ${vehicle.objectType}`)
217
+ bot.chat(`Sweet, ${entity.username} is riding that ${vehicle.displayName}`)
218
218
  }
219
219
  })
220
220
  bot.on('entityDetach', (entity, vehicle) => {
221
221
  if (entity.type === 'player' && vehicle.type === 'object') {
222
- bot.chat(`Lame, ${entity.username} stopped riding the ${vehicle.objectType}`)
222
+ bot.chat(`Lame, ${entity.username} stopped riding the ${vehicle.displayName}`)
223
223
  }
224
224
  })
225
225
  bot.on('entityEquipmentChange', (entity) => {
@@ -0,0 +1,66 @@
1
+ // This example will shoot the player that said "fire" in chat, when it is said in chat.
2
+ const mineflayer = require('mineflayer')
3
+
4
+ if (process.argv.length < 4 || process.argv.length > 6) {
5
+ console.log('Usage : node elytra.js <host> <port> [<name>] [<password>]')
6
+ process.exit(1)
7
+ }
8
+
9
+ const bot = mineflayer.createBot({
10
+ host: process.argv[2],
11
+ port: parseInt(process.argv[3]),
12
+ username: process.argv[4] ? process.argv[4] : 'elytraer',
13
+ password: process.argv[5]
14
+ })
15
+
16
+ bot.on('error', err => {
17
+ console.log(err)
18
+ })
19
+
20
+ bot.on('kicked', err => {
21
+ console.log(err)
22
+ })
23
+
24
+ bot.on('spawn', async function () {
25
+ bot.chat(`/give ${bot.username} minecraft:elytra`)
26
+ bot.chat(`/give ${bot.username} minecraft:firework_rocket 64`)
27
+
28
+ await sleep(1000)
29
+ const elytraItem = bot.inventory.slots.find(item => item?.name === 'elytra')
30
+ if (elytraItem == null) {
31
+ console.log('no elytra')
32
+ return
33
+ }
34
+ await bot.equip(elytraItem, 'torso')
35
+ const fireworkItem = bot.inventory.slots.find(item => item?.name === 'firework_rocket')
36
+ if (fireworkItem == null) {
37
+ console.log('no fireworks')
38
+ return
39
+ }
40
+ await bot.equip(fireworkItem, 'hand')
41
+ })
42
+
43
+ bot.on('chat', async (username, message) => {
44
+ if (message === 'fly') {
45
+ await bot.look(bot.entity.yaw, 50 * Math.PI / 180)
46
+ bot.setControlState('jump', true)
47
+ bot.setControlState('jump', false)
48
+ await sleep(50)
49
+
50
+ // try to fly
51
+ try {
52
+ await bot.elytraFly()
53
+ } catch (err) {
54
+ bot.chat(`Failed to fly: ${err}`)
55
+ return
56
+ }
57
+ await sleep(50)
58
+
59
+ // use rocket
60
+ bot.activateItem()
61
+ }
62
+ })
63
+
64
+ function sleep (ms) {
65
+ return new Promise(resolve => setTimeout(resolve, ms))
66
+ }
package/examples/guard.js CHANGED
@@ -59,7 +59,7 @@ bot.on('physicsTick', () => {
59
59
 
60
60
  // Only look for mobs within 16 blocks
61
61
  const filter = e => e.type === 'mob' && e.position.distanceTo(bot.entity.position) < 16 &&
62
- e.mobType !== 'Armor Stand' // Mojang classifies armor stands as mobs for some reason?
62
+ e.displayName !== 'Armor Stand' // Mojang classifies armor stands as mobs for some reason?
63
63
 
64
64
  const entity = bot.nearestEntity(filter)
65
65
  if (entity) {
@@ -108,9 +108,9 @@ bot.once('spawn', () => {
108
108
  })
109
109
 
110
110
  bot.on('mount', () => {
111
- bot.chat(`mounted ${bot.vehicle.objectType}`)
111
+ bot.chat(`mounted ${bot.vehicle.displayName}`)
112
112
  })
113
113
 
114
114
  bot.on('dismount', (vehicle) => {
115
- bot.chat(`dismounted ${vehicle.objectType}`)
115
+ bot.chat(`dismounted ${vehicle.displayName}`)
116
116
  })
@@ -224,12 +224,12 @@ def playerCollect(this, collector, collected):
224
224
  def entitySpawn(this, entity):
225
225
  if entity.type == "mob":
226
226
  p = entity.position
227
- console.log(f"Look out! A {entity.mobType} spawned at {p.toString()}")
227
+ console.log(f"Look out! A {entity.displayName} spawned at {p.toString()}")
228
228
  elif entity.type == "player":
229
229
  bot.chat(f"Look who decided to show up: {entity.username}")
230
230
  elif entity.type == "object":
231
231
  p = entity.position
232
- console.log(f"There's a {entity.objectType} at {p.toString()}")
232
+ console.log(f"There's a {entity.displayName} at {p.toString()}")
233
233
  elif entity.type == "global":
234
234
  bot.chat("Ooh lightning!")
235
235
  elif entity.type == "orb":
@@ -239,7 +239,7 @@ def entitySpawn(this, entity):
239
239
  @On(bot, "entityHurt")
240
240
  def entityHurt(this, entity):
241
241
  if entity.type == "mob":
242
- bot.chat(f"Haha! The ${entity.mobType} got hurt!")
242
+ bot.chat(f"Haha! The ${entity.displayName} got hurt!")
243
243
  elif entity.type == "player":
244
244
  if entity.username in bot.players:
245
245
  ping = bot.players[entity.username].ping
@@ -279,13 +279,13 @@ def entityEat(this, entity):
279
279
  @On(bot, "entityAttach")
280
280
  def entityAttach(this, entity, vehicle):
281
281
  if entity.type == "player" and vehicle.type == "object":
282
- print(f"Sweet, {entity.username} is riding that {vehicle.objectType}")
282
+ print(f"Sweet, {entity.username} is riding that {vehicle.displayName}")
283
283
 
284
284
 
285
285
  @On(bot, "entityDetach")
286
286
  def entityDetach(this, entity, vehicle):
287
287
  if entity.type == "player" and vehicle.type == "object":
288
- print(f"Lame, {entity.username} stopped riding the {vehicle.objectType}")
288
+ print(f"Lame, {entity.username} stopped riding the {vehicle.displayName}")
289
289
 
290
290
 
291
291
  @On(bot, "entityEquipmentChange")
package/index.d.ts CHANGED
@@ -8,6 +8,7 @@ import { Recipe } from 'prismarine-recipe'
8
8
  import { Block } from 'prismarine-block'
9
9
  import { Entity } from 'prismarine-entity'
10
10
  import { ChatMessage } from 'prismarine-chat'
11
+ import { world } from 'prismarine-world'
11
12
  import { Registry } from 'prismarine-registry'
12
13
 
13
14
  export function createBot (options: { client: Client } & Partial<BotOptions>): Bot
@@ -92,6 +93,8 @@ export interface BotEvents {
92
93
  entityEquip: (entity: Entity) => Promise<void> | void
93
94
  entitySleep: (entity: Entity) => Promise<void> | void
94
95
  entitySpawn: (entity: Entity) => Promise<void> | void
96
+ entityElytraFlew: (entity: Entity) => Promise<void> | void
97
+ usedFirework: () => Promise<void> | void
95
98
  itemDrop: (entity: Entity) => Promise<void> | void
96
99
  playerCollect: (collector: Entity, collected: Entity) => Promise<void> | void
97
100
  entityAttributes: (entity: Entity) => Promise<void> | void
@@ -164,6 +167,7 @@ export interface Bot extends TypedEmitter<BotEvents> {
164
167
  version: string
165
168
  entity: Entity
166
169
  entities: { [id: string]: Entity }
170
+ fireworkRocketDuration: number
167
171
  spawnPoint: Vec3
168
172
  game: GameState
169
173
  player: Player
@@ -189,7 +193,7 @@ export interface Bot extends TypedEmitter<BotEvents> {
189
193
  teamMap: { [name: string]: Team }
190
194
  controlState: ControlStateStatus
191
195
  creative: creativeMethods
192
- world: any
196
+ world: world.World
193
197
  _client: Client
194
198
  heldItem: Item | null
195
199
  usingHeldItem: boolean
@@ -260,6 +264,8 @@ export interface Bot extends TypedEmitter<BotEvents> {
260
264
 
261
265
  wake: () => Promise<void>
262
266
 
267
+ elytraFly: () => Promise<void>
268
+
263
269
  setControlState: (control: ControlState, state: boolean) => void
264
270
 
265
271
  getControlState: (control: ControlState) => boolean
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
- if (typeof process !== 'undefined' && parseInt(process.versions.node.split('.')[0]) < 14) {
1
+ if (typeof process !== 'undefined' && parseInt(process.versions.node.split('.')[0]) < 18) {
2
2
  console.error('Your node version is currently', process.versions.node)
3
- console.error('Please update it to a version >= 14.x.x from https://nodejs.org/')
3
+ console.error('Please update it to a version >= 18.x.x from https://nodejs.org/')
4
4
  process.exit(1)
5
5
  }
6
6
 
@@ -176,8 +176,6 @@ function inject (bot) {
176
176
  entityData = entitiesArray.find(entity => entity.internalId === type)
177
177
  }
178
178
  if (entityData) {
179
- entity.mobType = entityData.displayName
180
- entity.objectType = entityData.displayName
181
179
  entity.displayName = entityData.displayName
182
180
  entity.entityType = entityData.id
183
181
  entity.name = entityData.name
@@ -188,7 +186,6 @@ function inject (bot) {
188
186
  // unknown entity
189
187
  entity.type = 'other'
190
188
  entity.entityType = type
191
- entity.mobType = 'unknown'
192
189
  entity.displayName = 'unknown'
193
190
  entity.name = 'unknown'
194
191
  entity.kind = 'unknown'
@@ -361,6 +358,70 @@ function inject (bot) {
361
358
  }
362
359
  })
363
360
 
361
+ bot.fireworkRocketDuration = 0
362
+ function setElytraFlyingState (entity, elytraFlying) {
363
+ let startedFlying = false
364
+ if (elytraFlying) {
365
+ startedFlying = !entity.elytraFlying
366
+ entity.elytraFlying = true
367
+ } else if (entity.elytraFlying) {
368
+ entity.elytraFlying = false
369
+ }
370
+ if (bot.fireworkRocketDuration !== 0 && entity.id === bot.entity?.id && !elytraFlying) {
371
+ bot.fireworkRocketDuration = 0
372
+ knownFireworks.splice(0, knownFireworks.length)
373
+ }
374
+
375
+ if (startedFlying) {
376
+ bot.emit('entityElytraFlew', entity)
377
+ }
378
+ }
379
+
380
+ const knownFireworks = []
381
+ function handleBotUsedFireworkRocket (fireworkEntityId, fireworkInfo) {
382
+ if (knownFireworks.includes(fireworkEntityId)) return
383
+ knownFireworks.push(fireworkEntityId)
384
+ let flightDur = 1
385
+ if (fireworkInfo?.nbtData != null) {
386
+ let nbt = fireworkInfo.nbtData
387
+ if (nbt.type === 'compound' && nbt.value.Fireworks != null) {
388
+ nbt = nbt.value.Fireworks
389
+ if (nbt.type === 'compound' && nbt.value.Flight != null) {
390
+ nbt = nbt.value.Flight
391
+ if (nbt.type === 'int') {
392
+ flightDur += nbt.value
393
+ }
394
+ }
395
+ }
396
+ }
397
+ const baseDuration = 10 * flightDur
398
+ const randomDuration = Math.floor(Math.random() * 6) + Math.floor(Math.random() * 7)
399
+ bot.fireworkRocketDuration = baseDuration + randomDuration
400
+
401
+ bot.emit('usedFirework')
402
+ }
403
+
404
+ let fireworkEntityName
405
+ if (bot.supportFeature('fireworkNamePlural')) {
406
+ fireworkEntityName = 'fireworks_rocket'
407
+ } else if (bot.supportFeature('fireworkNameSingular')) {
408
+ fireworkEntityName = 'firework_rocket'
409
+ }
410
+
411
+ let fireworkMetadataIdx
412
+ let fireworkMetadataIsOpt
413
+ if (bot.supportFeature('fireworkMetadataVarInt7')) {
414
+ fireworkMetadataIdx = 7
415
+ fireworkMetadataIsOpt = false
416
+ } else if (bot.supportFeature('fireworkMetadataOptVarInt8')) {
417
+ fireworkMetadataIdx = 8
418
+ fireworkMetadataIsOpt = true
419
+ } else if (bot.supportFeature('fireworkMetadataOptVarInt9')) {
420
+ fireworkMetadataIdx = 9
421
+ fireworkMetadataIsOpt = true
422
+ }
423
+ const hasFireworkSupport = fireworkEntityName !== undefined && fireworkMetadataIdx !== undefined && fireworkMetadataIsOpt !== undefined
424
+
364
425
  bot._client.on('entity_metadata', (packet) => {
365
426
  // entity metadata
366
427
  const entity = fetchEntity(packet.entityId)
@@ -377,7 +438,25 @@ function inject (bot) {
377
438
  if (metas.sleeping_pos || metas.pose === 2) {
378
439
  bot.emit('entitySleep', entity)
379
440
  }
441
+
442
+ if (hasFireworkSupport && fireworkEntityName === entity.name && metas.attached_to_target !== undefined) {
443
+ // fireworkMetadataOptVarInt9 and later is implied by
444
+ // mcDataHasEntityMetadata, so no need to check metadata index and type
445
+ // (eg fireworkMetadataOptVarInt8)
446
+ if (metas.attached_to_target !== 0) {
447
+ const entityId = metas.attached_to_target - 1
448
+ if (entityId === bot.entity?.id) {
449
+ handleBotUsedFireworkRocket(entity.id, metas.fireworks_item)
450
+ }
451
+ }
452
+ }
453
+
380
454
  if (metas.shared_flags != null) {
455
+ if (bot.supportFeature('hasElytraFlying')) {
456
+ const elytraFlying = metas.shared_flags & 0x80
457
+ setElytraFlyingState(entity, Boolean(elytraFlying))
458
+ }
459
+
381
460
  if (metas.shared_flags & 2) {
382
461
  entity.crouching = true
383
462
  bot.emit('entityCrouch', entity)
@@ -399,16 +478,38 @@ function inject (bot) {
399
478
  bot.emit('entitySleep', entity)
400
479
  }
401
480
 
402
- const bitField = packet.metadata.find(p => p.key === 0)
403
- if (bitField === undefined) {
404
- return
481
+ if (hasFireworkSupport && fireworkEntityName === entity.name) {
482
+ const attachedToTarget = packet.metadata.find(e => e.key === fireworkMetadataIdx)
483
+ if (attachedToTarget !== undefined) {
484
+ let entityId
485
+ if (fireworkMetadataIsOpt) {
486
+ if (attachedToTarget.value !== 0) {
487
+ entityId = attachedToTarget.value - 1
488
+ } // else, not attached to an entity
489
+ } else {
490
+ entityId = attachedToTarget.value
491
+ }
492
+ if (entityId !== undefined && entityId === bot.entity?.id) {
493
+ const fireworksItem = packet.metadata.find(e => e.key === (fireworkMetadataIdx - 1))
494
+ handleBotUsedFireworkRocket(entity.id, fireworksItem?.value)
495
+ }
496
+ }
405
497
  }
406
- if ((bitField.value & 2) !== 0) {
407
- entity.crouching = true
408
- bot.emit('entityCrouch', entity)
409
- } else if (entity.crouching) { // prevent the initial entity_metadata packet from firing off an uncrouch event
410
- entity.crouching = false
411
- bot.emit('entityUncrouch', entity)
498
+
499
+ const bitField = packet.metadata.find(p => p.key === 0)
500
+ if (bitField !== undefined) {
501
+ if (bot.supportFeature('hasElytraFlying')) {
502
+ const elytraFlying = bitField.value & 0x80
503
+ setElytraFlyingState(entity, Boolean(elytraFlying))
504
+ }
505
+
506
+ if ((bitField.value & 2) !== 0) {
507
+ entity.crouching = true
508
+ bot.emit('entityCrouch', entity)
509
+ } else if (entity.crouching) { // prevent the initial entity_metadata packet from firing off an uncrouch event
510
+ entity.crouching = false
511
+ bot.emit('entityUncrouch', entity)
512
+ }
412
513
  }
413
514
  }
414
515
  })
@@ -138,8 +138,8 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
138
138
  return dYaw
139
139
  }
140
140
 
141
- // returns false if packet should be sent, true if not
142
- function sendPositionPacketInDeath () {
141
+ // returns false if bot should send position packets
142
+ function isEntityRemoved () {
143
143
  if (bot.isAlive === true) deadTicks = 0
144
144
  if (bot.isAlive === false && deadTicks <= 20) deadTicks++
145
145
  if (deadTicks >= 20) return true
@@ -148,7 +148,7 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
148
148
 
149
149
  function updatePosition (now) {
150
150
  // Only send updates for 20 ticks after death
151
- if (sendPositionPacketInDeath()) return
151
+ if (isEntityRemoved()) return
152
152
 
153
153
  // Increment the yaw in baby steps so that notchian clients (not the server) can keep up.
154
154
  const dYaw = deltaYaw(bot.entity.yaw, lastSentYaw)
@@ -192,6 +192,44 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
192
192
 
193
193
  bot.physics = physics
194
194
 
195
+ function getEffectLevel (mcData, effectName, effects) {
196
+ const effectDescriptor = mcData.effectsByName[effectName]
197
+ if (!effectDescriptor) {
198
+ return 0
199
+ }
200
+ const effectInfo = effects[effectDescriptor.id]
201
+ if (!effectInfo) {
202
+ return 0
203
+ }
204
+ return effectInfo.amplifier + 1
205
+ }
206
+
207
+ bot.elytraFly = async () => {
208
+ if (bot.entity.elytraFlying) {
209
+ throw new Error('Already elytra flying')
210
+ } else if (bot.entity.onGround) {
211
+ throw new Error('Unable to fly from ground')
212
+ } else if (bot.entity.isInWater) {
213
+ throw new Error('Unable to elytra fly while in water')
214
+ }
215
+
216
+ const mcData = require('minecraft-data')(bot.version)
217
+ if (getEffectLevel(mcData, 'Levitation', bot.entity.effects) > 0) {
218
+ throw new Error('Unable to elytra fly with levitation effect')
219
+ }
220
+
221
+ const torsoSlot = bot.getEquipmentDestSlot('torso')
222
+ const item = bot.inventory.slots[torsoSlot]
223
+ if (item == null || item.name !== 'elytra') {
224
+ throw new Error('Elytra must be equip to start flying')
225
+ }
226
+ bot._client.write('entity_action', {
227
+ entityId: bot.entity.id,
228
+ actionId: 8,
229
+ jumpBoost: 0
230
+ })
231
+ }
232
+
195
233
  bot.setControlState = (control, state) => {
196
234
  assert.ok(control in controlState, `invalid control: ${control}`)
197
235
  assert.ok(typeof state === 'boolean', `invalid state: ${state}`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mineflayer",
3
- "version": "4.12.0",
3
+ "version": "4.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,16 +21,16 @@
21
21
  },
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
- "minecraft-data": "^3.37.0",
24
+ "minecraft-data": "^3.44.0",
25
25
  "minecraft-protocol": "^1.44.0",
26
26
  "prismarine-biome": "^1.1.1",
27
27
  "prismarine-block": "^1.17.0",
28
28
  "prismarine-chat": "^1.7.1",
29
29
  "prismarine-chunk": "^1.34.0",
30
- "prismarine-entity": "^2.2.0",
30
+ "prismarine-entity": "^2.3.0",
31
31
  "prismarine-item": "^1.14.0",
32
32
  "prismarine-nbt": "^2.0.0",
33
- "prismarine-physics": "^1.7.0",
33
+ "prismarine-physics": "^1.8.0",
34
34
  "prismarine-recipe": "^1.3.0",
35
35
  "prismarine-registry": "^1.5.0",
36
36
  "prismarine-windows": "^2.8.0",
@@ -1,11 +0,0 @@
1
- FROM gitpod/workspace-full:latest
2
-
3
- RUN bash -c ". /home/gitpod/.sdkman/bin/sdkman-init.sh \
4
- && sdk install java"
5
-
6
- RUN bash -c ". .nvm/nvm.sh \
7
- && nvm install 14 \
8
- && nvm use 14 \
9
- && nvm alias default 14"
10
-
11
- RUN echo "nvm use default &>/dev/null" >> ~/.bashrc.d/51-nvm-fix