Skip to content

Commit 6c0a710

Browse files
authored
Merge branch 'master' into fix-velocity-configuration-phase
2 parents e3784d1 + 47a41c6 commit 6c0a710

23 files changed

Lines changed: 1006 additions & 143 deletions

.github/workflows/ci.yml

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
- name: Use Node.js 22.x
1616
uses: actions/setup-node@v1.4.4
1717
with:
18-
node-version: 22.x
18+
node-version: 24
1919
- run: npm i && npm run lint
2020

2121
PrepareSupportedVersions:
@@ -28,27 +28,33 @@ jobs:
2828
- name: Use Node.js 22.x
2929
uses: actions/setup-node@v1.4.4
3030
with:
31-
node-version: 22.x
31+
node-version: 24
3232
- id: set-matrix
3333
run: |
3434
node -e "
35-
const testedVersions = require('./lib/version').testedVersions;
36-
console.log('matrix='+JSON.stringify({'include': testedVersions.map(mcVersion => ({mcVersion}))}))
35+
const v = require('./lib/version').testedVersions;
36+
const groups = [];
37+
const SIZE = 4;
38+
for (let i = 0; i < v.length; i += SIZE) {
39+
groups.push({ versions: v.slice(i, i + SIZE).join(' ') });
40+
}
41+
console.log('matrix='+JSON.stringify({'include': groups}))
3742
" >> $GITHUB_OUTPUT
3843
3944
MinecraftServer:
4045
needs: PrepareSupportedVersions
4146
runs-on: ubuntu-latest
47+
name: MC ${{ matrix.versions }}
4248
strategy:
43-
matrix: ${{fromJson(needs.PrepareSupportedVersions.outputs.matrix)}}
49+
matrix: ${{ fromJson(needs.PrepareSupportedVersions.outputs.matrix) }}
4450
fail-fast: false
4551

4652
steps:
4753
- uses: actions/checkout@v2
4854
- name: Use Node.js 22.x
4955
uses: actions/setup-node@v1.4.4
5056
with:
51-
node-version: 22.x
57+
node-version: 24
5258
- name: Setup Java JDK
5359
uses: actions/setup-java@v1.4.3
5460
with:
@@ -58,4 +64,33 @@ jobs:
5864
run: npm install
5965

6066
- name: Start Tests
61-
run: npm run mocha_test -- -g ${{ matrix.mcVersion }}v
67+
run: |
68+
exit_code=0
69+
pids=""
70+
for v in ${{ matrix.versions }}; do
71+
npm run mocha_test -- --retries 3 -g "${v}v" > "test-${v}.log" 2>&1 &
72+
pids="$pids $!"
73+
done
74+
for pid in $pids; do
75+
wait $pid || exit_code=$?
76+
done
77+
# Print passing versions first, then failing versions last
78+
for v in ${{ matrix.versions }}; do
79+
if ! grep -q "failing" "test-${v}.log"; then
80+
echo ""
81+
echo "=========================================="
82+
echo " Results for Minecraft ${v}"
83+
echo "=========================================="
84+
cat "test-${v}.log"
85+
fi
86+
done
87+
for v in ${{ matrix.versions }}; do
88+
if grep -q "failing" "test-${v}.log"; then
89+
echo ""
90+
echo "=========================================="
91+
echo " FAILED: Minecraft ${v}"
92+
echo "=========================================="
93+
cat "test-${v}.log"
94+
fi
95+
done
96+
exit $exit_code

