Skip to content

Commit a454359

Browse files
committed
Parse packets inside runAfterInteractions.
1 parent da4de47 commit a454359

File tree

1 file changed

+90
-85
lines changed

1 file changed

+90
-85
lines changed

src/minecraft/connection.ts

+90-85
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// import { createHash } from 'react-native-crypto'
2+
import { InteractionManager } from 'react-native'
23
import net from 'react-native-tcp'
34
import events from 'events'
45
import {
@@ -100,92 +101,96 @@ const initiateConnection = async (opts: {
100101
// Handle timeout after 20 seconds of no data.
101102
if (conn.disconnectTimer) clearTimeout(conn.disconnectTimer)
102103
conn.disconnectTimer = setTimeout(() => conn.close(), 20000)
103-
// Buffer data for read.
104-
conn.bufferedData = Buffer.concat([conn.bufferedData, newData])
105-
// ;(async () => { This would need a mutex.
106-
while (true) {
107-
const packet = conn.compressionEnabled
108-
? parseCompressedPacket(conn.bufferedData)
109-
: parsePacket(conn.bufferedData)
110-
if (packet) {
111-
if (packet.id === 0x03 && !conn.loggedIn) {
112-
const [threshold] = readVarInt(packet.data)
113-
conn.compressionThreshold = threshold
114-
conn.compressionEnabled = threshold >= 0
115-
} else if (packet.id === 0x02 && !conn.loggedIn) {
116-
conn.loggedIn = true
117-
} else if (packet.id === 0x21) {
118-
conn
119-
.writePacket(0x0f, packet.data)
120-
.catch(err => conn.emit('error', err))
121-
} else if (
122-
(packet.id === 0x00 && !conn.loggedIn) ||
123-
(packet.id === 0x1a && conn.loggedIn)
124-
) {
125-
const [chatLength, chatVarIntLength] = readVarInt(packet.data)
126-
conn.disconnectReason = packet.data
127-
.slice(chatVarIntLength, chatVarIntLength + chatLength)
128-
.toString('utf8')
129-
} /* else if (packet.id === 0x01 && !conn.loggedIn) {
130-
// What if no accessToken was provided?
131-
const [serverIdLen, serverIdLenLen] = readVarInt(packet.data)
132-
const serverId = packet.data.slice(
133-
serverIdLenLen,
134-
serverIdLen + serverIdLenLen
135-
)
136-
const data = packet.data.slice(serverIdLen + serverIdLenLen)
137-
const [pkLen, pkLenLen] = readVarInt(data)
138-
const publicKey = data.slice(pkLenLen, pkLen + pkLenLen)
139-
const verifyTokenData = data.slice(pkLen + pkLenLen)
140-
const [, verifyTokenLengthLength] = readVarInt(verifyTokenData)
141-
const verifyToken = verifyTokenData.slice(verifyTokenLengthLength)
142-
// TODO: https://wiki.vg/Protocol_Encryption
143-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
144-
;(async () => {
145-
// Generate random 16-byte shared secret.
146-
const sharedSecret = await generateSharedSecret()
147-
// Generate hash.
148-
const sha1 = createHash('sha1')
149-
sha1.update(serverId) // ASCII encoding of the server id string from Encryption Request
150-
sha1.update(sharedSecret)
151-
sha1.update(publicKey) // Server's encoded public key from Encryption Request
152-
const hash = mcHexDigest(sha1.digest())
153-
// Send hash to Mojang servers.
154-
const req = await fetch(
155-
'https://sessionserver.mojang.com/session/minecraft/join',
156-
{
157-
method: 'POST',
158-
body: JSON.stringify({})
159-
}
104+
// Run after interactions to improve user experience.
105+
InteractionManager.runAfterInteractions(() => {
106+
// Buffer data for read.
107+
// TODO: Implement decryption.
108+
conn.bufferedData = Buffer.concat([conn.bufferedData, newData])
109+
// ;(async () => { This would need a mutex.
110+
while (true) {
111+
const packet = conn.compressionEnabled
112+
? parseCompressedPacket(conn.bufferedData)
113+
: parsePacket(conn.bufferedData)
114+
if (packet) {
115+
if (packet.id === 0x03 && !conn.loggedIn) {
116+
const [threshold] = readVarInt(packet.data)
117+
conn.compressionThreshold = threshold
118+
conn.compressionEnabled = threshold >= 0
119+
} else if (packet.id === 0x02 && !conn.loggedIn) {
120+
conn.loggedIn = true
121+
} else if (packet.id === 0x21) {
122+
conn
123+
.writePacket(0x0f, packet.data)
124+
.catch(err => conn.emit('error', err))
125+
} else if (
126+
(packet.id === 0x00 && !conn.loggedIn) ||
127+
(packet.id === 0x1a && conn.loggedIn)
128+
) {
129+
const [chatLength, chatVarIntLength] = readVarInt(packet.data)
130+
conn.disconnectReason = packet.data
131+
.slice(chatVarIntLength, chatVarIntLength + chatLength)
132+
.toString('utf8')
133+
} /* else if (packet.id === 0x01 && !conn.loggedIn) {
134+
// What if no accessToken was provided?
135+
const [serverIdLen, serverIdLenLen] = readVarInt(packet.data)
136+
const serverId = packet.data.slice(
137+
serverIdLenLen,
138+
serverIdLen + serverIdLenLen
160139
)
161-
// POST https://sessionserver.mojang.com/session/minecraft/join
162-
// Body: {"accessToken": "<accessToken>",
163-
// "selectedProfile": "<player's uuid without dashes>",
164-
// "serverId": "<serverHash>"}
165-
// Encrypt shared secret and verify token with public key.
166-
// Send encryption response packet.
167-
// Encrypted Shared Secret Length - VarInt
168-
// Encrypted Shared Secret - Byte Array
169-
// Encrypted Verify Token Length - VarInt
170-
// Encrypted Verify Token - Byte Array
171-
// It then sends a Login Success, and enables AES/CFB8 encryption.
172-
// For the Initial Vector (IV) and AES setup, both sides use the shared
173-
// secret as both the IV and the key. Similarly, the client will also
174-
// enable encryption upon sending Encryption Response.
175-
// From this point forward, everything is encrypted.
176-
// Note: the entire packet is encrypted, including the length
177-
// fields and the packet's data.
178-
// The Login Success packet is sent encrypted.
179-
})()
180-
} */
181-
conn.bufferedData =
182-
conn.bufferedData.length <= packet.packetLength
183-
? Buffer.alloc(0) // Avoid errors shortening.
184-
: conn.bufferedData.slice(packet.packetLength)
185-
conn.emit('packet', packet)
186-
} else break
187-
}
188-
conn.emit('data', newData)
140+
const data = packet.data.slice(serverIdLen + serverIdLenLen)
141+
const [pkLen, pkLenLen] = readVarInt(data)
142+
const publicKey = data.slice(pkLenLen, pkLen + pkLenLen)
143+
const verifyTokenData = data.slice(pkLen + pkLenLen)
144+
const [, verifyTokenLengthLength] = readVarInt(verifyTokenData)
145+
const verifyToken = verifyTokenData.slice(verifyTokenLengthLength)
146+
// TODO: https://wiki.vg/Protocol_Encryption
147+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
148+
;(async () => {
149+
// Generate random 16-byte shared secret.
150+
const sharedSecret = await generateSharedSecret()
151+
// Generate hash.
152+
const sha1 = createHash('sha1')
153+
sha1.update(serverId) // ASCII encoding of the server id string from Encryption Request
154+
sha1.update(sharedSecret)
155+
sha1.update(publicKey) // Server's encoded public key from Encryption Request
156+
const hash = mcHexDigest(sha1.digest())
157+
// Send hash to Mojang servers.
158+
const req = await fetch(
159+
'https://sessionserver.mojang.com/session/minecraft/join',
160+
{
161+
method: 'POST',
162+
body: JSON.stringify({})
163+
}
164+
)
165+
// POST https://sessionserver.mojang.com/session/minecraft/join
166+
// Body: {"accessToken": "<accessToken>",
167+
// "selectedProfile": "<player's uuid without dashes>",
168+
// "serverId": "<serverHash>"}
169+
// Encrypt shared secret and verify token with public key.
170+
// Send encryption response packet.
171+
// Encrypted Shared Secret Length - VarInt
172+
// Encrypted Shared Secret - Byte Array
173+
// Encrypted Verify Token Length - VarInt
174+
// Encrypted Verify Token - Byte Array
175+
// It then sends a Login Success, and enables AES/CFB8 encryption.
176+
// For the Initial Vector (IV) and AES setup, both sides use the shared
177+
// secret as both the IV and the key. Similarly, the client will also
178+
// enable encryption upon sending Encryption Response.
179+
// From this point forward, everything is encrypted.
180+
// Note: the entire packet is encrypted, including the length
181+
// fields and the packet's data.
182+
// The Login Success packet is sent encrypted.
183+
})()
184+
} */
185+
conn.bufferedData =
186+
conn.bufferedData.length <= packet.packetLength
187+
? Buffer.alloc(0) // Avoid errors shortening.
188+
: conn.bufferedData.slice(packet.packetLength)
189+
conn.emit('packet', packet)
190+
} else break
191+
}
192+
conn.emit('data', newData)
193+
}).then(() => {}, console.error)
189194
})
190195
socket.on('close', () => {
191196
conn.closed = true

0 commit comments

Comments
 (0)