Skip to content

Commit 58ae9e5

Browse files
rom1504extremeheatIceTank
authored
Support 1.21.3. (PrismarineJS#3489)
* Support 1.21.3. * Update version.js * Update entities.js * Update package.json * Update ci.yml * Fix gamemode test and implementation (PrismarineJS#3508) * Fix gamemode test and implementation * Add gamemode test related comments * Fix gamemode tests Add test function to kill the bot * Add gamemode out of bounds checks * Simplify gameMode parsing and check against spawnRespawnWorldDataField feature * 21.3 work * fix * reduce debug logging * outbound logging * fix logging * disable logging and --bail * Update internalTest.js typo * Fix 1.21.3 set creative slot * revert some setInventorySlot checks * fix internalTest and check * use features * Update physics.js fix lastSent * Remove debug logging --------- Co-authored-by: extremeheat <[email protected]> Co-authored-by: IceTank <[email protected]>
1 parent 3187368 commit 58ae9e5

13 files changed

+319
-67
lines changed

lib/plugins/blocks.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -395,10 +395,13 @@ function inject (bot, { version, storageBuilder, hideErrors }) {
395395
bot._client.on('explosion', (packet) => {
396396
// explosion
397397
const p = new Vec3(packet.x, packet.y, packet.z)
398-
packet.affectedBlockOffsets.forEach((offset) => {
399-
const pt = p.offset(offset.x, offset.y, offset.z)
400-
updateBlockState(pt, 0)
401-
})
398+
if (packet.affectedBlockOffsets) {
399+
// TODO: server no longer sends in 1.21.3. Is client supposed to compute this or is it sent via normal block updates?
400+
packet.affectedBlockOffsets.forEach((offset) => {
401+
const pt = p.offset(offset.x, offset.y, offset.z)
402+
updateBlockState(pt, 0)
403+
})
404+
}
402405
})
403406

404407
bot._client.on('spawn_entity_painting', (packet) => {

lib/plugins/creative.js

+22-2
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,40 @@ function inject (bot) {
2121
const creativeSlotsUpdates = []
2222

2323
// WARN: This method should not be called twice on the same slot before first promise succeeds
24-
async function setInventorySlot (slot, item) {
24+
async function setInventorySlot (slot, item, waitTimeout = 400) {
2525
assert(slot >= 0 && slot <= 44)
2626

2727
if (Item.equal(bot.inventory.slots[slot], item, true)) return
2828
if (creativeSlotsUpdates[slot]) {
2929
throw new Error(`Setting slot ${slot} cancelled due to calling bot.creative.setInventorySlot(${slot}, ...) again`)
3030
}
3131
creativeSlotsUpdates[slot] = true
32-
3332
bot._client.write('set_creative_slot', {
3433
slot,
3534
item: Item.toNotch(item)
3635
})
3736

37+
if (bot.supportFeature('noAckOnCreateSetSlotPacket')) {
38+
// No ack
39+
bot._setSlot(slot, item)
40+
if (waitTimeout === 0) return // no wait
41+
// allow some time to see if server rejects
42+
return new Promise((resolve, reject) => {
43+
function updateSlot (oldItem, newItem) {
44+
if (newItem.itemId !== item.itemId) {
45+
creativeSlotsUpdates[slot] = false
46+
reject(Error('Server rejected'))
47+
}
48+
}
49+
bot.inventory.once(`updateSlot:${slot}`, updateSlot)
50+
setTimeout(() => {
51+
bot.inventory.off(`updateSlot:${slot}`, updateSlot)
52+
creativeSlotsUpdates[slot] = false
53+
resolve()
54+
}, waitTimeout)
55+
})
56+
}
57+
3858
await onceWithCleanup(bot.inventory, `updateSlot:${slot}`, {
3959
timeout: 5000,
4060
checkCondition: (oldItem, newItem) => item === null ? newItem === null : newItem?.name === item.name && newItem?.count === item.count && newItem?.metadata === item.metadata

lib/plugins/entities.js

+103-10
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,16 @@ function inject (bot) {
341341
bot.emit('entityMoved', entity)
342342
})
343343

344+
// 1.21.3 - merges the packets above
345+
bot._client.on('sync_entity_position', (packet) => {
346+
const entity = fetchEntity(packet.entityId)
347+
entity.position.set(packet.x, packet.y, packet.z)
348+
entity.velocity.update(packet.dx, packet.dy, packet.dz)
349+
entity.yaw = packet.yaw
350+
entity.pitch = packet.pitch
351+
bot.emit('entityMoved', entity)
352+
})
353+
344354
bot._client.on('entity_head_rotation', (packet) => {
345355
// entity head look
346356
const entity = fetchEntity(packet.entityId)
@@ -361,6 +371,11 @@ function inject (bot) {
361371
if (eventName) bot.emit(eventName, entity)
362372
})
363373

374+
bot._client.on('damage_event', (packet) => { // 1.20+
375+
const entity = bot.entities[packet.entityId]
376+
bot.emit('entityHurt', entity)
377+
})
378+
364379
bot._client.on('attach_entity', (packet) => {
365380
// attach entity
366381
const entity = fetchEntity(packet.entityId)
@@ -585,6 +600,62 @@ function inject (bot) {
585600
bot._client.on('player_info', (packet) => {
586601
// player list item(s)
587602

603+
if (typeof packet.action !== 'number') {
604+
// the features checks below this will be un-needed with https://github.com/PrismarineJS/minecraft-data/pull/948
605+
for (const update of packet.data) {
606+
let player = bot.uuidToUsername[update.uuid] ? bot.players[bot.uuidToUsername[update.uuid]] : null
607+
let newPlayer = false
608+
609+
const obj = {
610+
uuid: update.uuid
611+
}
612+
613+
if (!player) newPlayer = true
614+
615+
player ||= obj
616+
617+
if (packet.action.add_player) {
618+
obj.username = update.player.name
619+
obj.displayName = player.displayName || new ChatMessage({ text: '', extra: [{ text: update.player.name }] })
620+
obj.skinData = extractSkinInformation(update.player.properties)
621+
}
622+
623+
if (packet.action.update_game_mode) {
624+
obj.gamemode = update.gamemode
625+
}
626+
627+
if (packet.action.update_latency) {
628+
obj.ping = update.latency
629+
}
630+
631+
if (update.displayName) {
632+
obj.displayName = ChatMessage.fromNotch(update.displayName)
633+
}
634+
635+
if (newPlayer) {
636+
if (!obj.username) continue // Should be unreachable
637+
player = bot.players[obj.username] = obj
638+
bot.uuidToUsername[obj.uuid] = obj.username
639+
} else {
640+
Object.assign(player, obj)
641+
}
642+
643+
const playerEntity = Object.values(bot.entities).find(e => e.type === 'player' && e.username === player.username)
644+
player.entity = playerEntity
645+
646+
if (playerEntity === bot.entity) {
647+
bot.player = player
648+
}
649+
650+
if (newPlayer) {
651+
bot.emit('playerJoined', player)
652+
} else {
653+
bot.emit('playerUpdated', player)
654+
}
655+
}
656+
return
657+
}
658+
588659
if (bot.supportFeature('playerInfoActionIsBitfield')) {
589660
for (const item of packet.data) {
590661
let player = bot.uuidToUsername[item.uuid] ? bot.players[bot.uuidToUsername[item.uuid]] : null
@@ -795,20 +866,42 @@ function inject (bot) {
795866
}
796867

797868
function moveVehicle (left, forward) {
798-
bot._client.write('steer_vehicle', {
799-
sideways: left,
800-
forward,
801-
jump: 0x01
802-
})
869+
if (bot.supportFeature('newPlayerInputPacket')) {
870+
// docs:
871+
// * left can take -1 or 1 : -1 means right, 1 means left
872+
// * forward can take -1 or 1 : -1 means backward, 1 means forward
873+
bot._client.write('player_input', {
874+
inputs: {
875+
forward: forward > 0,
876+
backward: forward < 0,
877+
left: left > 0,
878+
right: left < 0
879+
}
880+
})
881+
} else {
882+
bot._client.write('steer_vehicle', {
883+
sideways: left,
884+
forward,
885+
jump: 0x01
886+
})
887+
}
803888
}
804889

805890
function dismount () {
806891
if (bot.vehicle) {
807-
bot._client.write('steer_vehicle', {
808-
sideways: 0.0,
809-
forward: 0.0,
810-
jump: 0x02
811-
})
892+
if (bot.supportFeature('newPlayerInputPacket')) {
893+
bot._client.write('player_input', {
894+
inputs: {
895+
jump: true
896+
}
897+
})
898+
} else {
899+
bot._client.write('steer_vehicle', {
900+
sideways: 0.0,
901+
forward: 0.0,
902+
jump: 0x02
903+
})
904+
}
812905
} else {
813906
bot.emit('error', new Error('dismount: not mounted'))
814907
}

lib/plugins/game.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ const dimensionNames = {
1010
1: 'the_end'
1111
}
1212

13-
const parseGameMode = gameModeBits => gameModes[(gameModeBits & 0b11)] // lower two bits
13+
const parseGameMode = gameModeBits => {
14+
if (gameModeBits < 0 || gameModeBits > 0b11) {
15+
return 'survival'
16+
}
17+
return gameModes[(gameModeBits & 0b11)] // lower two bits
18+
}
1419

1520
function inject (bot, options) {
1621
function getBrandCustomChannelName () {
@@ -25,7 +30,12 @@ function inject (bot, options) {
2530
function handleRespawnPacketData (packet) {
2631
bot.game.levelType = packet.levelType ?? (packet.isFlat ? 'flat' : 'default')
2732
bot.game.hardcore = packet.isHardcore ?? Boolean(packet.gameMode & 0b100)
28-
bot.game.gameMode = packet.gamemode || parseGameMode(packet.gameMode)
33+
// Either a respawn packet or a login packet. Depending on the packet it can be "gamemode" or "gameMode"
34+
if (bot.supportFeature('spawnRespawnWorldDataField')) { // 1.20.5
35+
bot.game.gameMode = packet.gamemode
36+
} else {
37+
bot.game.gameMode = parseGameMode(packet.gamemode ?? packet.gameMode)
38+
}
2939
if (bot.supportFeature('segmentedRegistryCodecData')) { // 1.20.5
3040
if (typeof packet.dimension === 'number') {
3141
bot.game.dimension = bot.registry.dimensionsArray[packet.dimension]?.name?.replace('minecraft:', '')

lib/plugins/generic_place.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ function inject (bot) {
7979
cursorX: dx,
8080
cursorY: dy,
8181
cursorZ: dz,
82-
insideBlock: false
82+
insideBlock: false,
83+
sequence: 0, // 1.19.0
84+
worldBorderHit: false // 1.21.3
8385
})
8486
}
8587

lib/plugins/inventory.js

+12-6
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ function inject (bot, { hideErrors }) {
190190
// TODO: tell the server that we are not sneaking while doing this
191191
await bot.lookAt(block.position.offset(0.5, 0.5, 0.5), false)
192192
// place block message
193+
// TODO: logic below can likely be simplified
193194
if (bot.supportFeature('blockPlaceHasHeldItem')) {
194195
bot._client.write('block_place', {
195196
location: block.position,
@@ -225,7 +226,9 @@ function inject (bot, { hideErrors }) {
225226
cursorX: cursorPos.x,
226227
cursorY: cursorPos.y,
227228
cursorZ: cursorPos.z,
228-
insideBlock: false
229+
insideBlock: false,
230+
sequence: 0, // 1.19.0+
231+
worldBorderHit: false // 1.21.3+
229232
})
230233
}
231234

@@ -712,15 +715,18 @@ function inject (bot, { hideErrors }) {
712715
bot.currentWindow = null
713716
bot.emit('windowClose', oldWindow)
714717
})
715-
bot._client.on('set_slot', (packet) => {
718+
bot._setSlot = (slotId, newItem, window = bot.inventory) => {
716719
// set slot
720+
const oldItem = window.slots[slotId]
721+
window.updateSlot(slotId, newItem)
722+
updateHeldItem()
723+
bot.emit(`setSlot:${window.id}`, oldItem, newItem)
724+
}
725+
bot._client.on('set_slot', (packet) => {
717726
const window = packet.windowId === 0 ? bot.inventory : bot.currentWindow
718727
if (!window || window.id !== packet.windowId) return
719728
const newItem = Item.fromNotch(packet.item)
720-
const oldItem = window.slots[packet.slot]
721-
window.updateSlot(packet.slot, newItem)
722-
updateHeldItem()
723-
bot.emit(`setSlot:${window.id}`, oldItem, newItem)
729+
bot._setSlot(packet.slot, newItem, window)
724730
})
725731
bot._client.on('window_items', (packet) => {
726732
const window = packet.windowId === 0 ? bot.inventory : bot.currentWindow

0 commit comments

Comments
 (0)