.github/workflows/npm-publish.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ on:
33
push:
44
branches:
55
- master # Change this to your default branch
6+
permissions:
7+
id-token: write
8+
contents: write
69
jobs:
710
npm-publish:
811
name: npm-publish
@@ -13,13 +16,12 @@ jobs:
1316
- name: Set up Node.js
1417
uses: actions/setup-node@master
1518
with:
16-
node-version: 22.0.0
19+
node-version: 24
20+
registry-url: 'https://registry.npmjs.org'
1721
- id: publish
18-
uses: JS-DevTools/npm-publish@v1
19-
with:
20-
token: ${{ secrets.NPM_AUTH_TOKEN }}
22+
uses: JS-DevTools/npm-publish@v4
2123
- name: Create Release
22-
if: steps.publish.outputs.type != 'none'
24+
if: ${{ steps.publish.outputs.type }}
2325
id: create_release
2426
uses: actions/create-release@v1
2527
env:

docs/history.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
11
## 4.35.0
2+
3+
## 4.37.0
4+
* [CI: print failed version logs last for easy debugging (#3876)](https://github.com/PrismarineJS/mineflayer/commit/56efbcde16343576533eaa7a08e85b785ae847cd) (thanks @rom1504)
5+
* [Fix nether test reliability and add set_player_inventory handler (#3874)](https://github.com/PrismarineJS/mineflayer/commit/fd03f0a83e00229344b0624ce5ace9e0303599f3) (thanks @rom1504)
6+
* [Improve test reliability: reconnect, retry, server console, packet fix (#3873)](https://github.com/PrismarineJS/mineflayer/commit/23f60d2132d49c08d936073d215466a825045933) (thanks @rom1504)
7+
* [Improve test reliability: server console gamemode, retries, NaN guard (#3871)](https://github.com/PrismarineJS/mineflayer/commit/7af5e2ccf2f7ed1240ea792cb425a20e669618c2) (thanks @rom1504)
8+
* [CI: run 4 MC versions per job to halve runner pressure (#3870)](https://github.com/PrismarineJS/mineflayer/commit/85a164d86e2b8bac996946ef6fb46d800861c82f) (thanks @rom1504)
9+
* [Speed up tests: remove sleep(1000) in resetState (#3869)](https://github.com/PrismarineJS/mineflayer/commit/81ecd72bafafc711ee2bc32b19d64f2bd897c4fb) (thanks @rom1504)
10+
11+
## 4.36.0
12+
* [Update CI to Node 24 (#3861)](https://github.com/PrismarineJS/mineflayer/commit/3b05f83d536adb31103c811f7093361ff35449a1) (thanks @rom1504)
13+
* [Fix publish condition for npm-publish v4 (#3858)](https://github.com/PrismarineJS/mineflayer/commit/ea663478fa1cb4f602735675aebd0c8459c7576d) (thanks @rom1504)
14+
* [Switch to trusted publishing via OIDC (#3856)](https://github.com/PrismarineJS/mineflayer/commit/3173eeded3982b144bc40d7b0fdfb083ee7eb118) (thanks @rom1504)
15+
* [Bump @types/node to 25.2.1 + fix flaky time & nether tests (#3828)](https://github.com/PrismarineJS/mineflayer/commit/89686596c8de1091bf45104ba3230a5e87707a20) (thanks @plainprince)
16+
217
* [🎈 1.21.11 (#3781)](https://github.com/PrismarineJS/mineflayer/commit/597745c7c061943620fcacba7254bccee05b7a3e) (thanks @rom1504bot)
318
* [Fix chat pattern and event listener in tutorial (#3783)](https://github.com/PrismarineJS/mineflayer/commit/48586138f560991de60bea639c71fa82954cf50f) (thanks @brentspine)
419
* [Update history.md](https://github.com/PrismarineJS/mineflayer/commit/c9f766513d5bb5a93c5b86c07827128716c0afdb) (thanks @extremeheat)

index.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ export interface BotEvents {
162162
bossBarDeleted: (bossBar: BossBar) => Promise<void> | void
163163
bossBarUpdated: (bossBar: BossBar) => Promise<void> | void
164164
resourcePack: (url: string, hash?: string, uuid?: string) => Promise<void> | void
165+
heldItemChanged: (newItem: Item | null) => Promise<void> | void
165166
particle: (particle: Particle) => Promise<void> | void
166167
}
167168

@@ -386,8 +387,8 @@ export interface Bot extends TypedEmitter<BotEvents> {
386387
times?: number
387388
) => Promise<void>
388389

389-
390-
390+
391+
391392
setCommandBlock: (pos: Vec3, command: string, options: CommandBlockOptions) => void
392393

393394
clickWindow: (

lib/plugins/digging.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,11 @@ function inject (bot) {
210210
}
211211

212212
bot.on('death', () => {
213-
bot.removeAllListeners('diggingAborted')
214-
bot.removeAllListeners('diggingCompleted')
215-
bot.stopDigging()
213+
try {
214+
bot.removeAllListeners('diggingAborted')
215+
bot.removeAllListeners('diggingCompleted')
216+
bot.stopDigging()
217+
} catch (_) {}
216218
})
217219

218220
function canDigBlock (block) {
@@ -246,13 +248,14 @@ function inject (bot) {
246248
return block.digTime(
247249
type,
248250
creative,
249-
bot.entity.isInWater,
251+
['water', 'flowing_water'].includes(bot._getBlockAtEyeLevel()?.name),
250252
!bot.entity.onGround,
251253
enchantments,
252254
bot.entity.effects
253255
)
254256
}
255257

258+
bot._getBlockAtEyeLevel = () => bot.entity.position && bot.blockAt(bot.entity.position.offset(0, bot.entity.eyeHeight, 0))
256259
bot.dig = dig
257260
bot.stopDigging = noop
258261
bot.canDigBlock = canDigBlock

lib/plugins/entities.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const { Vec3 } = require('vec3')
22
const conv = require('../conversions')
3+
const mojangson = require('mojangson')
34
// These values are only accurate for versions 1.14 and above (crouch hitbox changes)
45
// Todo: hitbox sizes for sleeping, swimming/crawling, and flying with elytra
56
const PLAYER_HEIGHT = 1.8
@@ -953,7 +954,12 @@ function extractSkinInformation (properties) {
953954
return undefined
954955
}
955956

956-
const skinTexture = JSON.parse(Buffer.from(props.textures.value, 'base64').toString('utf8'))
957+
let skinTexture
958+
try { // Handles mojangson-style player data
959+
skinTexture = JSON.parse(Buffer.from(props.textures.value, 'base64'))
960+
} catch (e) {
961+
skinTexture = mojangson.simplify(mojangson.parse(Buffer.from(props.textures.value, 'base64').toString('utf-8')))
962+
}
957963

958964
const skinTextureUrl = skinTexture?.textures?.SKIN?.url ?? undefined
959965
const skinTextureModel = skinTexture?.textures?.SKIN?.metadata?.model ?? undefined

lib/plugins/game.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,18 @@ function inject (bot, options) {
4747
} else if (bot.supportFeature('dimensionIsAString')) {
4848
bot.game.dimension = packet.dimension.replace('minecraft:', '')
4949
} else if (bot.supportFeature('dimensionIsAWorld')) {
50-
bot.game.dimension = packet.worldName.replace('minecraft:', '')
50+
if (bot.supportFeature('dimensionDataInCodec')) {
51+
// For 1.19+, we need the dimension TYPE name (not the world/level name) so
52+
// the codec lookup succeeds. In login packets the type is "worldType"; in
53+
// respawn packets it is "dimension". worldName is the level name which may
54+
// differ from the type on proxy/modded servers.
55+
const dimType = packet.worldType ?? packet.dimension
56+
bot.game.dimension = typeof dimType === 'string'
57+
? dimType.replace('minecraft:', '')
58+
: packet.worldName.replace('minecraft:', '')
59+
} else {
60+
bot.game.dimension = packet.worldName.replace('minecraft:', '')
61+
}
5162
} else {
5263
throw new Error('Unsupported dimension type in login packet')
5364
}
@@ -60,11 +71,6 @@ function inject (bot, options) {
6071
bot.game.height = 256
6172

6273
if (bot.supportFeature('dimensionDataInCodec')) { // 1.19+
63-
// pre 1.20.5 before we consolidated login and respawn's SpawnInfo structure into one type,
64-
// "dimension" was called "worldType" in login_packet's payload but not respawn.
65-
if (packet.worldType && !bot.game.dimension) {
66-
bot.game.dimension = packet.worldType.replace('minecraft:', '')
67-
}
6874
const dimData = bot.registry.dimensionsByName[bot.game.dimension]
6975
if (dimData) {
7076
bot.game.minY = dimData.minY

lib/plugins/inventory.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const assert = require('assert')
22
const { Vec3 } = require('vec3')
33
const { once, sleep, createDoneTask, createTask, withTimeout } = require('../promise_utils')
4+
const { toNotchianYaw, toNotchianPitch } = require('../conversions')
45

56
module.exports = inject
67

@@ -128,7 +129,10 @@ function inject (bot, { hideErrors }) {
128129
bot._client.write('use_item', {
129130
hand: offHand ? 1 : 0,
130131
sequence,
131-
rotation: { x: 0, y: 0 }
132+
rotation: {
133+
x: toNotchianYaw(bot.entity.yaw),
134+
y: toNotchianPitch(bot.entity.pitch)
135+
}
132136
})
133137
}
134138
}
@@ -547,7 +551,7 @@ function inject (bot, { hideErrors }) {
547551
}
548552
const window = bot.currentWindow || bot.inventory
549553

550-
assert.ok(mode >= 0 && mode <= 4)
554+
assert.ok(mode >= 0 && mode <= 6)
551555
const actionId = createActionNumber()
552556

553557
const click = {
@@ -728,6 +732,17 @@ function inject (bot, { hideErrors }) {
728732
const newItem = Item.fromNotch(packet.item)
729733
bot._setSlot(packet.slot, newItem, window)
730734
})
735+
// 1.21.9+ uses set_player_inventory for server-initiated inventory changes
736+
// (e.g. console /give) instead of set_slot
737+
bot._client.on('set_player_inventory', (packet) => {
738+
const newItem = Item.fromNotch(packet.contents)
739+
bot._setSlot(packet.slotId, newItem)
740+
})
741+
bot.inventory.on('updateSlot', (index) => {
742+
if (index === bot.quickBarSlot + bot.inventory.hotbarStart) {
743+
bot.emit('heldItemChanged', bot.heldItem)
744+
}
745+
})
731746
bot._client.on('window_items', (packet) => {
732747
const window = packet.windowId === 0 ? bot.inventory : bot.currentWindow
733748
if (!window || window.id !== packet.windowId) {

lib/plugins/physics.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
7676
}
7777

7878
function tickPhysics (now) {
79+
if (!bot.entity?.position || !Number.isFinite(bot.entity.position.x)) return // entity not ready
7980
if (bot.blockAt(bot.entity.position) == null) return // check if chunk is unloaded
8081
if (bot.physicsEnabled && shouldUsePhysics) {
8182
physics.simulatePlayer(new PlayerState(bot, controlState), world).apply(bot)
@@ -102,6 +103,7 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
102103
if (bot.inConfigurationPhase) return
103104

104105
// sends data, no logic
106+
if (!Number.isFinite(position.x) || !Number.isFinite(position.y) || !Number.isFinite(position.z)) return
105107
const oldPos = new Vec3(lastSent.x, lastSent.y, lastSent.z)
106108
lastSent.x = position.x
107109
lastSent.y = position.y
@@ -131,6 +133,7 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
131133
if (bot.inConfigurationPhase) return
132134

133135
// sends data, no logic
136+
if (!Number.isFinite(position.x) || !Number.isFinite(position.y) || !Number.isFinite(position.z)) return
134137
const oldPos = new Vec3(lastSent.x, lastSent.y, lastSent.z)
135138
lastSent.x = position.x
136139
lastSent.y = position.y
@@ -162,6 +165,8 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
162165
function updatePosition (now) {
163166
// Only send updates for 20 ticks after death
164167
if (isEntityRemoved()) return
168+
// Don't send position with invalid coordinates (NaN after death)
169+
if (!Number.isFinite(bot.entity.position.x)) return
165170

166171
// Don't send any position packets during configuration phase (Velocity support)
167172
if (bot.inConfigurationPhase) return
@@ -427,6 +432,27 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
427432
if (bot.supportFeature('teleportUsesOwnPacket')) {
428433
bot._client.write('teleport_confirm', { teleportId: packet.teleportId })
429434
}
435+
436+
// After death/respawn, delay the forced position_look response.
437+
// Sending it immediately causes "Invalid move player packet" kicks
438+
// on older servers, but the server needs it to complete the respawn.
439+
if (respawnTimer > 0 && Date.now() - respawnTimer < 2000) {
440+
respawnTimer = 0 // only delay once
441+
const delayedPos = pos.clone()
442+
const delayedYaw = newYaw
443+
const delayedPitch = newPitch
444+
const delayedOnGround = bot.entity.onGround
445+
setTimeout(() => {
446+
sendPacketPositionAndLook(delayedPos, delayedYaw, delayedPitch, delayedOnGround)
447+
shouldUsePhysics = true
448+
bot.jumpTicks = 0
449+
lastSentYaw = bot.entity.yaw
450+
lastSentPitch = bot.entity.pitch
451+
bot.emit('forcedMove')
452+
}, 1500)
453+
return
454+
}
455+
430456
sendPacketPositionAndLook(pos, newYaw, newPitch, bot.entity.onGround)
431457

432458
shouldUsePhysics = true
@@ -459,7 +485,12 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
459485
})
460486
}
461487

488+
let respawnTimer = 0
462489
bot.on('mount', () => { shouldUsePhysics = false })
490+
bot.on('death', () => {
491+
shouldUsePhysics = false
492+
respawnTimer = Date.now()
493+
})
463494
bot.on('respawn', () => { shouldUsePhysics = false })
464495
bot.on('login', () => {
465496
shouldUsePhysics = false

package.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mineflayer",
3-
"version": "4.35.0",
3+
"version": "4.37.0",
44
"description": "create minecraft bots with a stable, high level API",
55
"main": "index.js",
66
"types": "index.d.ts",
@@ -21,8 +21,9 @@
2121
},
2222
"license": "MIT",
2323
"dependencies": {
24-
"minecraft-data": "^3.98.0",
25-
"minecraft-protocol": "^1.64.0",
24+
"minecraft-data": "^3.108.0",
25+
"minecraft-protocol": "^1.66.0",
26+
"mojangson": "^2.0.4",
2627
"prismarine-biome": "^1.1.1",
2728
"prismarine-block": "^1.22.0",
2829
"prismarine-chat": "^1.7.1",
@@ -31,7 +32,7 @@
3132
"prismarine-item": "^1.17.0",
3233
"prismarine-nbt": "^2.0.0",
3334
"prismarine-physics": "^1.9.0",
34-
"prismarine-recipe": "^1.3.0",
35+
"prismarine-recipe": "^1.5.0",
3536
"prismarine-registry": "^1.10.0",
3637
"prismarine-windows": "^2.9.0",
3738
"prismarine-world": "^3.6.0",
@@ -41,7 +42,7 @@
4142
"vec3": "^0.1.7"
4243
},
4344
"devDependencies": {
44-
"@types/node": "^24.0.6",
45+
"@types/node": "^25.2.1",
4546
"doctoc": "^2.0.1",
4647
"minecraft-wrap": "^1.3.0",
4748
"mineflayer": "file:",

0 commit comments

Comments
 (0)