@@ -83,140 +83,131 @@ export class JavaScriptServerConnection
83
83
84
84
const initiateJavaScriptConnection = async ( opts : ConnectionOptions ) => {
85
85
const [ host , port ] = await resolveHostname ( opts . host , opts . port )
86
- return await new Promise < ServerConnection > ( ( resolve , reject ) => {
87
- const socket = net . createConnection ( { host, port } )
88
- const conn = new JavaScriptServerConnection ( socket , opts )
89
- let resolved = false
90
- const { accessToken, selectedProfile } = opts
91
- socket . on ( 'connect' , ( ) => {
92
- // Create data to send in Handshake.
93
- const portBuf = Buffer . alloc ( 2 )
94
- portBuf . writeUInt16BE ( port )
95
- const handshakeData = [
96
- writeVarInt ( opts . protocolVersion ) ,
97
- host ,
98
- portBuf ,
99
- writeVarInt ( 2 )
100
- ]
101
- // Initialise Handshake with server.
102
- socket . write ( makeBasePacket ( 0x00 , concatPacketData ( handshakeData ) ) , ( ) =>
103
- // Send Login Start packet.
104
- socket . write ( makeBasePacket ( 0x00 , getLoginPacket ( opts ) ) , ( ) => {
105
- resolved = true
106
- resolve ( conn )
107
- } )
108
- )
109
- } )
110
- socket . on ( 'close' , ( ) => {
111
- conn . closed = true
112
- conn . emit ( 'close' )
113
- } )
114
- socket . on ( 'error' , err => {
115
- if ( ! resolved ) reject ( err )
116
- else {
117
- conn . disconnectReason = err . message
118
- conn . emit ( 'error' , err )
119
- }
120
- } )
121
- const lock = new Semaphore ( 1 )
122
- socket . on ( 'data' , newData => {
123
- // Handle timeout after 20 seconds of no data.
124
- if ( conn . disconnectTimer ) clearTimeout ( conn . disconnectTimer )
125
- conn . disconnectTimer = setTimeout ( ( ) => conn . close ( ) , 20000 )
126
- // Run after interactions to improve user experience.
127
- InteractionManager . runAfterInteractions ( async ( ) => {
128
- await lock . acquire ( )
129
- try {
130
- // Note: the entire packet is encrypted, including the length fields and the packet's data.
131
- // https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/src/transforms/encryption.js
132
- let finalData = newData
133
- if ( conn . aesDecipher ) finalData = conn . aesDecipher . update ( newData )
134
- // Buffer data for read.
135
- conn . bufferedData = Buffer . concat ( [ conn . bufferedData , finalData ] )
136
- while ( true ) {
137
- const packet = conn . compressionEnabled
138
- ? await parseCompressedPacket ( conn . bufferedData )
139
- : parsePacket ( conn . bufferedData )
140
- if ( packet ) {
141
- // Remove packet from buffered data.
142
- conn . bufferedData =
143
- conn . bufferedData . length <= packet . packetLength
144
- ? Buffer . alloc ( 0 ) // Avoid errors shortening.
145
- : conn . bufferedData . slice ( packet . packetLength )
146
- // Internally handle login packets.
147
- const is1164 =
148
- conn . options . protocolVersion >= protocolMap [ '1.16.4' ]
149
- const is117 = conn . options . protocolVersion >= protocolMap [ 1.17 ]
150
- const is119 = conn . options . protocolVersion >= protocolMap [ 1.19 ]
151
- const is1191 =
152
- conn . options . protocolVersion >= protocolMap [ '1.19.1' ]
153
- if ( packet . id === 0x03 && ! conn . loggedIn /* Set Compression */ ) {
154
- const [ threshold ] = readVarInt ( packet . data )
155
- conn . compressionThreshold = threshold
156
- conn . compressionEnabled = threshold >= 0
157
- } else if ( packet . id === 0x02 && ! conn . loggedIn ) {
158
- conn . loggedIn = true // Login Success
159
- } else if (
160
- // Keep Alive (clientbound)
161
- ( packet . id === 0x1f && is1164 && ! is117 ) ||
162
- ( packet . id === 0x21 && is117 && ! is119 ) ||
163
- ( packet . id === 0x1e && is119 && ! is1191 ) ||
164
- ( packet . id === 0x20 && is1191 )
165
- ) {
166
- const id = is1191 ? 0x12 : is119 ? 0x11 : is117 ? 0x0f : 0x10
167
- conn
168
- . writePacket ( id , packet . data )
169
- . catch ( err => conn . emit ( 'error' , err ) )
170
- } else if (
171
- // Disconnect (login) or Disconnect (play)
172
- ( packet . id === 0x00 && ! conn . loggedIn ) ||
173
- ( packet . id === 0x19 && conn . loggedIn && is1164 && ! is117 ) ||
174
- ( packet . id === 0x1a && conn . loggedIn && is117 && ! is119 ) ||
175
- ( packet . id === 0x17 && conn . loggedIn && is119 && ! is1191 ) ||
176
- ( packet . id === 0x19 && conn . loggedIn && is1191 )
177
- ) {
178
- const [ chatLength , chatVarIntLength ] = readVarInt ( packet . data )
179
- conn . disconnectReason = packet . data
180
- . slice ( chatVarIntLength , chatVarIntLength + chatLength )
181
- . toString ( 'utf8' )
182
- } else if ( packet . id === 0x04 && ! conn . loggedIn ) {
183
- /* Login Plugin Request */
184
- const [ msgId ] = readVarInt ( packet . data )
185
- const rs = concatPacketData ( [ writeVarInt ( msgId ) , false ] )
186
- conn . writePacket ( 0x02 , rs ) . catch ( err => conn . emit ( 'error' , err ) )
187
- } else if ( packet . id === 0x01 && ! conn . loggedIn ) {
188
- /* Encryption Request */
189
- if ( ! accessToken || ! selectedProfile ) {
190
- conn . disconnectReason =
191
- '{"text":"This server requires a premium account to be logged in!"}'
192
- conn . close ( )
193
- continue
194
- }
195
- handleEncryptionRequest (
196
- packet ,
197
- accessToken ,
198
- selectedProfile ,
199
- conn ,
200
- is119 ,
201
- async ( secret : Buffer , response : Buffer ) => {
202
- const AES_ALG = 'aes-128-cfb8'
203
- conn . aesDecipher = createDecipheriv ( AES_ALG , secret , secret )
204
- await conn . writePacket ( 0x01 , response )
205
- conn . aesCipher = createCipheriv ( AES_ALG , secret , secret )
206
- }
207
- )
86
+ const socket = net . createConnection ( { host, port } )
87
+ const conn = new JavaScriptServerConnection ( socket , opts )
88
+ const { accessToken, selectedProfile } = opts
89
+ socket . on ( 'connect' , ( ) => {
90
+ conn . emit ( 'connect' )
91
+ // Create data to send in Handshake.
92
+ const portBuf = Buffer . alloc ( 2 )
93
+ portBuf . writeUInt16BE ( port )
94
+ const handshakeData = [
95
+ writeVarInt ( opts . protocolVersion ) ,
96
+ host ,
97
+ portBuf ,
98
+ writeVarInt ( 2 )
99
+ ]
100
+ // Initialise Handshake with server.
101
+ socket . write ( makeBasePacket ( 0x00 , concatPacketData ( handshakeData ) ) , ( ) =>
102
+ // Send Login Start packet.
103
+ socket . write ( makeBasePacket ( 0x00 , getLoginPacket ( opts ) ) )
104
+ )
105
+ } )
106
+ socket . on ( 'close' , ( ) => {
107
+ conn . closed = true
108
+ conn . emit ( 'close' )
109
+ } )
110
+ socket . on ( 'error' , err => {
111
+ conn . disconnectReason = err . message
112
+ conn . emit ( 'error' , err )
113
+ } )
114
+ const lock = new Semaphore ( 1 )
115
+ socket . on ( 'data' , newData => {
116
+ // Handle timeout after 20 seconds of no data.
117
+ if ( conn . disconnectTimer ) clearTimeout ( conn . disconnectTimer )
118
+ conn . disconnectTimer = setTimeout ( ( ) => conn . close ( ) , 20000 )
119
+ // Run after interactions to improve user experience.
120
+ InteractionManager . runAfterInteractions ( async ( ) => {
121
+ await lock . acquire ( )
122
+ try {
123
+ // Note: the entire packet is encrypted, including the length fields and the packet's data.
124
+ // https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/src/transforms/encryption.js
125
+ let finalData = newData
126
+ if ( conn . aesDecipher ) finalData = conn . aesDecipher . update ( newData )
127
+ // Buffer data for read.
128
+ conn . bufferedData = Buffer . concat ( [ conn . bufferedData , finalData ] )
129
+ while ( true ) {
130
+ const packet = conn . compressionEnabled
131
+ ? await parseCompressedPacket ( conn . bufferedData )
132
+ : parsePacket ( conn . bufferedData )
133
+ if ( packet ) {
134
+ // Remove packet from buffered data.
135
+ conn . bufferedData =
136
+ conn . bufferedData . length <= packet . packetLength
137
+ ? Buffer . alloc ( 0 ) // Avoid errors shortening.
138
+ : conn . bufferedData . slice ( packet . packetLength )
139
+ // Internally handle login packets.
140
+ const is1164 = conn . options . protocolVersion >= protocolMap [ '1.16.4' ]
141
+ const is117 = conn . options . protocolVersion >= protocolMap [ 1.17 ]
142
+ const is119 = conn . options . protocolVersion >= protocolMap [ 1.19 ]
143
+ const is1191 = conn . options . protocolVersion >= protocolMap [ '1.19.1' ]
144
+ if ( packet . id === 0x03 && ! conn . loggedIn /* Set Compression */ ) {
145
+ const [ threshold ] = readVarInt ( packet . data )
146
+ conn . compressionThreshold = threshold
147
+ conn . compressionEnabled = threshold >= 0
148
+ } else if ( packet . id === 0x02 && ! conn . loggedIn ) {
149
+ conn . loggedIn = true // Login Success
150
+ } else if (
151
+ // Keep Alive (clientbound)
152
+ ( packet . id === 0x1f && is1164 && ! is117 ) ||
153
+ ( packet . id === 0x21 && is117 && ! is119 ) ||
154
+ ( packet . id === 0x1e && is119 && ! is1191 ) ||
155
+ ( packet . id === 0x20 && is1191 )
156
+ ) {
157
+ const id = is1191 ? 0x12 : is119 ? 0x11 : is117 ? 0x0f : 0x10
158
+ conn
159
+ . writePacket ( id , packet . data )
160
+ . catch ( err => conn . emit ( 'error' , err ) )
161
+ } else if (
162
+ // Disconnect (login) or Disconnect (play)
163
+ ( packet . id === 0x00 && ! conn . loggedIn ) ||
164
+ ( packet . id === 0x19 && conn . loggedIn && is1164 && ! is117 ) ||
165
+ ( packet . id === 0x1a && conn . loggedIn && is117 && ! is119 ) ||
166
+ ( packet . id === 0x17 && conn . loggedIn && is119 && ! is1191 ) ||
167
+ ( packet . id === 0x19 && conn . loggedIn && is1191 )
168
+ ) {
169
+ const [ chatLength , chatVarIntLength ] = readVarInt ( packet . data )
170
+ conn . disconnectReason = packet . data
171
+ . slice ( chatVarIntLength , chatVarIntLength + chatLength )
172
+ . toString ( 'utf8' )
173
+ } else if ( packet . id === 0x04 && ! conn . loggedIn ) {
174
+ /* Login Plugin Request */
175
+ const [ msgId ] = readVarInt ( packet . data )
176
+ const rs = concatPacketData ( [ writeVarInt ( msgId ) , false ] )
177
+ conn . writePacket ( 0x02 , rs ) . catch ( err => conn . emit ( 'error' , err ) )
178
+ } else if ( packet . id === 0x01 && ! conn . loggedIn ) {
179
+ /* Encryption Request */
180
+ if ( ! accessToken || ! selectedProfile ) {
181
+ conn . disconnectReason =
182
+ '{"text":"This server requires a premium account to be logged in!"}'
183
+ conn . close ( )
184
+ continue
208
185
}
209
- conn . emit ( 'packet' , packet )
210
- } else break
211
- }
212
- conn . emit ( 'data' , newData )
213
- } catch ( err ) {
214
- conn . emit ( 'error' , err )
186
+ handleEncryptionRequest (
187
+ packet ,
188
+ accessToken ,
189
+ selectedProfile ,
190
+ conn ,
191
+ is119 ,
192
+ async ( secret : Buffer , response : Buffer ) => {
193
+ const AES_ALG = 'aes-128-cfb8'
194
+ conn . aesDecipher = createDecipheriv ( AES_ALG , secret , secret )
195
+ await conn . writePacket ( 0x01 , response )
196
+ conn . aesCipher = createCipheriv ( AES_ALG , secret , secret )
197
+ }
198
+ )
199
+ }
200
+ conn . emit ( 'packet' , packet )
201
+ } else break
215
202
}
216
- lock . release ( )
217
- } ) . then ( ( ) => { } , console . error )
218
- } )
203
+ conn . emit ( 'data' , newData )
204
+ } catch ( err ) {
205
+ conn . emit ( 'error' , err )
206
+ }
207
+ lock . release ( )
208
+ } ) . then ( ( ) => { } , console . error )
219
209
} )
210
+ return conn
220
211
}
221
212
222
213
export default initiateJavaScriptConnection
0 commit comments