Things and støff
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
import WebSocket from "ws";
|
import WebSocket from "ws";
|
||||||
import { Player } from "./player.js";
|
import { Player } from "./player.js";
|
||||||
import * as msg from "../utils/messages.js";
|
|
||||||
import { mustBeString, mustBe } from "../utils/mustbe.js";
|
import { mustBeString, mustBe } from "../utils/mustbe.js";
|
||||||
import { Scene } from "../scenes/scene.js";
|
import { Scene } from "../scenes/scene.js";
|
||||||
import { gGame } from "./globals.js";
|
import { gGame } from "./globals.js";
|
||||||
|
import { formatMessage, MessageType } from "../utils/messages.js";
|
||||||
|
|
||||||
export class Session {
|
export class Session {
|
||||||
/** @type {WebSocket} */
|
/** @type {WebSocket} */
|
||||||
@@ -71,7 +71,7 @@ export class Session {
|
|||||||
/**
|
/**
|
||||||
* Send a message via our websocket.
|
* Send a message via our websocket.
|
||||||
*
|
*
|
||||||
* @param {string|number} messageType
|
* @param {MessageType} messageType The message "header" (the first arg in the array sent to the client) holds the message type.
|
||||||
* @param {...any} args
|
* @param {...any} args
|
||||||
*/
|
*/
|
||||||
send(messageType, ...args) {
|
send(messageType, ...args) {
|
||||||
@@ -79,7 +79,7 @@ export class Session {
|
|||||||
console.error("Trying to send a message without a valid websocket", messageType, args);
|
console.error("Trying to send a message without a valid websocket", messageType, args);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._websocket.send(JSON.stringify([messageType, ...args]));
|
this._websocket.send(formatMessage(messageType, ...args));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,7 +100,7 @@ export class Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.send(
|
this.send(
|
||||||
msg.PROMPT, // message type
|
MessageType.PROMPT, // message type
|
||||||
text, // TODO: prompt text must be string or an array of strings
|
text, // TODO: prompt text must be string or an array of strings
|
||||||
mustBe(options, "object"),
|
mustBe(options, "object"),
|
||||||
);
|
);
|
||||||
@@ -113,12 +113,12 @@ export class Session {
|
|||||||
* @param {object?} options message options for the client.
|
* @param {object?} options message options for the client.
|
||||||
*/
|
*/
|
||||||
sendText(text, options = {}) {
|
sendText(text, options = {}) {
|
||||||
this.send(msg.TEXT, text, options);
|
this.send(MessageType.TEXT, text, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {string|string[]} errorMessage */
|
/** @param {string|string[]} errorMessage */
|
||||||
sendError(errorMessage) {
|
sendError(errorMessage, options = { verbatim: true }) {
|
||||||
this.send(msg.ERROR, mustBeString(errorMessage));
|
this.send(MessageType.ERROR, mustBeString(errorMessage), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,15 +128,15 @@ export class Session {
|
|||||||
calamity(errorMessage) {
|
calamity(errorMessage) {
|
||||||
//
|
//
|
||||||
// The client should know not to format calamaties anyway, but we add “preformatted” anyway
|
// The client should know not to format calamaties anyway, but we add “preformatted” anyway
|
||||||
this.send(msg.CALAMITY, errorMessage, { preformatted: true });
|
this.send(MessageType.CALAMITY, errorMessage, { preformatted: true });
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} systemMessageType
|
* @param {MessageType} systemMessageType
|
||||||
* @param {any?} value
|
* @param {any?} value
|
||||||
*/
|
*/
|
||||||
sendSystemMessage(systemMessageType, value = undefined) {
|
sendSystemMessage(systemMessageType, value = undefined) {
|
||||||
this.send(msg.SYSTEM, mustBeString(systemMessageType), value);
|
this.send(MessageType.SYSTEM, mustBeString(systemMessageType), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
server/package-lock.json
generated
7
server/package-lock.json
generated
@@ -10,6 +10,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"figlet": "^1.8.2",
|
"figlet": "^1.8.2",
|
||||||
|
"sprintf-js": "^1.1.3",
|
||||||
"ws": "^8.14.2"
|
"ws": "^8.14.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -378,6 +379,12 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sprintf-js": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/supports-color": {
|
"node_modules/supports-color": {
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"figlet": "^1.8.2",
|
"figlet": "^1.8.2",
|
||||||
|
"sprintf-js": "^1.1.3",
|
||||||
"ws": "^8.14.2"
|
"ws": "^8.14.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -33,7 +34,6 @@
|
|||||||
"quoteProps": "consistent",
|
"quoteProps": "consistent",
|
||||||
"singleQuote": false,
|
"singleQuote": false,
|
||||||
"trailingComma": "all",
|
"trailingComma": "all",
|
||||||
"tabWidth": 4,
|
|
||||||
"bracketSpacing": true,
|
"bracketSpacing": true,
|
||||||
"objectWrap": "preserve",
|
"objectWrap": "preserve",
|
||||||
"arrowParens": "always"
|
"arrowParens": "always"
|
||||||
|
|||||||
BIN
server/public/android-chrome-192x192.png
Normal file
BIN
server/public/android-chrome-192x192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
BIN
server/public/android-chrome-512x512.png
Normal file
BIN
server/public/android-chrome-512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 448 KiB |
BIN
server/public/apple-touch-icon.png
Normal file
BIN
server/public/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@@ -1,11 +1,16 @@
|
|||||||
import { crackdown } from "./crackdown.js";
|
import { crackdown } from "./crackdown.js";
|
||||||
|
import { parseArgs } from "./parseArgs.js";
|
||||||
|
import { MessageType } from "./messages.js";
|
||||||
|
|
||||||
const MsgContext.REPLY = "R";
|
/** Regex to validate if a :help [topic] command i entered correctly */
|
||||||
const QUIT = "QUIT";
|
|
||||||
const HELP = "HELP";
|
|
||||||
const COLON = ":";
|
|
||||||
const helpRegex = /^:help(?:\s+(.*))?$/;
|
const helpRegex = /^:help(?:\s+(.*))?$/;
|
||||||
const colonRegex = /^:([a-z0-9_]+)(?:\s+(.*))?$/;
|
|
||||||
|
/** Regex to validate if a :<command> [args] was entered correctly */
|
||||||
|
const colonRegex = /^:([a-z0-9_]+)(?:\s+(.*?)\s*)?$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The client that talks to the MUD Sever
|
||||||
|
*/
|
||||||
class MUDClient {
|
class MUDClient {
|
||||||
//
|
//
|
||||||
// Constructor
|
// Constructor
|
||||||
@@ -14,7 +19,7 @@ class MUDClient {
|
|||||||
this.websocket = null;
|
this.websocket = null;
|
||||||
|
|
||||||
/** @type {boolean} Are we in development mode (decided by the server); */
|
/** @type {boolean} Are we in development mode (decided by the server); */
|
||||||
this.dev = false;
|
this.isDev = false;
|
||||||
|
|
||||||
this.promptOptions = {};
|
this.promptOptions = {};
|
||||||
this.shouldReply = false;
|
this.shouldReply = false;
|
||||||
@@ -176,8 +181,8 @@ class MUDClient {
|
|||||||
//
|
//
|
||||||
// The quit command has its own message type
|
// The quit command has its own message type
|
||||||
if (inputText === ":quit") {
|
if (inputText === ":quit") {
|
||||||
this.send(QUIT);
|
this.send(MessageType.QUIT);
|
||||||
this.writeToOutput("> " + inputText, { class: "input" });
|
this.writeToOutput("> " + inputText, { verbatim: true, class: "input" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,8 +198,8 @@ class MUDClient {
|
|||||||
let help = helpRegex.exec(inputText);
|
let help = helpRegex.exec(inputText);
|
||||||
if (help) {
|
if (help) {
|
||||||
console.log("here");
|
console.log("here");
|
||||||
help[1] ? this.send(HELP, help[1].trim()) : this.send(HELP);
|
help[1] ? this.send(MshType.HELP, help[1].trim()) : this.send(MshType.HELP);
|
||||||
this.writeToOutput("> " + inputText, { class: "input" });
|
this.writeToOutput("> " + inputText, { verbatim: true, class: "input" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,19 +209,14 @@ class MUDClient {
|
|||||||
// _ | (_| (_) | | | | | | | | | | | (_| | | | | (_| |
|
// _ | (_| (_) | | | | | | | | | | | (_| | | | | (_| |
|
||||||
// (_) \___\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_|
|
// (_) \___\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_|
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
let colonCommand = colonRegex.exec(inputText);
|
const colon = colonRegex.exec(inputText);
|
||||||
if (colonCommand) {
|
if (colon) {
|
||||||
this.send(COLON, colonCommand[1], colonCommand[2]);
|
const args = typeof colon[2] === "string" ? parseArgs(colon[2]) : [];
|
||||||
this.writeToOutput("> " + inputText, { class: "input" });
|
this.send(MessageType.COLON, colon[1], args);
|
||||||
|
this.writeToOutput("> " + inputText, { verbatim: true, class: "input colon" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// The server doesn't want any input from us, so we just ignore this input
|
|
||||||
if (!this.shouldReply) {
|
|
||||||
// the server is not ready for data!
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// _
|
// _
|
||||||
// _ __ ___ _ __ | |_ _
|
// _ __ ___ _ __ | |_ _
|
||||||
// | '__/ _ \ '_ \| | | | |
|
// | '__/ _ \ '_ \| | | | |
|
||||||
@@ -227,6 +227,12 @@ class MUDClient {
|
|||||||
// We handle replies below
|
// We handle replies below
|
||||||
//-------------------------
|
//-------------------------
|
||||||
|
|
||||||
|
//
|
||||||
|
if (!this.shouldReply) {
|
||||||
|
// the server is not ready for data!
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// The server wants a password, let's hash it before sending it.
|
// The server wants a password, let's hash it before sending it.
|
||||||
if (this.promptOptions.password) {
|
if (this.promptOptions.password) {
|
||||||
inputText = await this.hashPassword(inputText);
|
inputText = await this.hashPassword(inputText);
|
||||||
@@ -238,14 +244,14 @@ class MUDClient {
|
|||||||
this.username = inputText;
|
this.username = inputText;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.send(REPLY, inputText);
|
this.send(MessageType.REPLY, inputText);
|
||||||
this.shouldReply = false;
|
this.shouldReply = false;
|
||||||
this.promptOptions = {};
|
this.promptOptions = {};
|
||||||
|
|
||||||
//
|
//
|
||||||
// We add our own command to the output stream so the
|
// We add our own command to the output stream so the
|
||||||
// player can see what they typed.
|
// player can see what they typed.
|
||||||
this.writeToOutput("> " + inputText, { class: "input" });
|
this.writeToOutput("> " + inputText, { verbatim: true, class: "input" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +263,7 @@ class MUDClient {
|
|||||||
//
|
//
|
||||||
/** @param {any[]} data*/
|
/** @param {any[]} data*/
|
||||||
onMessageReceived(data) {
|
onMessageReceived(data) {
|
||||||
if (this.dev) {
|
if (this.isDev) {
|
||||||
console.debug(data);
|
console.debug(data);
|
||||||
}
|
}
|
||||||
const messageType = data.shift();
|
const messageType = data.shift();
|
||||||
@@ -291,7 +297,7 @@ class MUDClient {
|
|||||||
return this.handleDebugMessages(data);
|
return this.handleDebugMessages(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.dev) {
|
if (this.isDev) {
|
||||||
this.writeToOutput(`unknown message type: ${messageType}: ${JSON.stringify(data)}`, {
|
this.writeToOutput(`unknown message type: ${messageType}: ${JSON.stringify(data)}`, {
|
||||||
class: "debug",
|
class: "debug",
|
||||||
verbatim: true,
|
verbatim: true,
|
||||||
@@ -314,7 +320,7 @@ class MUDClient {
|
|||||||
// Debug messages let the server send data to be displayed on the player's screen
|
// Debug messages let the server send data to be displayed on the player's screen
|
||||||
// and also logged to the players browser's log.
|
// and also logged to the players browser's log.
|
||||||
handleDebugMessages(data) {
|
handleDebugMessages(data) {
|
||||||
if (!this.dev) {
|
if (!this.isDev) {
|
||||||
return; // debug messages are thrown away if we're not in dev mode.
|
return; // debug messages are thrown away if we're not in dev mode.
|
||||||
}
|
}
|
||||||
this.writeToOutput(data, { class: "debug", verbatim: true });
|
this.writeToOutput(data, { class: "debug", verbatim: true });
|
||||||
@@ -332,16 +338,16 @@ class MUDClient {
|
|||||||
console.debug("Incoming system message", data);
|
console.debug("Incoming system message", data);
|
||||||
|
|
||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
const messageType = data.shift();
|
const systemMessageType = data.shift();
|
||||||
|
|
||||||
switch (messageType) {
|
switch (systemMessageType) {
|
||||||
case "username":
|
case "username":
|
||||||
this.username = data[0];
|
this.username = data[0];
|
||||||
break;
|
break;
|
||||||
case "dev":
|
case "dev":
|
||||||
// This is a message that tells us that the server is in
|
// This is a message that tells us that the server is in
|
||||||
// "dev" mode, and that we should do the same.
|
// "dev" mode, and that we should do the same.
|
||||||
this.dev = data[0];
|
this.isDev = !!data[0];
|
||||||
this.status.textContent = "[DEV] " + this.status.textContent;
|
this.status.textContent = "[DEV] " + this.status.textContent;
|
||||||
break;
|
break;
|
||||||
case "salt":
|
case "salt":
|
||||||
@@ -349,12 +355,12 @@ class MUDClient {
|
|||||||
console.debug("updating crypto salt", data[0]);
|
console.debug("updating crypto salt", data[0]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.debug("unknown system message", messageType, data);
|
console.debug("unknown system message", systemMessageType, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're in dev mode, we should output all system messages (in a shaded/faint fashion).
|
// If we're in dev mode, we should output all system messages (in a shaded/faint fashion).
|
||||||
if (this.dev) {
|
if (this.isDev) {
|
||||||
this.writeToOutput(`system message: ${messageType} = ${JSON.stringify(data)}`, { class: "debug" });
|
this.writeToOutput(`system message: ${systemMessageType} = ${JSON.stringify(data)}`, { class: "debug" });
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -436,7 +442,7 @@ class MUDClient {
|
|||||||
* @param {string} className
|
* @param {string} className
|
||||||
*/
|
*/
|
||||||
updateStatus(message, className) {
|
updateStatus(message, className) {
|
||||||
this.status.textContent = this.dev ? `[DEV] Status: ${message}` : `Status: ${message}`;
|
this.status.textContent = this.isDev ? `[DEV] Status: ${message}` : `Status: ${message}`;
|
||||||
this.status.className = className;
|
this.status.className = className;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
server/public/favicon-16x16.png
Normal file
BIN
server/public/favicon-16x16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 783 B |
BIN
server/public/favicon-32x32.png
Normal file
BIN
server/public/favicon-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
BIN
server/public/favicon.ico
Normal file
BIN
server/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
1
server/public/messages.js
Symbolic link
1
server/public/messages.js
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../utils/messages.js
|
||||||
1
server/public/mustbe.js
Symbolic link
1
server/public/mustbe.js
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../utils/mustbe.js
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import { mustBeString } from "./mustbe.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a command string into arguments. For use with colon-commands.
|
* Parse a command string into arguments. For use with colon-commands.
|
||||||
*
|
*
|
||||||
@@ -7,7 +5,9 @@ import { mustBeString } from "./mustbe.js";
|
|||||||
* @returns {(string|number)[]} Command arguments
|
* @returns {(string|number)[]} Command arguments
|
||||||
*/
|
*/
|
||||||
export function parseArgs(cmdString) {
|
export function parseArgs(cmdString) {
|
||||||
mustBeString(cmdString);
|
if (typeof cmdString === "string") {
|
||||||
|
throw new Error("Expected string. GoT a finger in the eye instead");
|
||||||
|
}
|
||||||
const args = [];
|
const args = [];
|
||||||
const quoteChars = ["'", '"', "`"];
|
const quoteChars = ["'", '"', "`"];
|
||||||
const backslash = "\\";
|
const backslash = "\\";
|
||||||
@@ -16,7 +16,8 @@ export function parseArgs(cmdString) {
|
|||||||
let inQuotes = false; // are we inside quotes of some kind?
|
let inQuotes = false; // are we inside quotes of some kind?
|
||||||
let currentQuoteChar = ""; // if were in quotes, which are they?
|
let currentQuoteChar = ""; // if were in quotes, which are they?
|
||||||
|
|
||||||
const push = (value) => {
|
// helper function
|
||||||
|
const pushVal = (value) => {
|
||||||
const n = Number(value);
|
const n = Number(value);
|
||||||
if (Number.isSafeInteger(n)) {
|
if (Number.isSafeInteger(n)) {
|
||||||
args.push(n);
|
args.push(n);
|
||||||
@@ -39,7 +40,7 @@ export function parseArgs(cmdString) {
|
|||||||
} else if (char === " " || char === "\t") {
|
} else if (char === " " || char === "\t") {
|
||||||
// Whitespace - end current arg if it exists
|
// Whitespace - end current arg if it exists
|
||||||
if (currentArg) {
|
if (currentArg) {
|
||||||
push(currentArg);
|
pushVal(currentArg);
|
||||||
currentArg = "";
|
currentArg = "";
|
||||||
}
|
}
|
||||||
// Skip multiple whitespace
|
// Skip multiple whitespace
|
||||||
@@ -68,7 +69,7 @@ export function parseArgs(cmdString) {
|
|||||||
|
|
||||||
// Add final argument if exists
|
// Add final argument if exists
|
||||||
if (currentArg) {
|
if (currentArg) {
|
||||||
push(currentArg);
|
pushVal(currentArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentQuoteChar) {
|
if (currentQuoteChar) {
|
||||||
@@ -79,5 +80,3 @@ export function parseArgs(cmdString) {
|
|||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(parseArgs("\"k1m er '-9 ' `anus pikke`"));
|
|
||||||
1
server/public/site.webmanifest
Normal file
1
server/public/site.webmanifest
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||||
@@ -2,10 +2,10 @@ import { PasswordPrompt } from "./passwordPrompt.js";
|
|||||||
import { Player } from "../../models/player.js";
|
import { Player } from "../../models/player.js";
|
||||||
import { Scene } from "../scene.js";
|
import { Scene } from "../scene.js";
|
||||||
import { UsernamePrompt } from "./usernamePrompt.js";
|
import { UsernamePrompt } from "./usernamePrompt.js";
|
||||||
|
import { CreateUsernamePrompt } from "../playerCreation/createUsernamePrompt.js";
|
||||||
|
|
||||||
/** @property {Session} session */
|
/** @property {Session} session */
|
||||||
export class AuthenticationScene extends Scene {
|
export class AuthenticationScene extends Scene {
|
||||||
|
|
||||||
introText = [
|
introText = [
|
||||||
"= Welcome", //
|
"= Welcome", //
|
||||||
];
|
];
|
||||||
@@ -15,13 +15,13 @@ export class AuthenticationScene extends Scene {
|
|||||||
|
|
||||||
onReady() {
|
onReady() {
|
||||||
// current prompt
|
// current prompt
|
||||||
this.doPrompt(new UsernamePrompt(this));
|
this.show(UsernamePrompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {Player} player */
|
/** @param {Player} player */
|
||||||
usernameAccepted(player) {
|
usernameAccepted(player) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.doPrompt(new PasswordPrompt(this));
|
this.show(PasswordPrompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordAccepted() {
|
passwordAccepted() {
|
||||||
@@ -34,4 +34,8 @@ export class AuthenticationScene extends Scene {
|
|||||||
this.session.setScene("new JustLoggedInScene");
|
this.session.setScene("new JustLoggedInScene");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createPlayer() {
|
||||||
|
scene.session.setScene(new PlayerCreationScene(this.scene));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Prompt } from "../prompt.js";
|
import { Prompt } from "../prompt.js";
|
||||||
import * as security from "../../utils/security.js";
|
import * as security from "../../utils/security.js";
|
||||||
import { Config } from "../../config.js";
|
import { Config } from "../../config.js";
|
||||||
import { context } from "../../utils/messages.js";
|
|
||||||
|
|
||||||
export class PasswordPrompt extends Prompt {
|
export class PasswordPrompt extends Prompt {
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import { Player } from "../../models/player.js";
|
import { Player } from "../../models/player.js";
|
||||||
import { Prompt } from "../prompt.js";
|
import { Prompt } from "../prompt.js";
|
||||||
import * as security from "../../utils/security.js";
|
import * as security from "../../utils/security.js";
|
||||||
import { context } from "../../utils/messages.js";
|
|
||||||
import { gGame } from "../../models/globals.js";
|
import { gGame } from "../../models/globals.js";
|
||||||
import { PlayerCreationScene } from "../playerCreation/playerCreationSene.js";
|
import { PlayerCreationScene } from "../playerCreation/playerCreationSene.js";
|
||||||
import { Config } from "../../config.js";
|
import { Config } from "../../config.js";
|
||||||
|
import { AuthenticationScene } from "./authenticationScene.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class
|
||||||
|
*
|
||||||
|
* @property {AuthenticationScene} scene
|
||||||
|
*/
|
||||||
export class UsernamePrompt extends Prompt {
|
export class UsernamePrompt extends Prompt {
|
||||||
//
|
//
|
||||||
promptText = [
|
promptText = [
|
||||||
@@ -24,17 +29,14 @@ export class UsernamePrompt extends Prompt {
|
|||||||
// Let the client know that we're asking for a username
|
// Let the client know that we're asking for a username
|
||||||
promptOptions = { username: true };
|
promptOptions = { username: true };
|
||||||
|
|
||||||
//
|
/** @returns {AuthenticationScene} */
|
||||||
// User entered ":create"
|
get scene() {
|
||||||
onColon_create() {
|
return this._scene;
|
||||||
// User creation scene.
|
|
||||||
this.scene.session.setScene(new PlayerCreationScene(this.scene));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// User replied to our prompt
|
// User replied to our prompt
|
||||||
onReply(text) {
|
onReply(text) {
|
||||||
|
|
||||||
//
|
//
|
||||||
// do basic syntax checks on usernames
|
// do basic syntax checks on usernames
|
||||||
if (!security.isUsernameSane(text)) {
|
if (!security.isUsernameSane(text)) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Prompt } from "../prompt.js";
|
import { Prompt } from "../prompt.js";
|
||||||
import * as security from "../../utils/security.js";
|
import * as security from "../../utils/security.js";
|
||||||
import { Config } from "../../config.js";
|
import { Config } from "../../config.js";
|
||||||
import { context } from "../../utils/messages.js";
|
|
||||||
|
|
||||||
export class CreatePasswordPrompt extends Prompt {
|
export class CreatePasswordPrompt extends Prompt {
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class PlayerCreationScene extends Scene {
|
|||||||
this.session.calamity("Server is full, no more players can be created");
|
this.session.calamity("Server is full, no more players can be created");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.doPrompt(new CreateUsernamePrompt(this));
|
this.showPrompt(new CreateUsernamePrompt(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,13 +27,13 @@ export class PlayerCreationScene extends Scene {
|
|||||||
*
|
*
|
||||||
* @param {string} username
|
* @param {string} username
|
||||||
*/
|
*/
|
||||||
onUsernameAccepted(username) {
|
usernameAccepted(username) {
|
||||||
const player = gGame.createPlayer(username);
|
const player = gGame.createPlayer(username);
|
||||||
this.player = player;
|
this.player = player;
|
||||||
|
|
||||||
this.session.sendSystemMessage("salt", player.salt);
|
this.session.sendSystemMessage("salt", player.salt);
|
||||||
this.session.sendText(`Username _*${username}*_ is available, and I've reserved it for you :)`);
|
this.session.sendText(`Username _*${username}*_ is available, and I've reserved it for you :)`);
|
||||||
this.doPrompt("new passwordprompt");
|
this.showPrompt("new passwordprompt");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,9 +42,15 @@ export class PlayerCreationScene extends Scene {
|
|||||||
*
|
*
|
||||||
* @param {string} password
|
* @param {string} password
|
||||||
*/
|
*/
|
||||||
onPasswordAccepted(password) {
|
passwordAccepted(password) {
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.session.sendText("*_Success_* ✅ You will now be asked to log in again, sorry for that ;)");
|
this.session.sendText("*_Success_* ✅ You will now be asked to log in again, sorry for that ;)");
|
||||||
this.player.setPasswordHash(security.generateHash(this.password));
|
this.player.setPasswordHash(security.generateHash(this.password));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// User entered ":create"
|
||||||
|
onColon__create() {
|
||||||
|
this.scene.createPlayer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import figlet from "figlet";
|
import figlet from "figlet";
|
||||||
import { gGame } from "../models/globals.js";
|
import { gGame } from "../models/globals.js";
|
||||||
import * as msg from "../utils/messages.js";
|
|
||||||
import { Session } from "../models/session.js";
|
import { Session } from "../models/session.js";
|
||||||
import { Scene } from "./scene.js";
|
import { Scene } from "./scene.js";
|
||||||
import { WebsocketMessage } from "../utils/messages.js";
|
import { MessageType, WebsocketMessage } from "../utils/messages.js";
|
||||||
import { mustBe, mustBeString } from "../utils/mustbe.js";
|
import { mustBe, mustBeString } from "../utils/mustbe.js";
|
||||||
|
import { sprintf } from "sprintf-js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} PromptMethods
|
* @typedef {object} PromptMethods
|
||||||
@@ -19,8 +19,13 @@ import { mustBe, mustBeString } from "../utils/mustbe.js";
|
|||||||
* - onColon(...)
|
* - onColon(...)
|
||||||
*/
|
*/
|
||||||
export class Prompt {
|
export class Prompt {
|
||||||
/** @protected @readonly @constant @type {Scene} */
|
/** @private @readonly @type {Scene} */
|
||||||
scene;
|
_scene;
|
||||||
|
|
||||||
|
/** @type {Scene} */
|
||||||
|
get scene() {
|
||||||
|
return this._scene;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Extra info about the prompt we send to the client.
|
// Extra info about the prompt we send to the client.
|
||||||
@@ -58,7 +63,7 @@ export class Prompt {
|
|||||||
if (!(scene instanceof Scene)) {
|
if (!(scene instanceof Scene)) {
|
||||||
throw new Error("Expected an instance of >>Scene<< but got " + typeof scene);
|
throw new Error("Expected an instance of >>Scene<< but got " + typeof scene);
|
||||||
}
|
}
|
||||||
this.scene = scene;
|
this._scene = scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,24 +97,42 @@ export class Prompt {
|
|||||||
* Triggered when a user types a :command that begins with a colon
|
* Triggered when a user types a :command that begins with a colon
|
||||||
*
|
*
|
||||||
* @param {string} command
|
* @param {string} command
|
||||||
* @param {string} argLine
|
* @param {any[]} args
|
||||||
*/
|
*/
|
||||||
onColon(command, argLine) {
|
|
||||||
const methodName = "onColon_" + command;
|
onColon(command, args) {
|
||||||
const method = this[methodName];
|
const methodName = "onColon__" + command;
|
||||||
if (typeof method === "function") {
|
const property = this[methodName];
|
||||||
method.call(this, argLine);
|
|
||||||
|
//
|
||||||
|
// Default: we have no handler for the Foo command,
|
||||||
|
// So let's see if daddy can handle it.
|
||||||
|
if (property === undefined) {
|
||||||
|
return this.scene.onColon(command, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// If the prompt has a method called onColon_foo() =>
|
||||||
|
if (typeof property === "function") {
|
||||||
|
property.call(this, args);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// For static "denial of commands" such as :inv ==> "you cannot access your inventory right now"
|
// If the prompt has a _string_ called onColon_foo =>
|
||||||
if (typeof method === "string") {
|
if (typeof property === "string") {
|
||||||
this.sendText(method);
|
this.sendText(property);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// :inv ==> you cannot INV right now
|
//
|
||||||
this.sendError(`You cannot ${command.toUpperCase()} right now`);
|
// We found a property that has the right name but the wrong type.
|
||||||
|
throw new Error(
|
||||||
|
[
|
||||||
|
`Logic error. Prompt has a handler for a command called ${command}`,
|
||||||
|
`but it is neither a function or a string, but a ${typeof property}`,
|
||||||
|
].join(" "),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,7 +190,7 @@ export class Prompt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} systemMessageType
|
* @param {string} systemMessageType The subtype of the system message (dev, salt, username, etc.)
|
||||||
* @param {any?} value
|
* @param {any?} value
|
||||||
*/
|
*/
|
||||||
sendSystemMessage(...args) {
|
sendSystemMessage(...args) {
|
||||||
@@ -184,13 +207,6 @@ export class Prompt {
|
|||||||
|
|
||||||
//
|
//
|
||||||
// Easter ægg
|
// Easter ægg
|
||||||
onColon_pull_out_wand = "You cannot pull out your wand right now! But thanks for trying 😘🍌🍆";
|
// Example of having a string as a colon-handler
|
||||||
|
onColon__pull_out_wand = "You cannot pull out your wand right now! But thanks for trying 😘🍌🍆";
|
||||||
//
|
|
||||||
// Easter ægg2
|
|
||||||
onColon_imperial(argLine) {
|
|
||||||
const n = Number(argLine);
|
|
||||||
|
|
||||||
this.sendText(`${n} centimeters is only ${n / 2.54} inches. This is why americans have such small wands`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import { Session } from "../models/session.js";
|
import { Session } from "../models/session.js";
|
||||||
import { Prompt } from "./prompt.js";
|
import { Prompt } from "./prompt.js";
|
||||||
const MsgContext.ERROR_INSANE_PASSWORD = "Invalid password.";
|
|
||||||
const MsgContext.ERROR_INCORRECT_PASSWOD = "Incorrect password.";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Scene - a class for showing one or more prompts in a row.
|
||||||
|
*
|
||||||
|
* Scenes are mostly there to keep track of which prompt to show,
|
||||||
|
* and to store data for subsequent prompts to access.
|
||||||
|
*
|
||||||
|
* The prompts themselves are responsible for data validation and
|
||||||
|
* interpretation.
|
||||||
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
* @method onReady
|
|
||||||
*/
|
*/
|
||||||
export class Scene {
|
export class Scene {
|
||||||
/**
|
/**
|
||||||
@@ -31,8 +36,7 @@ export class Scene {
|
|||||||
return this._prompt;
|
return this._prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {}
|
||||||
}
|
|
||||||
|
|
||||||
/** @param {Session} session */
|
/** @param {Session} session */
|
||||||
execute(session) {
|
execute(session) {
|
||||||
@@ -51,8 +55,73 @@ export class Scene {
|
|||||||
/**
|
/**
|
||||||
* @param {Prompt} prompt
|
* @param {Prompt} prompt
|
||||||
*/
|
*/
|
||||||
doPrompt(prompt) {
|
showPrompt(prompt) {
|
||||||
this._prompt = prompt;
|
this._prompt = prompt;
|
||||||
prompt.execute();
|
prompt.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {new (scene: Scene) => Prompt} promptClassReference */
|
||||||
|
show(promptClassReference) {
|
||||||
|
this.showPrompt(new promptClassReference(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when a user types a :command that begins with a colon
|
||||||
|
* and the current Prompt cannot handle that command.
|
||||||
|
*
|
||||||
|
* @param {string} command
|
||||||
|
* @param {any[]} args
|
||||||
|
*/
|
||||||
|
onColon(command, args) {
|
||||||
|
const propertyName = "onColon__" + command;
|
||||||
|
const property = this[propertyName];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Default: we have no handler for the Foo command
|
||||||
|
if (property === undefined) {
|
||||||
|
this.session.sendError(`You cannot ${command.toUpperCase()} right now`, { verbatim: true }); // :foo ==> you cannot FOO right now
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// If the prompt has a method called onColon_foo() =>
|
||||||
|
if (typeof property === "function") {
|
||||||
|
property.call(this, args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// If the prompt has a _string_ called onColon_foo =>
|
||||||
|
if (typeof property === "string") {
|
||||||
|
this.session.sendText(property);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// We found a property that has the right name but the wrong type.
|
||||||
|
throw new Error(
|
||||||
|
[
|
||||||
|
`Logic error. Scene has a handler for a command called ${command}`,
|
||||||
|
`but it is neither a function or a string, but a ${typeof property}`,
|
||||||
|
].join(" "),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Easter ægg
|
||||||
|
// Example dynamic colon handler
|
||||||
|
/** @param {any[]} args */
|
||||||
|
onColon__imperial(args) {
|
||||||
|
if (args.length === 0) {
|
||||||
|
this.session.sendText("The imperial system is the freeest system ever. Also the least good");
|
||||||
|
}
|
||||||
|
|
||||||
|
const n = Number(args[0]);
|
||||||
|
|
||||||
|
this.session.sendText(
|
||||||
|
sprintf("%.2f centimeters is only %.2f inches. This is american wands are so short!", n, n / 2.54),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onColon__hi = "Hoe";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import WebSocket, { WebSocketServer } from "ws";
|
|||||||
import http from "http";
|
import http from "http";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import * as msg from "./utils/messages.js";
|
|
||||||
import { Session } from "./models/session.js";
|
import { Session } from "./models/session.js";
|
||||||
import { GameSeeder } from "./seeders/gameSeeder.js";
|
import { GameSeeder } from "./seeders/gameSeeder.js";
|
||||||
import { Config } from "./config.js";
|
import { Config } from "./config.js";
|
||||||
import { gGame } from "./models/globals.js";
|
import { gGame } from "./models/globals.js";
|
||||||
import { AuthenticationScene } from "./scenes/authentication/authenticationScene.js";
|
import { AuthenticationScene } from "./scenes/authentication/authenticationScene.js";
|
||||||
|
import { MessageType, WebsocketMessage, formatMessage } from "./utils/messages.js";
|
||||||
|
|
||||||
// __ __ _ _ ____ ____
|
// __ __ _ _ ____ ____
|
||||||
// | \/ | | | | _ \ / ___| ___ _ ____ _____ _ __
|
// | \/ | | | | _ \ / ___| ___ _ ____ _____ _ __
|
||||||
@@ -32,7 +32,7 @@ class MudServer {
|
|||||||
console.info("New connection established");
|
console.info("New connection established");
|
||||||
const session = new Session(websocket, gGame);
|
const session = new Session(websocket, gGame);
|
||||||
if (Config.dev) {
|
if (Config.dev) {
|
||||||
websocket.send(msg.prepareToSend(msg.SYSTEM, "dev", true));
|
websocket.send(formatMessage(MessageType.SYSTEM, "dev", true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ____ _ ___ ____ _____
|
// ____ _ ___ ____ _____
|
||||||
@@ -56,7 +56,7 @@ class MudServer {
|
|||||||
this.onMessage(session, data);
|
this.onMessage(session, data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error, data.toString(), data);
|
console.error(error, data.toString(), data);
|
||||||
websocket.send(msg.prepareToSend(msg.CALAMITY, error));
|
websocket.send(formatMessage(MessageType.CALAMITY, error));
|
||||||
session.close();
|
session.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -159,7 +159,7 @@ class MudServer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const msgObj = new msg.WebsocketMessage(data.toString());
|
const msgObj = new WebsocketMessage(data.toString());
|
||||||
|
|
||||||
//
|
//
|
||||||
// Handle replies to prompts. The main workhorse of the game.
|
// Handle replies to prompts. The main workhorse of the game.
|
||||||
@@ -174,7 +174,7 @@ class MudServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Handle QUIT messages. When the player types :quit
|
// Handle MessageType.QUIT messages. When the player types :quit
|
||||||
if (msgObj.isQuit()) {
|
if (msgObj.isQuit()) {
|
||||||
session.scene.onQuit();
|
session.scene.onQuit();
|
||||||
session.close(0, "Closing the socket, graceful goodbye!");
|
session.close(0, "Closing the socket, graceful goodbye!");
|
||||||
@@ -184,7 +184,7 @@ class MudServer {
|
|||||||
//
|
//
|
||||||
// Handle any text that starts with ":" that isn't :help or :quit
|
// Handle any text that starts with ":" that isn't :help or :quit
|
||||||
if (msgObj.isColon()) {
|
if (msgObj.isColon()) {
|
||||||
return session.scene.prompt.onColon(msgObj.command, msgObj.argLine);
|
return session.scene.prompt.onColon(msgObj.command, msgObj.args);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,19 +1,6 @@
|
|||||||
import { mustBe, mustBeInteger, mustBeString, mustMatch } from "./mustbe.js";
|
import { mustBe, mustBeString, mustMatch } from "./mustbe.js";
|
||||||
|
|
||||||
const colonCommandRegex = /^:([a-z0-9_]+)(:?\s*(.*))?$/;
|
export const MessageType = Object.freeze({
|
||||||
|
|
||||||
/**
|
|
||||||
* Enum-like object holding placeholder tokens.
|
|
||||||
*
|
|
||||||
* @readonly
|
|
||||||
* @enum {string}
|
|
||||||
*/
|
|
||||||
export const MsgContext = Object.freeze({
|
|
||||||
PASSWORD: ":password",
|
|
||||||
USERNAME: ":username",
|
|
||||||
});
|
|
||||||
|
|
||||||
export const MsgTtype = Object.freeze({
|
|
||||||
/**
|
/**
|
||||||
* Very bad logic error. Player must quit game, refresh page, and log in again.
|
* Very bad logic error. Player must quit game, refresh page, and log in again.
|
||||||
*
|
*
|
||||||
@@ -28,7 +15,7 @@ export const MsgTtype = Object.freeze({
|
|||||||
*
|
*
|
||||||
* Server-->Client-->Player
|
* Server-->Client-->Player
|
||||||
*/
|
*/
|
||||||
MsgContext.ERROR: "E",
|
ERROR: "E",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message to be displayed.
|
* Message to be displayed.
|
||||||
@@ -42,7 +29,7 @@ export const MsgTtype = Object.freeze({
|
|||||||
*
|
*
|
||||||
* Player-->Client-->Server
|
* Player-->Client-->Server
|
||||||
*/
|
*/
|
||||||
MsgContext.REPLY: "R",
|
REPLY: "R",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Player wants to quit.
|
* Player wants to quit.
|
||||||
@@ -93,7 +80,7 @@ export const MsgTtype = Object.freeze({
|
|||||||
* Represents a message sent to/from client
|
* Represents a message sent to/from client
|
||||||
*
|
*
|
||||||
* @property {string?} command
|
* @property {string?} command
|
||||||
* @property {string?} argLine
|
* @property {any[]} args
|
||||||
*/
|
*/
|
||||||
export class WebsocketMessage {
|
export class WebsocketMessage {
|
||||||
/** @protected @type {any[]} _arr The array that contains the message data */
|
/** @protected @type {any[]} _arr The array that contains the message data */
|
||||||
@@ -136,22 +123,22 @@ export class WebsocketMessage {
|
|||||||
this.type = mustBeString(data[0]);
|
this.type = mustBeString(data[0]);
|
||||||
|
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case MsgContext.REPLY: // player ==> client ==> server
|
case MessageType.REPLY: // player ==> client ==> server
|
||||||
this.text = mustBeString(data[1]);
|
this.text = mustBeString(data[1]);
|
||||||
break;
|
break;
|
||||||
case HELP: // player ==> client ==> server
|
case MessageType.HELP: // player ==> client ==> server
|
||||||
this.text = data[1] === undefined ? "" : mustBeString(data[1]).trim();
|
this.text = data[1] === undefined ? "" : mustBeString(data[1]).trim();
|
||||||
break;
|
break;
|
||||||
case COLON: // player ==> client ==> server
|
case MessageType.COLON: // player ==> client ==> server
|
||||||
this.command = mustMatch(data[1], /^[a-z0-9_]+$/);
|
this.command = mustMatch(data[1], /^[a-z0-9_]+$/);
|
||||||
this.argLine = data[2]; // parse??
|
this.args = mustBe(data[2], "any[]");
|
||||||
break;
|
break;
|
||||||
case DEBUG: // server ==> client
|
case MessageType.DEBUG: // server ==> client
|
||||||
case MsgContext.ERROR: // server ==> client ==> player
|
case MessageType.ERROR: // server ==> client ==> player
|
||||||
case QUIT: // player ==> client ==> server
|
case MessageType.QUIT: // player ==> client ==> server
|
||||||
case SYSTEM: // client <==> server
|
case MessageType.SYSTEM: // client <==> server
|
||||||
case PROMPT: // server ==> client ==> player
|
case MessageType.PROMPT: // server ==> client ==> player
|
||||||
case TEXT: // server ==> client ==> player
|
case MessageType.TEXT: // server ==> client ==> player
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown message type: >>${typeof this.type}<<`);
|
throw new Error(`Unknown message type: >>${typeof this.type}<<`);
|
||||||
@@ -159,27 +146,27 @@ export class WebsocketMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isQuit() {
|
isQuit() {
|
||||||
return this.type === QUIT;
|
return this.type === MessageType.QUIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
isHelp() {
|
isHelp() {
|
||||||
return this.type === HELP;
|
return this.type === MessageType.HELP;
|
||||||
}
|
}
|
||||||
|
|
||||||
isColon() {
|
isColon() {
|
||||||
return this.type === COLON;
|
return this.type === MessageType.COLON;
|
||||||
}
|
}
|
||||||
|
|
||||||
isReply() {
|
isReply() {
|
||||||
return this.type === MsgContext.REPLY;
|
return this.type === MessageType.REPLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
isSysMessage() {
|
isSysMessage() {
|
||||||
return this.type === SYSTEM;
|
return this.type === MessageType.SYSTEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
isDebug() {
|
isDebug() {
|
||||||
return this.type === DEBUG;
|
return this.type === MessageType.DEBUG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,6 +176,6 @@ export class WebsocketMessage {
|
|||||||
* @param {string} messageType
|
* @param {string} messageType
|
||||||
* @param {...any} args
|
* @param {...any} args
|
||||||
*/
|
*/
|
||||||
export function prepareToSend(messageType, ...args) {
|
export function formatMessage(messageType, ...args) {
|
||||||
return JSON.stringify([messageType, ...args]);
|
return JSON.stringify([messageType, ...args]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,14 @@ export function mustBe(value, ...types) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isArray = Array.isArray(value);
|
||||||
|
|
||||||
|
if (isArray && (types.includes("any[]") || types.includes("array"))) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: only checks first element of array if it's a string.
|
// NOTE: only checks first element of array if it's a string.
|
||||||
if (types.includes("strings[]") && Array.isArray(value) && (value.length === 0 || typeof value[0] === "string")) {
|
if (isArray && types.includes("strings[]") && (value.length === 0 || typeof value[0] === "string")) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,12 +30,6 @@ export function mustBeString(value) {
|
|||||||
return mustBe(value, "string");
|
return mustBe(value, "string");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mustBeInteger(value) {
|
|
||||||
if (typeof value === "number" && Number.isSafeInteger(value)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} str
|
* @param {string} str
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ export function frameText(text, options) {
|
|||||||
let output = "";
|
let output = "";
|
||||||
|
|
||||||
//
|
//
|
||||||
// GENERATE THE MARGIN SPACE ABOVE THE FRAMED TEXT
|
// GENERATE THE MARGIN SPACE ABOVE THE FRAMED MsgType.TEXT
|
||||||
//
|
//
|
||||||
// ( we insert space characters even though )
|
// ( we insert space characters even though )
|
||||||
// ( they wouldn't normally be visible. But )
|
// ( they wouldn't normally be visible. But )
|
||||||
@@ -263,7 +263,7 @@ export function frameText(text, options) {
|
|||||||
).repeat(options.vPadding);
|
).repeat(options.vPadding);
|
||||||
|
|
||||||
//
|
//
|
||||||
// GENERATE FRAMED TEXT SEGMENT
|
// GENERATE FRAMED MsgType.TEXT SEGMENT
|
||||||
//
|
//
|
||||||
// ║ My pretty ║
|
// ║ My pretty ║
|
||||||
// ║ text here ║
|
// ║ text here ║
|
||||||
@@ -318,7 +318,7 @@ export function frameText(text, options) {
|
|||||||
"\n";
|
"\n";
|
||||||
|
|
||||||
//
|
//
|
||||||
// GENERATE THE MARGIN SPACE BELOW THE FRAMED TEXT
|
// GENERATE THE MARGIN SPACE BELOW THE FRAMED MsgType.TEXT
|
||||||
//
|
//
|
||||||
// ( we insert space characters even though )
|
// ( we insert space characters even though )
|
||||||
// ( they wouldn't normally be visible. But )
|
// ( they wouldn't normally be visible. But )
|
||||||
|
|||||||
Reference in New Issue
Block a user