Skip to content

Commit

Permalink
Support 1.21.3. (#3489)
Browse files Browse the repository at this point in the history
* Support 1.21.3.

* Update version.js

* Update entities.js

* Update package.json

* Update ci.yml

* Fix gamemode test and implementation (#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]>
  • Loading branch information
3 people authored Jan 8, 2025
1 parent 3187368 commit 58ae9e5
Show file tree
Hide file tree
Showing 13 changed files with 319 additions and 67 deletions.
11 changes: 7 additions & 4 deletions lib/plugins/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,10 +395,13 @@ function inject (bot, { version, storageBuilder, hideErrors }) {
bot._client.on('explosion', (packet) => {
// explosion
const p = new Vec3(packet.x, packet.y, packet.z)
packet.affectedBlockOffsets.forEach((offset) => {
const pt = p.offset(offset.x, offset.y, offset.z)
updateBlockState(pt, 0)
})
if (packet.affectedBlockOffsets) {
// TODO: server no longer sends in 1.21.3. Is client supposed to compute this or is it sent via normal block updates?
packet.affectedBlockOffsets.forEach((offset) => {
const pt = p.offset(offset.x, offset.y, offset.z)
updateBlockState(pt, 0)
})
}
})

bot._client.on('spawn_entity_painting', (packet) => {
Expand Down
24 changes: 22 additions & 2 deletions lib/plugins/creative.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,40 @@ function inject (bot) {
const creativeSlotsUpdates = []

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

if (Item.equal(bot.inventory.slots[slot], item, true)) return
if (creativeSlotsUpdates[slot]) {
throw new Error(`Setting slot ${slot} cancelled due to calling bot.creative.setInventorySlot(${slot}, ...) again`)
}
creativeSlotsUpdates[slot] = true

bot._client.write('set_creative_slot', {
slot,
item: Item.toNotch(item)
})

if (bot.supportFeature('noAckOnCreateSetSlotPacket')) {
// No ack
bot._setSlot(slot, item)
if (waitTimeout === 0) return // no wait
// allow some time to see if server rejects
return new Promise((resolve, reject) => {
function updateSlot (oldItem, newItem) {
if (newItem.itemId !== item.itemId) {
creativeSlotsUpdates[slot] = false
reject(Error('Server rejected'))
}
}
bot.inventory.once(`updateSlot:${slot}`, updateSlot)
setTimeout(() => {
bot.inventory.off(`updateSlot:${slot}`, updateSlot)
creativeSlotsUpdates[slot] = false
resolve()
}, waitTimeout)
})
}

await onceWithCleanup(bot.inventory, `updateSlot:${slot}`, {
timeout: 5000,
checkCondition: (oldItem, newItem) => item === null ? newItem === null : newItem?.name === item.name && newItem?.count === item.count && newItem?.metadata === item.metadata
Expand Down
113 changes: 103 additions & 10 deletions lib/plugins/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,16 @@ function inject (bot) {
bot.emit('entityMoved', entity)
})

// 1.21.3 - merges the packets above
bot._client.on('sync_entity_position', (packet) => {
const entity = fetchEntity(packet.entityId)
entity.position.set(packet.x, packet.y, packet.z)
entity.velocity.update(packet.dx, packet.dy, packet.dz)
entity.yaw = packet.yaw
entity.pitch = packet.pitch
bot.emit('entityMoved', entity)
})

bot._client.on('entity_head_rotation', (packet) => {
// entity head look
const entity = fetchEntity(packet.entityId)
Expand All @@ -361,6 +371,11 @@ function inject (bot) {
if (eventName) bot.emit(eventName, entity)
})

bot._client.on('damage_event', (packet) => { // 1.20+
const entity = bot.entities[packet.entityId]
bot.emit('entityHurt', entity)
})

bot._client.on('attach_entity', (packet) => {
// attach entity
const entity = fetchEntity(packet.entityId)
Expand Down Expand Up @@ -585,6 +600,62 @@ function inject (bot) {
bot._client.on('player_info', (packet) => {
// player list item(s)

if (typeof packet.action !== 'number') {
// the features checks below this will be un-needed with https://github.com/PrismarineJS/minecraft-data/pull/948
for (const update of packet.data) {
let player = bot.uuidToUsername[update.uuid] ? bot.players[bot.uuidToUsername[update.uuid]] : null
let newPlayer = false

const obj = {
uuid: update.uuid
}

if (!player) newPlayer = true

player ||= obj

if (packet.action.add_player) {
obj.username = update.player.name
obj.displayName = player.displayName || new ChatMessage({ text: '', extra: [{ text: update.player.name }] })
obj.skinData = extractSkinInformation(update.player.properties)
}

if (packet.action.update_game_mode) {
obj.gamemode = update.gamemode
}

if (packet.action.update_latency) {
obj.ping = update.latency
}

if (update.displayName) {
obj.displayName = ChatMessage.fromNotch(update.displayName)
}

if (newPlayer) {
if (!obj.username) continue // Should be unreachable
player = bot.players[obj.username] = obj
bot.uuidToUsername[obj.uuid] = obj.username
} else {
Object.assign(player, obj)
}

const playerEntity = Object.values(bot.entities).find(e => e.type === 'player' && e.username === player.username)
player.entity = playerEntity

if (playerEntity === bot.entity) {
bot.player = player
}

if (newPlayer) {
bot.emit('playerJoined', player)
} else {
bot.emit('playerUpdated', player)
}
}
return
}

if (bot.supportFeature('playerInfoActionIsBitfield')) {
for (const item of packet.data) {
let player = bot.uuidToUsername[item.uuid] ? bot.players[bot.uuidToUsername[item.uuid]] : null
Expand Down Expand Up @@ -795,20 +866,42 @@ function inject (bot) {
}

function moveVehicle (left, forward) {
bot._client.write('steer_vehicle', {
sideways: left,
forward,
jump: 0x01
})
if (bot.supportFeature('newPlayerInputPacket')) {
// docs:
// * left can take -1 or 1 : -1 means right, 1 means left
// * forward can take -1 or 1 : -1 means backward, 1 means forward
bot._client.write('player_input', {
inputs: {
forward: forward > 0,
backward: forward < 0,
left: left > 0,
right: left < 0
}
})
} else {
bot._client.write('steer_vehicle', {
sideways: left,
forward,
jump: 0x01
})
}
}

function dismount () {
if (bot.vehicle) {
bot._client.write('steer_vehicle', {
sideways: 0.0,
forward: 0.0,
jump: 0x02
})
if (bot.supportFeature('newPlayerInputPacket')) {
bot._client.write('player_input', {
inputs: {
jump: true
}
})
} else {
bot._client.write('steer_vehicle', {
sideways: 0.0,
forward: 0.0,
jump: 0x02
})
}
} else {
bot.emit('error', new Error('dismount: not mounted'))
}
Expand Down
14 changes: 12 additions & 2 deletions lib/plugins/game.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ const dimensionNames = {
1: 'the_end'
}

const parseGameMode = gameModeBits => gameModes[(gameModeBits & 0b11)] // lower two bits
const parseGameMode = gameModeBits => {
if (gameModeBits < 0 || gameModeBits > 0b11) {
return 'survival'
}
return gameModes[(gameModeBits & 0b11)] // lower two bits
}

function inject (bot, options) {
function getBrandCustomChannelName () {
Expand All @@ -25,7 +30,12 @@ function inject (bot, options) {
function handleRespawnPacketData (packet) {
bot.game.levelType = packet.levelType ?? (packet.isFlat ? 'flat' : 'default')
bot.game.hardcore = packet.isHardcore ?? Boolean(packet.gameMode & 0b100)
bot.game.gameMode = packet.gamemode || parseGameMode(packet.gameMode)
// Either a respawn packet or a login packet. Depending on the packet it can be "gamemode" or "gameMode"
if (bot.supportFeature('spawnRespawnWorldDataField')) { // 1.20.5
bot.game.gameMode = packet.gamemode
} else {
bot.game.gameMode = parseGameMode(packet.gamemode ?? packet.gameMode)
}
if (bot.supportFeature('segmentedRegistryCodecData')) { // 1.20.5
if (typeof packet.dimension === 'number') {
bot.game.dimension = bot.registry.dimensionsArray[packet.dimension]?.name?.replace('minecraft:', '')
Expand Down
4 changes: 3 additions & 1 deletion lib/plugins/generic_place.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ function inject (bot) {
cursorX: dx,
cursorY: dy,
cursorZ: dz,
insideBlock: false
insideBlock: false,
sequence: 0, // 1.19.0
worldBorderHit: false // 1.21.3
})
}

Expand Down
18 changes: 12 additions & 6 deletions lib/plugins/inventory.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ function inject (bot, { hideErrors }) {
// TODO: tell the server that we are not sneaking while doing this
await bot.lookAt(block.position.offset(0.5, 0.5, 0.5), false)
// place block message
// TODO: logic below can likely be simplified
if (bot.supportFeature('blockPlaceHasHeldItem')) {
bot._client.write('block_place', {
location: block.position,
Expand Down Expand Up @@ -225,7 +226,9 @@ function inject (bot, { hideErrors }) {
cursorX: cursorPos.x,
cursorY: cursorPos.y,
cursorZ: cursorPos.z,
insideBlock: false
insideBlock: false,
sequence: 0, // 1.19.0+
worldBorderHit: false // 1.21.3+
})
}

Expand Down Expand Up @@ -712,15 +715,18 @@ function inject (bot, { hideErrors }) {
bot.currentWindow = null
bot.emit('windowClose', oldWindow)
})
bot._client.on('set_slot', (packet) => {
bot._setSlot = (slotId, newItem, window = bot.inventory) => {
// set slot
const oldItem = window.slots[slotId]
window.updateSlot(slotId, newItem)
updateHeldItem()
bot.emit(`setSlot:${window.id}`, oldItem, newItem)
}
bot._client.on('set_slot', (packet) => {
const window = packet.windowId === 0 ? bot.inventory : bot.currentWindow
if (!window || window.id !== packet.windowId) return
const newItem = Item.fromNotch(packet.item)
const oldItem = window.slots[packet.slot]
window.updateSlot(packet.slot, newItem)
updateHeldItem()
bot.emit(`setSlot:${window.id}`, oldItem, newItem)
bot._setSlot(packet.slot, newItem, window)
})
bot._client.on('window_items', (packet) => {
const window = packet.windowId === 0 ? bot.inventory : bot.currentWindow
Expand Down
Loading

0 comments on commit 58ae9e5

Please sign in to comment.