Skip to content

Commit

Permalink
Add protocol ID map, filter parsed native packets.
Browse files Browse the repository at this point in the history
We now provide an array of accepted clientbound IDs to the native
connection module when creating a connection.
This reduces JavaScript<->native communication considerably.
  • Loading branch information
retrixe committed Feb 24, 2023
1 parent 4b14002 commit 18dad9e
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 87 deletions.
24 changes: 8 additions & 16 deletions src/minecraft/connection/javascript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) &
Expand Down Expand Up @@ -137,34 +138,25 @@ 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
conn.compressionEnabled = threshold >= 0
} 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
Expand All @@ -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)
Expand Down
21 changes: 11 additions & 10 deletions src/minecraft/connection/native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -85,21 +86,16 @@ 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 */) {
this.loggedIn = true
} 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
Expand All @@ -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')
Expand Down Expand Up @@ -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)
}
Expand Down
14 changes: 6 additions & 8 deletions src/minecraft/packets/chat.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { concatPacketData, PacketDataTypes } from '../packet'
import { protocolMap, writeVarInt } from '../utils'
import packetIds from './ids'

export const makeChatMessagePacket = (
msg: string,
Expand All @@ -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)
Expand All @@ -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)]
}
}
119 changes: 119 additions & 0 deletions src/minecraft/packets/ids.ts
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 18dad9e

Please sign in to comment.