Skip to content

Commit af3d7b7

Browse files
Merge pull request #159 from rune-js/login-responses
Adding some basic login response codes so accounts can not log in twice.
2 parents 053a1a4 + c758147 commit af3d7b7

File tree

3 files changed

+90
-14
lines changed

3 files changed

+90
-14
lines changed

src/net/data-parser/client-login-parser.ts

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,23 @@ function longToName(nameLong: BigInt): string {
2424
return ac.split('').reverse().join('');
2525
}
2626

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+
2744
/**
2845
* Parses the login packet from the game client.
2946
*/
@@ -83,15 +100,33 @@ export class ClientLoginParser extends DataParser {
83100
throw new Error(`Server key mismatch - ${this.clientConnection.serverKey} != ${incomingServerKey}`);
84101
}
85102

86-
const clientUuid = decrypted.get('INT');
103+
const gameClientId = decrypted.get('INT');
87104
const usernameLong = BigInt(decrypted.get('LONG'));
88105
const username = longToName(usernameLong);
89106
const password = decrypted.getString();
90107

91108
logger.info(`Login request: ${username}/${password}`);
92109

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 {
93128
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)
95130
];
96131

97132
const inCipher = new Isaac(sessionKey);
@@ -102,12 +137,12 @@ export class ClientLoginParser extends DataParser {
102137

103138
const outCipher = new Isaac(sessionKey);
104139

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);
106141

107142
world.registerPlayer(player);
108143

109144
const outputBuffer = new ByteBuffer(6);
110-
outputBuffer.put(2, 'BYTE'); // login response code
145+
outputBuffer.put(LoginResponseCode.SUCCESS, 'BYTE');
111146
outputBuffer.put(player.rights.valueOf(), 'BYTE');
112147
outputBuffer.put(0, 'BYTE'); // ???
113148
outputBuffer.put(player.worldIndex + 1, 'SHORT');
@@ -116,8 +151,44 @@ export class ClientLoginParser extends DataParser {
116151

117152
player.init();
118153

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]);
121156
this.clientConnection.player = player;
122157
}
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+
}
123194
}

src/world/actor/player/updating/actor-updating.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function registerNewActors(packet: Packet, player: Player, trackedActors:
3535
continue;
3636
}
3737

38-
if(!world.playerExists(nearbyActor)) {
38+
if(!world.playerOnline(nearbyActor)) {
3939
// Other player is no longer in the game world
4040
continue;
4141
}
@@ -85,7 +85,7 @@ export function updateTrackedActors(packet: Packet, playerPosition: Position, ap
8585
let exists = true;
8686

8787
if(trackedActor instanceof Player) {
88-
if(!world.playerExists(trackedActor as Player)) {
88+
if(!world.playerOnline(trackedActor as Player)) {
8989
exists = false;
9090
}
9191
} else {

src/world/world.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -475,13 +475,18 @@ export class World {
475475
return Promise.resolve();
476476
}
477477

478-
public playerExists(player: Player): boolean {
479-
const foundPlayer = this.playerList[player.worldIndex];
480-
if(!foundPlayer) {
481-
return false;
482-
}
478+
public playerOnline(player: Player | string): boolean {
479+
if(typeof player === 'string') {
480+
player = player.toLowerCase();
481+
return this.playerList.findIndex(p => p !== null && p.username.toLowerCase() === player) !== -1;
482+
} else {
483+
const foundPlayer = this.playerList[player.worldIndex];
484+
if(!foundPlayer) {
485+
return false;
486+
}
483487

484-
return foundPlayer.equals(player);
488+
return foundPlayer.equals(player);
489+
}
485490
}
486491

487492
public registerPlayer(player: Player): boolean {

0 commit comments

Comments
 (0)