Skip to content

Commit 460520f

Browse files
committed
Fix many bugs with Android native connection.
- Rename writeToConnection to writePacket. - Timeout correctly after 20 seconds of no data received. - Fix Login Start packet being malformed. - Fix incorrect UUID being returned from openConnection. - Break read thread loop correctly when an error occurs. - Properly read lock and write lock in the socket read thread. - Avoid conflicts with other NativeModules using ecm: prefix for events.
1 parent b47fc6e commit 460520f

File tree

2 files changed

+34
-33
lines changed

2 files changed

+34
-33
lines changed

android/app/src/main/java/com/enderchat/modules/connection/ConnectionModule.kt

+25-18
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class ConnectionModule(reactContext: ReactApplicationContext)
4646
loggedIn = false
4747
}
4848

49-
private fun directlyWriteToConnection(id: Int, data: ByteArray): Boolean {
49+
private fun directlyWritePacket(id: Int, data: ByteArray): Boolean {
5050
val packet = Packet(id, data)
5151
var asBytes =
5252
if (compressionEnabled) packet.writeCompressedPacket(compressionThreshold)
@@ -62,15 +62,15 @@ class ConnectionModule(reactContext: ReactApplicationContext)
6262
return socketIsOpen
6363
}
6464

65-
@ReactMethod fun writeToConnection(
65+
@ReactMethod fun writePacket(
6666
connId: String, packetId: Int, data: String, promise: Promise
6767
) = runBlocking {
6868
launch(Dispatchers.Default) {
6969
lock.read {
7070
if (connId == connectionId.toString()) {
7171
try {
7272
val dataBytes = Base64.decode(data, Base64.DEFAULT)
73-
promise.resolve(directlyWriteToConnection(packetId, dataBytes))
73+
promise.resolve(directlyWritePacket(packetId, dataBytes))
7474
} catch (e: Exception) {
7575
promise.reject(e)
7676
}
@@ -107,7 +107,8 @@ class ConnectionModule(reactContext: ReactApplicationContext)
107107

108108
// Create socket and connection ID.
109109
socket = Socket(host, port)
110-
this.connectionId = UUID.randomUUID()
110+
socket.soTimeout = 20 * 1000
111+
this.connectionId = connectionId
111112

112113
// Create data to send in Handshake.
113114
val portBuf = ByteBuffer.allocate(2)
@@ -123,7 +124,8 @@ class ConnectionModule(reactContext: ReactApplicationContext)
123124
socket.getOutputStream().write(Packet(0x00, handshakeData.toByteArray()).writePacket())
124125

125126
// Send Login Start packet.
126-
socket.getOutputStream().write(Base64.decode(loginPacket, Base64.DEFAULT))
127+
val loginPacketData = Base64.decode(loginPacket, Base64.DEFAULT)
128+
socket.getOutputStream().write(Packet(0x00, loginPacketData).writePacket())
127129

128130
// Update the current socket and resolve/reject.
129131
this.socket = socket
@@ -159,15 +161,18 @@ class ConnectionModule(reactContext: ReactApplicationContext)
159161
// Re-use the current thread, start reading from the socket.
160162
val buffer = ByteArrayOutputStream()
161163
val buf = ByteArray(4096)
162-
var aesDecipher: Cipher?
163-
while (lock.read {
164-
aesDecipher = this.aesDecipher
165-
return@read this.socket == socket
166-
}) {
164+
while (true) {
165+
lock.readLock().lock()
166+
if (this.socket != socket) {
167+
lock.readLock().unlock()
168+
break
169+
}
167170
try {
168171
val n = socket.getInputStream().read(buf)
169-
if (n == -1)
172+
if (n == -1) {
173+
lock.readLock().unlock()
170174
break
175+
}
171176

172177
// Decrypt if necessary.
173178
if (aesDecipher != null) {
@@ -187,8 +192,7 @@ class ConnectionModule(reactContext: ReactApplicationContext)
187192

188193
// We can handle Keep Alive, Login Success and Set Compression.
189194
if (packet.id.value == keepAliveClientBoundId) {
190-
directlyWriteToConnection(keepAliveServerBoundId, packet.data)
191-
continue
195+
directlyWritePacket(keepAliveServerBoundId, packet.data)
192196
} else if (packet.id.value == setCompressionId && !loggedIn) {
193197
val threshold = VarInt.read(packet.data)?.value ?: 0
194198
compressionThreshold = threshold
@@ -199,7 +203,7 @@ class ConnectionModule(reactContext: ReactApplicationContext)
199203

200204
// Forward the packet to JavaScript.
201205
val packetLengthLength =
202-
packet.totalLength - packet.data.size - packet.id.data.size
206+
packet.totalLength - (packet.data.size + packet.id.data.size)
203207
val params = Arguments.createMap().apply {
204208
putString("connectionId", connectionId.toString())
205209
putDouble("id", packet.id.value.toDouble())
@@ -210,15 +214,18 @@ class ConnectionModule(reactContext: ReactApplicationContext)
210214
putDouble("packetLength", packet.totalLength.toDouble())
211215
putDouble("lengthLength", packetLengthLength.toDouble())
212216
}
213-
sendEvent(reactContext = reactApplicationContext, "packet", params)
217+
sendEvent(reactContext = reactApplicationContext, "ecm:packet", params)
218+
lock.readLock().unlock()
214219
} catch (e: Exception) {
215-
lock.write { directlyCloseConnection() }
220+
lock.readLock().unlock()
221+
lock.write { if (this.socket == socket) directlyCloseConnection() }
216222
val params = Arguments.createMap().apply {
217223
putString("connectionId", connectionId.toString())
218224
putString("stackTrace", e.stackTraceToString())
219225
putString("message", e.message)
220226
}
221-
sendEvent(reactContext = reactApplicationContext, "error", params)
227+
sendEvent(reactContext = reactApplicationContext, "ecm:error", params)
228+
break
222229
}
223230
}
224231

@@ -228,7 +235,7 @@ class ConnectionModule(reactContext: ReactApplicationContext)
228235
val params = Arguments.createMap().apply {
229236
putString("connectionId", connectionId.toString())
230237
}
231-
sendEvent(reactContext = reactApplicationContext, "close", params)
238+
sendEvent(reactContext = reactApplicationContext, "ecm:close", params)
232239
}
233240
}
234241

src/minecraft/connection/native.ts

+9-15
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,8 @@ export class NativeServerConnection
5353
super()
5454
this.id = id
5555
this.options = options
56-
this.eventEmitter.addListener('packet', (event: NativePacketEvent) => {
57-
console.log(event)
56+
this.eventEmitter.addListener('ecm:packet', (event: NativePacketEvent) => {
5857
if (event.connectionId !== this.id) return
59-
// Handle timeout after 20 seconds of no data. (TODO: Handle this natively.)
60-
if (this.disconnectTimer) clearTimeout(this.disconnectTimer)
61-
this.disconnectTimer = setTimeout(() => this.close(), 20000)
6258
// Run after interactions to improve user experience.
6359
InteractionManager.runAfterInteractions(() => {
6460
const packet: Packet = {
@@ -123,18 +119,16 @@ export class NativeServerConnection
123119
this.emit('packet', packet)
124120
}).then(() => {}, console.error)
125121
})
126-
this.eventEmitter.addListener('error', (event: NativeErrorEvent) => {
127-
console.log(event)
122+
this.eventEmitter.addListener('ecm:error', (event: NativeErrorEvent) => {
128123
if (event.connectionId !== this.id) return
129124
console.error(event.stackTrace)
130125
this.emit('error', new Error(event.message))
131126
})
132-
this.eventEmitter.addListener('close', (event: NativeEvent) => {
133-
console.log(event)
127+
this.eventEmitter.addListener('ecm:close', (event: NativeEvent) => {
134128
if (event.connectionId !== this.id) return
135-
this.eventEmitter.removeAllListeners('packet')
136-
this.eventEmitter.removeAllListeners('error')
137-
this.eventEmitter.removeAllListeners('close')
129+
this.eventEmitter.removeAllListeners('ecm:packet')
130+
this.eventEmitter.removeAllListeners('ecm:error')
131+
this.eventEmitter.removeAllListeners('ecm:close')
138132
this.closed = true
139133
this.emit('close')
140134
})
@@ -149,9 +143,9 @@ export class NativeServerConnection
149143
if (this.closed) return
150144
this.closed = true
151145
ConnectionModule.closeConnection(this.id)
152-
this.eventEmitter.removeAllListeners('packet')
153-
this.eventEmitter.removeAllListeners('error')
154-
this.eventEmitter.removeAllListeners('close')
146+
this.eventEmitter.removeAllListeners('ecm:packet')
147+
this.eventEmitter.removeAllListeners('ecm:error')
148+
this.eventEmitter.removeAllListeners('ecm:close')
155149
}
156150
}
157151

0 commit comments

Comments
 (0)