UNPKG

@hp4k1h5/terminordle

Version:

> multiplayer [wordle](https://www.powerlanguage.co.uk/wordle/) clone in your terminal

262 lines (261 loc) 8.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.endSession = exports.again = exports.guess = exports.join = exports.createSession = exports.remove = exports.Session = exports.sessions = void 0; const structs_1 = require("../../lib/structs"); const __1 = require("../../"); const args_1 = require("../../cli/args"); const msg_1 = require("./msg"); const util_1 = require("../../util"); exports.sessions = {}; const MAX_GUESSES = 20; class Session { constructor(session_id) { this.session_id = session_id; this.guests = []; this.answer = 'ibudi'; this.guesses = []; this.reset_lock = false; } } exports.Session = Session; function remove(ws, log) { if (!ws.session_id) return; const userSession = exports.sessions[ws.session_id]; if (!userSession || !userSession.guests) return; const userIndex = userSession.guests.findIndex(guest => guest.id === ws.user_id); if (userIndex === -1) return; userSession.guests.splice(userIndex, 1); log && log.log({ removing: ws.user_id, from: ws.session_id, clients: args_1.wss.clients.size, }); const guests = getGuests(userSession); guests.forEach(guest => { (0, msg_1.msg)(guest, { type: structs_1.ClientMsgType.info, content: `${ws.user_id} has left the game`, }); }); // delete session if empty if (userSession && userSession.guests.length === 0) { delete exports.sessions[ws.session_id]; } // free user name if (ws.user_id && util_1.names[ws.user_id]) { util_1.names[ws.user_id] = false; } // only close open(ing) connections if (ws.readyState < 2) { ws.close(); } } exports.remove = remove; function createSession(ws, message, log) { let session_id; try { session_id = sessionId(); } catch (e) { return; } exports.sessions[session_id] = new Session(session_id); const answer = selectAnswer(); exports.sessions[session_id].answer = answer; const response = { type: structs_1.MsgType.session_id, user_id: ws.user_id, content: session_id, session_id, }; log && log.log({ session_id, answer }); join(ws, response); } exports.createSession = createSession; const wordList = Object.keys(util_1.words); function sessionId() { let id; let tries = 0; const MAX_TRIES = 10; while (!id && tries++ < MAX_TRIES) { id = [(0, util_1.getRand)(wordList), '-', (0, util_1.getRand)(wordList)].join(''); if (!exports.sessions[id]) { exports.sessions[id] = new Session(id); return id; } id = undefined; } throw 'no session available'; } const filteredWordList = wordList.filter(word => word.length === 5 && /[a-z]/i.test(word)); function selectAnswer() { return (0, util_1.getRand)(filteredWordList); } function join(ws, message) { if (!message || !message.session_id || !exports.sessions[message.session_id]) { const e = { type: structs_1.MsgType.error, content: `no such session id ${message.session_id}, ${ws.user_id}`, }; (0, msg_1.msg)(ws, e); ws.close(); throw e; } const session = exports.sessions[message.session_id]; // add guess to session session.guests.push({ id: ws.user_id }); // update ws ws.session_id = message.session_id; // send session id confirmation const response = { type: structs_1.MsgType.session_id, user_id: message.user_id, session_id: message.session_id, content: message.session_id, }; (0, msg_1.msg)(ws, response); // update other players const guests = getGuests(exports.sessions[message.session_id]); guests.forEach(guest => { (0, msg_1.msg)(guest, { type: structs_1.MsgType.info, content: `${ws.user_id} joined!` }); }); // replay guesses back to client exports.sessions[message.session_id].guesses.forEach((guess, i) => { (0, msg_1.msg)(ws, { type: structs_1.MsgType.guess, content: { guess, rem: MAX_GUESSES - (i + 1) }, }); }); } exports.join = join; function guess(ws, message) { try { (0, __1.validateResponse)(message); } catch (e) { (0, msg_1.err)(ws, e); return; } if (!message.session_id) { (0, msg_1.err)(ws, 'no session_id'); return; } const session = exports.sessions[message.session_id]; if (!session) { (0, msg_1.err)(ws, 'no such session_id'); return; } const guess = (0, __1.wordToRow)(message.content); (0, __1.evaluateGuess)(guess, (0, __1.wordToRow)(session.answer)); session.guesses.push(guess); // broadcast guess to session guests const sessionGuests = getGuests(session); const response = { type: structs_1.ClientMsgType.guess, content: { guess, rem: MAX_GUESSES - session.guesses.length }, }; const correct = (0, __1.isCorrect)(guess); // game over free lock if (correct) session.reset_lock = false; sessionGuests.forEach(guest => { // update client (0, msg_1.msg)(guest, response); // check win condition if (correct) { const winMsg = { ...message, type: structs_1.ClientMsgType.again, content: ws.user_id, user_id: ws.user_id, }; (0, msg_1.msg)(guest, winMsg); } else if (session.guesses.length >= MAX_GUESSES) { const lossMsg = { type: structs_1.ClientMsgType.again, content: `out of guesses (20) ANSWER: ${session.answer}`, }; // update client (0, msg_1.msg)(guest, lossMsg); return; } }); } exports.guess = guess; function again(cnx, message, log) { if (!message || typeof message.content !== 'string' || !cnx.session_id) { remove(cnx, log); return undefined; } if (!/^y/i.test(message.content)) { (0, msg_1.msg)(cnx, { type: structs_1.ClientMsgType.info, content: 'goodbye', }); remove(cnx, log); return; } log && log.log({ again: message.content, user_id: cnx.user_id, }); // get session const session = exports.sessions[cnx.session_id]; // another session member has reset the session if (session.reset_lock && cnx.session_id) { // replay guesses back to client session.guesses.forEach((guess, i) => { (0, msg_1.msg)(cnx, { type: structs_1.MsgType.guess, content: { guess, rem: MAX_GUESSES - (i + 1) }, }); }); return; } // first to play again resets the session session.reset_lock = true; // reset_lock is freed by win/loss condition // reset answer const answer = selectAnswer(); session.answer = answer; // reset guesses session.guesses = []; log && log.log({ session_id: cnx.session_id, answer }); // client side reset is handled client side on decision } exports.again = again; function endSession(cnx, message) { if (!cnx.session_id) return; const session = exports.sessions[cnx.session_id]; if (!session) { (0, msg_1.err)(cnx, message); // still boot the connection remove(cnx); return; } // boot all session guests from server const guests = getGuests(session); guests.forEach(guest => { remove(guest); }); } exports.endSession = endSession; function getGuests(session) { const sessionGuests = []; args_1.wss.clients.forEach((client) => { if (session.guests.find(guest => { return guest.id === client.user_id; })) { sessionGuests.push(client); } }); return sessionGuests; }