174 lines
5.3 KiB
JavaScript
Executable File
174 lines
5.3 KiB
JavaScript
Executable File
import { Session } from "../models/session.js";
|
|
import * as msg from "../utils/messages.js";
|
|
import * as security from "../utils/security.js";
|
|
import { Player } from "../models/player.js";
|
|
import { AuthState } from "./authState.js";
|
|
import { Config } from "../config.js";
|
|
|
|
const USERNAME_PROMPT =
|
|
"Enter a valid username (4-20 characters, [a-z], [A-Z], [0-9], and underscore)";
|
|
const PASSWORD_PROMPT = "Enter a valid password";
|
|
const PASSWORD_PROMPT2 = "Enter your password again";
|
|
const ERROR_INSANE_PASSWORD = "Invalid password.";
|
|
const ERROR_INSANE_USERNAME =
|
|
"Invalid username. It must be 4-20 characters, and may only contain [a-z], [A-Z], [0-9] and underscore";
|
|
const ERROR_INCORRECT_PASSWOD = "Incorrect password.";
|
|
|
|
/** @property {Session} session */
|
|
export class PlayerCreationState {
|
|
/**
|
|
* @proteted
|
|
* @type {(msg: ClientMessage) => }
|
|
*
|
|
* Allows us to dynamically set which
|
|
* method handles incoming messages.
|
|
*/
|
|
_dynamicMessageHandler;
|
|
|
|
/** @protected @type {Player} */
|
|
_player;
|
|
/** @protected @type {string} */
|
|
_password;
|
|
|
|
/**
|
|
* @param {Session} session
|
|
*/
|
|
constructor(session) {
|
|
/** @type {Session} */
|
|
this.session = session;
|
|
}
|
|
|
|
onAttach() {
|
|
//
|
|
// If there are too many players, stop allowing new players in.
|
|
if (this.session.game._players.size >= Config.maxPlayers) {
|
|
this.session.sendCalamity(
|
|
"Server is full, no more players can be created",
|
|
);
|
|
this.session.close();
|
|
}
|
|
|
|
this.session.sendFigletMessage("New Player");
|
|
this.session.sendPrompt("username", USERNAME_PROMPT);
|
|
|
|
// our initial substate is to receive a username
|
|
this.setMessageHandler(this.receiveUsername);
|
|
}
|
|
|
|
/** @param {msg.ClientMessage} message */
|
|
onMessage(message) {
|
|
this._dynamicMessageHandler(message);
|
|
}
|
|
|
|
/* @param {(msg: ClientMessage) => } handler */
|
|
setMessageHandler(handler) {
|
|
this._dynamicMessageHandler = handler;
|
|
}
|
|
|
|
/** @param {msg.ClientMessage} message */
|
|
receiveUsername(message) {
|
|
//
|
|
// NOTE FOR ACCOUNT CREATION
|
|
// Do adult-word checks, so we dont have Fucky_McFuckFace
|
|
// https://www.npmjs.com/package/glin-profanity
|
|
|
|
//
|
|
// handle invalid message types
|
|
if (!message.isUsernameResponse()) {
|
|
this.session.sendError("Incorrect message type!");
|
|
this.session.sendPrompt("username", USERNAME_PROMPT);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// do basic syntax checks on usernames
|
|
if (!security.isUsernameSane(message.username)) {
|
|
this.session.sendError(ERROR_INSANE_USERNAME);
|
|
this.session.sendPrompt("username", USERNAME_PROMPT);
|
|
return;
|
|
}
|
|
|
|
const player = this.session.game.createPlayer(message.username);
|
|
|
|
//
|
|
// handle taken/occupied username
|
|
if (player === false) {
|
|
// Telling the user right away that the username is taken can
|
|
// lead to data leeching. But fukkit.
|
|
|
|
this.session.sendError(
|
|
`Username _${message.username}_ was taken by another player.`,
|
|
);
|
|
this.session.sendPrompt("username", USERNAME_PROMPT);
|
|
return;
|
|
}
|
|
|
|
this._player = player;
|
|
|
|
this.session.sendSystemMessage("salt", player.salt);
|
|
this.session.sendMessage(
|
|
`Username _*${message.username}*_ is available, and I've reserved it for you :)`,
|
|
);
|
|
this.session.sendPrompt("password", PASSWORD_PROMPT);
|
|
this.setMessageHandler(this.receivePassword);
|
|
}
|
|
|
|
/** @param {msg.ClientMessage} message */
|
|
receivePassword(message) {
|
|
//
|
|
// handle invalid message types
|
|
if (!message.isPasswordResponse()) {
|
|
console.log("Invalid message type, expected password reply", message);
|
|
this.session.sendError("Incorrect message type!");
|
|
this.session.sendPrompt("password", PASSWORD_PROMPT);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check that it's been hashed thoroughly before being sent here.
|
|
if (!security.isPasswordSane(message.password)) {
|
|
this.session.sendError(ERROR_INSANE_PASSWORD);
|
|
this.session.sendPrompt("password", PASSWORD_PROMPT);
|
|
return;
|
|
}
|
|
|
|
this._password = message.password; // it's relatively safe to store the PW here temporarily. The client already hashed the hell out of it.
|
|
this.session.sendPrompt("password", PASSWORD_PROMPT2);
|
|
|
|
this.setMessageHandler(this.receivePasswordConfirmation);
|
|
}
|
|
|
|
/** @param {msg.ClientMessage} memssage */
|
|
receivePasswordConfirmation(message) {
|
|
//
|
|
// handle invalid message types
|
|
if (!message.isPasswordResponse()) {
|
|
console.log("Invalid message type, expected password reply", message);
|
|
this.session.sendError("Incorrect message type!");
|
|
this.session.sendPrompt("password", PASSWORD_PROMPT);
|
|
this.setMessageHandler(this.receivePassword);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Handle mismatching passwords
|
|
if (message.password !== this._password) {
|
|
this.session.sendError(
|
|
"Incorrect, you have to enter your password twice in a row successfully",
|
|
);
|
|
this.session.sendPrompt("password", PASSWORD_PROMPT);
|
|
this.setMessageHandler(this.receivePassword);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Success!
|
|
// Take the user to the login screen.
|
|
this.session.sendMessage(
|
|
"*_Success_* ✅ You will now be asked to log in again, sorry for that ;)",
|
|
);
|
|
this._player.setPasswordHash(security.generateHash(this._password));
|
|
this.session.setState(new AuthState(this.session));
|
|
}
|
|
}
|