@@ -24,6 +24,23 @@ function longToName(nameLong: BigInt): string {
24
24
return ac . split ( '' ) . reverse ( ) . join ( '' ) ;
25
25
}
26
26
27
+ /**
28
+ * Codes for user login attempts that are sent back to the game client
29
+ * to inform the user of the status of their login attempt.
30
+ */
31
+ enum LoginResponseCode {
32
+ SUCCESS = 2 ,
33
+ INVALID_CREDENTIALS = 3 ,
34
+ ACCOUNT_DISABLED = 4 ,
35
+ ALREADY_LOGGED_IN = 5 ,
36
+ GAME_UPDATED = 6 ,
37
+ WORLD_FULL = 7 ,
38
+ LOGIN_SERVER_OFFLINE = 8 ,
39
+ LOGIN_LIMIT_EXCEEDED = 9 ,
40
+ BAD_SESSION_ID = 10 ,
41
+ // @TODO the rest
42
+ }
43
+
27
44
/**
28
45
* Parses the login packet from the game client.
29
46
*/
@@ -83,15 +100,33 @@ export class ClientLoginParser extends DataParser {
83
100
throw new Error ( `Server key mismatch - ${ this . clientConnection . serverKey } != ${ incomingServerKey } ` ) ;
84
101
}
85
102
86
- const clientUuid = decrypted . get ( 'INT' ) ;
103
+ const gameClientId = decrypted . get ( 'INT' ) ;
87
104
const usernameLong = BigInt ( decrypted . get ( 'LONG' ) ) ;
88
105
const username = longToName ( usernameLong ) ;
89
106
const password = decrypted . getString ( ) ;
90
107
91
108
logger . info ( `Login request: ${ username } /${ password } ` ) ;
92
109
110
+ const credentialsResponseCode = this . checkCredentials ( username , password ) ;
111
+ if ( credentialsResponseCode === - 1 ) {
112
+ this . sendLogin ( [ clientKey1 , clientKey2 ] , gameClientId , username , password , isLowDetail ) ;
113
+ } else {
114
+ logger . warn ( `${ username } attempted to login but received error code ${ credentialsResponseCode } .` ) ;
115
+ this . sendLoginResponse ( credentialsResponseCode ) ;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Logs a user in and notifies their game client of a successful login.
121
+ * @param clientKeys The user's client keys (sent by the client).
122
+ * @param gameClientId The user's game client ID (sent by the client).
123
+ * @param username The user's username.
124
+ * @param password The user's password.
125
+ * @param isLowDetail Whether or not the user selected the "Low Detail" option.
126
+ */
127
+ private sendLogin ( clientKeys : [ number , number ] , gameClientId : number , username : string , password : string , isLowDetail : boolean ) : void {
93
128
const sessionKey : number [ ] = [
94
- Number ( clientKey1 ) , Number ( clientKey2 ) , Number ( this . clientConnection . serverKey >> BigInt ( 32 ) ) , Number ( this . clientConnection . serverKey )
129
+ Number ( clientKeys [ 0 ] ) , Number ( clientKeys [ 1 ] ) , Number ( this . clientConnection . serverKey >> BigInt ( 32 ) ) , Number ( this . clientConnection . serverKey )
95
130
] ;
96
131
97
132
const inCipher = new Isaac ( sessionKey ) ;
@@ -102,12 +137,12 @@ export class ClientLoginParser extends DataParser {
102
137
103
138
const outCipher = new Isaac ( sessionKey ) ;
104
139
105
- const player = new Player ( this . clientConnection . socket , inCipher , outCipher , clientUuid , username , password , isLowDetail ) ;
140
+ const player = new Player ( this . clientConnection . socket , inCipher , outCipher , gameClientId , username , password , isLowDetail ) ;
106
141
107
142
world . registerPlayer ( player ) ;
108
143
109
144
const outputBuffer = new ByteBuffer ( 6 ) ;
110
- outputBuffer . put ( 2 , 'BYTE' ) ; // login response code
145
+ outputBuffer . put ( LoginResponseCode . SUCCESS , 'BYTE' ) ;
111
146
outputBuffer . put ( player . rights . valueOf ( ) , 'BYTE' ) ;
112
147
outputBuffer . put ( 0 , 'BYTE' ) ; // ???
113
148
outputBuffer . put ( player . worldIndex + 1 , 'SHORT' ) ;
@@ -116,8 +151,44 @@ export class ClientLoginParser extends DataParser {
116
151
117
152
player . init ( ) ;
118
153
119
- this . clientConnection . clientKey1 = BigInt ( clientKey1 ) ;
120
- this . clientConnection . clientKey2 = BigInt ( clientKey2 ) ;
154
+ this . clientConnection . clientKey1 = BigInt ( clientKeys [ 0 ] ) ;
155
+ this . clientConnection . clientKey2 = BigInt ( clientKeys [ 1 ] ) ;
121
156
this . clientConnection . player = player ;
122
157
}
158
+
159
+ /**
160
+ * Validates an incoming user's credentials and returns an error code if a problem occurs.
161
+ * This also checks if the user is already online.
162
+ * @param username The incoming user's username input.
163
+ * @param password The incoming user's password input.
164
+ */
165
+ private checkCredentials ( username : string , password : string ) : number {
166
+ if ( ! username || ! password ) {
167
+ return LoginResponseCode . INVALID_CREDENTIALS ;
168
+ }
169
+
170
+ username = username . trim ( ) . toLowerCase ( ) ;
171
+ password = password . trim ( ) ;
172
+
173
+ if ( username === '' || password === '' ) {
174
+ return LoginResponseCode . INVALID_CREDENTIALS ;
175
+ }
176
+
177
+ if ( world . playerOnline ( username ) ) {
178
+ return LoginResponseCode . ALREADY_LOGGED_IN ;
179
+ }
180
+
181
+ return - 1 ;
182
+ }
183
+
184
+ /**
185
+ * Sends a login response code (by itself) to the game client.
186
+ * Used for error responses.
187
+ * @param responseCode The response code to send to the game client.
188
+ */
189
+ private sendLoginResponse ( responseCode : number ) : void {
190
+ const outputBuffer = new ByteBuffer ( 1 ) ;
191
+ outputBuffer . put ( responseCode , 'BYTE' ) ;
192
+ this . clientConnection . socket . write ( outputBuffer ) ;
193
+ }
123
194
}
0 commit comments