diff --git a/src/minecraft/connection/javascript.ts b/src/minecraft/connection/javascript.ts index 2818767..32c89ee 100644 --- a/src/minecraft/connection/javascript.ts +++ b/src/minecraft/connection/javascript.ts @@ -19,6 +19,7 @@ import { import { ServerConnection, ConnectionOptions } from '.' import { getLoginPacket, handleEncryptionRequest } from './shared' import { readVarInt, writeVarInt, resolveHostname, protocolMap } from '../utils' +import packetIds from '../packets/ids' export declare interface JavaScriptServerConnection { on: ((event: 'packet', listener: (packet: Packet) => void) => this) & @@ -137,10 +138,7 @@ const initiateJavaScriptConnection = async (opts: ConnectionOptions) => { ? Buffer.alloc(0) // Avoid errors shortening. : conn.bufferedData.slice(packet.packetLength) // Internally handle login packets. - const is1164 = conn.options.protocolVersion >= protocolMap['1.16.4'] - const is117 = conn.options.protocolVersion >= protocolMap[1.17] - const is119 = conn.options.protocolVersion >= protocolMap[1.19] - const is1191 = conn.options.protocolVersion >= protocolMap['1.19.1'] + const { protocolVersion: version } = conn.options if (packet.id === 0x03 && !conn.loggedIn /* Set Compression */) { const [threshold] = readVarInt(packet.data) conn.compressionThreshold = threshold @@ -148,23 +146,17 @@ const initiateJavaScriptConnection = async (opts: ConnectionOptions) => { } else if (packet.id === 0x02 && !conn.loggedIn) { conn.loggedIn = true // Login Success } else if ( - // Keep Alive (clientbound) - (packet.id === 0x1f && is1164 && !is117) || - (packet.id === 0x21 && is117 && !is119) || - (packet.id === 0x1e && is119 && !is1191) || - (packet.id === 0x20 && is1191) + packet.id === packetIds.CLIENTBOUND_KEEP_ALIVE(version) ) { - const id = is1191 ? 0x12 : is119 ? 0x11 : is117 ? 0x0f : 0x10 + const id = packetIds.SERVERBOUND_KEEP_ALIVE(version) conn - .writePacket(id, packet.data) + .writePacket(id ?? 0, packet.data) .catch(err => conn.emit('error', err)) } else if ( // Disconnect (login) or Disconnect (play) (packet.id === 0x00 && !conn.loggedIn) || - (packet.id === 0x19 && conn.loggedIn && is1164 && !is117) || - (packet.id === 0x1a && conn.loggedIn && is117 && !is119) || - (packet.id === 0x17 && conn.loggedIn && is119 && !is1191) || - (packet.id === 0x19 && conn.loggedIn && is1191) + (packet.id === packetIds.CLIENTBOUND_DISCONNECT_PLAY(version) && + conn.loggedIn) ) { const [chatLength, chatVarIntLength] = readVarInt(packet.data) conn.disconnectReason = packet.data @@ -188,7 +180,7 @@ const initiateJavaScriptConnection = async (opts: ConnectionOptions) => { accessToken, selectedProfile, conn, - is119, + version >= protocolMap['1.19'], async (secret: Buffer, response: Buffer) => { const AES_ALG = 'aes-128-cfb8' conn.aesDecipher = createDecipheriv(AES_ALG, secret, secret) diff --git a/src/minecraft/connection/native.ts b/src/minecraft/connection/native.ts index 5b522ff..be9e5d8 100644 --- a/src/minecraft/connection/native.ts +++ b/src/minecraft/connection/native.ts @@ -8,6 +8,7 @@ import { ServerConnection, ConnectionOptions } from '.' import { concatPacketData, Packet } from '../packet' import { getLoginPacket, handleEncryptionRequest } from './shared' import { readVarInt, writeVarInt, resolveHostname, protocolMap } from '../utils' +import packetIds from '../packets/ids' const { ConnectionModule } = NativeModules @@ -85,10 +86,7 @@ export class NativeServerConnection ) // Internally handle login packets. We aren't handling these in native to share code. - const is1164 = options.protocolVersion >= protocolMap['1.16.4'] - const is117 = options.protocolVersion >= protocolMap[1.17] - const is119 = options.protocolVersion >= protocolMap[1.19] - const is1191 = options.protocolVersion >= protocolMap['1.19.1'] + const { protocolVersion: version } = options // Set Compression and Keep Alive are handled in native for now. // When modifying this code, apply the same changes to the JavaScript back-end. if (packet.id === 0x02 && !this.loggedIn /* Login Success */) { @@ -96,10 +94,8 @@ export class NativeServerConnection } else if ( // Disconnect (login) or Disconnect (play) (packet.id === 0x00 && !this.loggedIn) || - (packet.id === 0x19 && this.loggedIn && is1164 && !is117) || - (packet.id === 0x1a && this.loggedIn && is117 && !is119) || - (packet.id === 0x17 && this.loggedIn && is119 && !is1191) || - (packet.id === 0x19 && this.loggedIn && is1191) + (packet.id === packetIds.CLIENTBOUND_DISCONNECT_PLAY(version) && + this.loggedIn) ) { const [chatLength, chatVarIntLength] = readVarInt(packet.data) this.disconnectReason = packet.data @@ -124,7 +120,7 @@ export class NativeServerConnection accessToken, selectedProfile, this, - is119, + version >= protocolMap['1.19'], async (secret: Buffer, response: Buffer) => { const eSecret = secret.toString('base64') const eResp = response.toString('base64') @@ -176,7 +172,12 @@ const initiateNativeConnection = async (opts: ConnectionOptions) => { loginPacket: getLoginPacket(opts).toString('base64'), ...opts, host, - port + port, + packetFilter: Object.keys(packetIds) + .filter(name => name.startsWith('CLIENTBOUND')) + .map(name => + packetIds[name as keyof typeof packetIds](opts.protocolVersion) + ) }) return new NativeServerConnection(id, opts) } diff --git a/src/minecraft/packets/chat.ts b/src/minecraft/packets/chat.ts index cbe64fe..98aeb37 100644 --- a/src/minecraft/packets/chat.ts +++ b/src/minecraft/packets/chat.ts @@ -1,5 +1,6 @@ import { concatPacketData, PacketDataTypes } from '../packet' import { protocolMap, writeVarInt } from '../utils' +import packetIds from './ids' export const makeChatMessagePacket = ( msg: string, @@ -9,15 +10,12 @@ export const makeChatMessagePacket = ( const is119 = protocolVersion >= protocolMap[1.19] const is1191 = protocolVersion >= protocolMap['1.19.1'] if (!is119) { - return [0x03, concatPacketData([msg])] + const id = packetIds.SERVERBOUND_CHAT_MESSAGE(protocolVersion) + return [id ?? 0, concatPacketData([msg])] } else { const id = msg.startsWith('/') - ? is1191 - ? 0x04 - : 0x03 - : is1191 - ? 0x05 - : 0x04 + ? packetIds.SERVERBOUND_CHAT_COMMAND(protocolVersion) + : packetIds.SERVERBOUND_CHAT_MESSAGE(protocolVersion) const timestamp = Buffer.alloc(8) timestamp.writeIntBE(Date.now(), 2, 6) // writeBigInt64BE(BigInt(Date.now())) const salt = msgSalt ?? Buffer.alloc(8) @@ -30,6 +28,6 @@ export const makeChatMessagePacket = ( false ] if (is1191) data.push(writeVarInt(0), writeVarInt(0)) - return [id, concatPacketData(data)] + return [id ?? 0, concatPacketData(data)] } } diff --git a/src/minecraft/packets/ids.ts b/src/minecraft/packets/ids.ts new file mode 100644 index 0000000..2cceead --- /dev/null +++ b/src/minecraft/packets/ids.ts @@ -0,0 +1,119 @@ +import { protocolMap } from '../utils' + +const generateIdFunction = + (ids: Array<[number, number | null]>) => (protocolVersion: number) => { + for (const [version, id] of ids) { + if (version <= protocolVersion) return id + } + return null + } + +const packetIds = { + // Clientbound (login) + CLIENTBOUND_SET_COMPRESSION: generateIdFunction([ + [protocolMap['1.16.4'], 0x03] + ]), + CLIENTBOUND_LOGIN_SUCCESS: generateIdFunction([ + [protocolMap['1.16.4'], 0x02] + ]), + CLIENTBOUND_LOGIN_PLUGIN_REQUEST: generateIdFunction([ + [protocolMap['1.16.4'], 0x04] + ]), + CLIENTBOUND_ENCRYPTION_REQUEST: generateIdFunction([ + [protocolMap['1.16.4'], 0x01] + ]), + CLIENTBOUND_DISCONNECT_LOGIN: generateIdFunction([ + [protocolMap['1.16.4'], 0x00] + ]), + + // Clientbound (play) + CLIENTBOUND_KEEP_ALIVE: generateIdFunction([ + [protocolMap['1.19.1'], 0x20], + [protocolMap['1.19'], 0x1e], + [protocolMap['1.17'], 0x21], + [protocolMap['1.16.4'], 0x1f] + ]), + CLIENTBOUND_DISCONNECT_PLAY: generateIdFunction([ + [protocolMap['1.19.1'], 0x19], + [protocolMap['1.19'], 0x17], + [protocolMap['1.17'], 0x1a], + [protocolMap['1.16.4'], 0x19] + ]), + CLIENTBOUND_LOGIN_PLAY: generateIdFunction([ + [protocolMap['1.19.1'], 0x25], + [protocolMap['1.19'], 0x23], + [protocolMap['1.17'], 0x26], + [protocolMap['1.16.4'], 0x24] + ]), + CLIENTBOUND_RESPAWN: generateIdFunction([ + [protocolMap['1.19.1'], 0x3e], + [protocolMap['1.19'], 0x3b], + [protocolMap['1.17'], 0x3d], + [protocolMap['1.16.4'], 0x39] + ]), + CLIENTBOUND_UPDATE_HEALTH: generateIdFunction([ + [protocolMap['1.19.1'], 0x55], + [protocolMap['1.17'], 0x52], + [protocolMap['1.16.4'], 0x49] + ]), + CLIENTBOUND_DEATH_COMBAT_EVENT: generateIdFunction([ + [protocolMap['1.19.1'], 0x36], + [protocolMap['1.19'], 0x33], + [protocolMap['1.17'], 0x35], + [protocolMap['1.16.4'], 0x31] + ]), + CLIENTBOUND_OPEN_WINDOW: generateIdFunction([ + [protocolMap['1.19.1'], 0x2d], + [protocolMap['1.19'], 0x2b], + [protocolMap['1.16.4'], 0x2e] + ]), + CLIENTBOUND_CHAT_MESSAGE: generateIdFunction([ + [protocolMap['1.19'], null], + [protocolMap['1.17'], 0x0f], + [protocolMap['1.16.4'], 0x0e] + ]), + CLIENTBOUND_PLAYER_CHAT_MESSAGE: generateIdFunction([ + [protocolMap['1.19.1'], 0x33], + [protocolMap['1.19'], 0x30] + ]), + CLIENTBOUND_SYSTEM_CHAT_MESSAGE: generateIdFunction([ + [protocolMap['1.19.1'], 0x62], + [protocolMap['1.19'], 0x5f] + ]), + + // Serverbound (play) + SERVERBOUND_KEEP_ALIVE: generateIdFunction([ + [protocolMap['1.19.1'], 0x12], + [protocolMap['1.19'], 0x11], + [protocolMap['1.17'], 0x0f], + [protocolMap['1.16.4'], 0x10] + ]), + SERVERBOUND_CLOSE_WINDOW: generateIdFunction([ + [protocolMap['1.19.1'], 0x0c], + [protocolMap['1.19'], 0x0b], + [protocolMap['1.17'], 0x09], + [protocolMap['1.16.4'], 0x0a] + ]), + SERVERBOUND_CLIENT_SETTINGS: generateIdFunction([ + [protocolMap['1.19.1'], 0x08], + [protocolMap['1.19'], 0x07], + [protocolMap['1.16.4'], 0x05] + ]), + SERVERBOUND_CLIENT_STATUS: generateIdFunction([ + [protocolMap['1.19.1'], 0x07], + [protocolMap['1.19'], 0x06], + [protocolMap['1.16.4'], 0x04] + ]), + SERVERBOUND_CHAT_MESSAGE: generateIdFunction([ + [protocolMap['1.19.1'], 0x05], + [protocolMap['1.19'], 0x04], + [protocolMap['1.16.4'], 0x03] + ]), + SERVERBOUND_CHAT_COMMAND: generateIdFunction([ + [protocolMap['1.19.1'], 0x04], + [protocolMap['1.19'], 0x03], + [protocolMap['1.16.4'], null] + ]) +} + +export default packetIds diff --git a/src/screens/chat/packetHandler.ts b/src/screens/chat/packetHandler.ts index 7b94466..f4fec3e 100644 --- a/src/screens/chat/packetHandler.ts +++ b/src/screens/chat/packetHandler.ts @@ -9,6 +9,7 @@ import { } from '../../minecraft/packet' import { protocolMap, readVarInt, writeVarInt } from '../../minecraft/utils' import { makeChatMessagePacket } from '../../minecraft/packets/chat' +import packetIds from '../../minecraft/packets/ids' export const enderChatPrefix = '\u00A74[\u00A7cEnderChat\u00A74] \u00A7c' export const parseMessageError = 'An error occurred when parsing chat.' @@ -96,36 +97,30 @@ export const packetHandler = charLimit: number ) => (packet: Packet) => { - const { protocolVersion } = connection.options + const { protocolVersion: version } = connection.options if (statusRef.current === 'CONNECTING' && connection.loggedIn) { setLoading('') statusRef.current = 'CONNECTED' const messageToSend = joinMessage.substring(0, charLimit).trim() if (sendJoinMessage && messageToSend) { connection - .writePacket(...makeChatMessagePacket(messageToSend, protocolVersion)) + .writePacket(...makeChatMessagePacket(messageToSend, version)) .catch(handleError(addMessage, sendMessageError)) } if (sendSpawnCommand) { connection - .writePacket(...makeChatMessagePacket('/spawn', protocolVersion)) + .writePacket(...makeChatMessagePacket('/spawn', version)) .catch(handleError(addMessage, sendMessageError)) } } - const is117 = protocolVersion >= protocolMap[1.17] - const is118 = protocolVersion >= protocolMap[1.18] - const is119 = protocolVersion >= protocolMap[1.19] - const is1191 = protocolVersion >= protocolMap['1.19.1'] - if ( - /* Login (play) */ - (packet.id === 0x24 && !is117) || - (packet.id === 0x26 && is117 && !is119) || - (packet.id === 0x23 && is119 && !is1191) || - (packet.id === 0x25 && is1191) - ) { + const is117 = version >= protocolMap[1.17] + const is118 = version >= protocolMap[1.18] + const is1191 = version >= protocolMap['1.19.1'] + if (packet.id === packetIds.CLIENTBOUND_LOGIN_PLAY(version)) { // Send Client Settings packet. - const clientSettingsId = is119 ? (is1191 ? 0x08 : 0x07) : 0x05 + const clientSettingsId = + packetIds.SERVERBOUND_CLIENT_SETTINGS(version) ?? 0 const viewDistance = Buffer.alloc(1) viewDistance.writeInt8(2) const skinParts = Buffer.alloc(1) @@ -144,35 +139,21 @@ export const packetHandler = connection .writePacket(clientSettingsId, concatPacketData(packetData)) .catch(handleError(addMessage, clientSettingsError)) - } else if ( - /* Respawn */ - (packet.id === 0x39 && !is117) || - (packet.id === 0x3d && is117 && !is119) || - (packet.id === 0x3b && is119 && !is1191) || - (packet.id === 0x3e && is1191) - ) { + } else if (packet.id === packetIds.CLIENTBOUND_RESPAWN(version)) { // Send spawn command when switching worlds. if (sendSpawnCommand) { connection - .writePacket(...makeChatMessagePacket('/spawn', protocolVersion)) + .writePacket(...makeChatMessagePacket('/spawn', version)) .catch(handleError(addMessage, sendMessageError)) } - } else if ( - /* Chat Message (clientbound) */ - (packet.id === 0x0e && !is117) || - (packet.id === 0x0f && is117 && !is119) - ) { + } else if (packet.id === packetIds.CLIENTBOUND_CHAT_MESSAGE(version)) { handleSystemMessage(packet, addMessage, handleError, is1191) } else if ( - /* System Chat Message (clientbound) */ - (packet.id === 0x5f && is119 && !is1191) || - (packet.id === 0x62 && is1191) + packet.id === packetIds.CLIENTBOUND_SYSTEM_CHAT_MESSAGE(version) ) { handleSystemMessage(packet, addMessage, handleError, is1191) } else if ( - /* Player Chat Message (clientbound) */ - (packet.id === 0x30 && is119 && !is1191) || - (packet.id === 0x33 && is1191) + packet.id === packetIds.CLIENTBOUND_PLAYER_CHAT_MESSAGE(version) ) { try { const { type, displayName, signedChat, unsignedChat } = @@ -190,25 +171,16 @@ export const packetHandler = } catch (e) { handleError(addMessage, parseMessageError)(e) } - } else if ( - /* Open Window */ - (packet.id === 0x2e && !is119) || - (packet.id === 0x2b && is119 && !is1191) || - (packet.id === 0x2d && is119) - ) { + } else if (packet.id === packetIds.CLIENTBOUND_OPEN_WINDOW(version)) { // Just close the window. const [windowId] = readVarInt(packet.data) const buf = Buffer.alloc(1) buf.writeUInt8(windowId) connection // Close Window (serverbound) - .writePacket(is1191 ? 0x0c : is119 ? 0x0b : is117 ? 0x09 : 0x0a, buf) + .writePacket(packetIds.SERVERBOUND_CLOSE_WINDOW(version) ?? 0, buf) .catch(handleError(addMessage, inventoryCloseError)) } else if ( - /* Death Combat Event */ - (packet.id === 0x31 && !is117) || - (packet.id === 0x35 && is117 && !is119) || - (packet.id === 0x33 && is119 && !is1191) || - (packet.id === 0x36 && is1191) + packet.id === packetIds.CLIENTBOUND_DEATH_COMBAT_EVENT(version) ) { let data = packet.data if (!is117) { @@ -231,15 +203,11 @@ export const packetHandler = // Automatically respawn. // LOW-TODO: Should this be manual, or a dialog, like MC? addMessage(deathRespawnMessage) + const clientStatusId = packetIds.SERVERBOUND_CLIENT_STATUS(version) ?? 0 connection // Client Status - .writePacket(is1191 ? 0x07 : is119 ? 0x06 : 0x04, writeVarInt(0)) + .writePacket(clientStatusId, writeVarInt(0)) .catch(handleError(addMessage, respawnError)) - } else if ( - /* Update Health */ - (packet.id === 0x49 && !is117) || - (packet.id === 0x52 && is117 && !is1191) || - (packet.id === 0x55 && is1191) - ) { + } else if (packet.id === packetIds.CLIENTBOUND_UPDATE_HEALTH(version)) { const newHealth = packet.data.readFloatBE(0) if (healthRef.current != null && healthRef.current > newHealth) { const info = healthMessage