#!/usr/bin/env node const net = require("net"); const util = require("util"); const protocol = require("./protocol.js"); const keyboard = require("./keyboard.js"); if (process.argv.length != 4) { console.log("Usage: ./client.js "); console.log("Pass address of doomrooms server as arguments."); process.exit(1); } const GAME_ID = "tictactoe-tom"; keyboard.init(); function fatalError(err) { console.log("A fatal error occurred:"); console.log(" ", err); process.exit(1); } function playerToString(player) { return player.nick; } function roomToString(room) { let res = ` '${room.name}' (${room.id})`; if (room.hidden) res += " (HIDDEN)"; res += "\n"; res += " Players:"; for (const player of room.players) { if (player.nick == room.admin) res += "\x1B[4m"; res += " " + playerToString(player); if (player.nick == room.admin) res += "\x1B[0m"; } return res; } async function showMenu(title, options) { console.log(title); for (let i = 0; i < options.length; i++) { console.log(" " + (i + 1) + ") " + options[i]); } while (true) { const line = await keyboard.prompt("> "); const num = parseInt(line.trim(), 10); if (num >= 1 && num <= options.length) { return num - 1; break; } console.log( "That's an invalid choice. " + "Please enter a number between 1 and " + options.length + "."); } } function messageHandler(msg) { console.log("ATTENTION: UNHANDLED INCOMING MESSAGE"); console.log("Message: " + util.inspect(msg)); } let currentRoom = null; let currentGame = null; // Returns: 0 for re-loop, 1 for quit, 2 for roomMenu async function mainMenu() { const title = "Main Menu"; const options = [ "Create a room", "Join an existing room", "Search for rooms", "Quit", ]; switch (await showMenu(title, options)) { case 0: { const roomName = await keyboard.prompt("Room name to make: "); const shown = await showMenu("Should the room be shown in searches?", ["Yes", "No"]) == 0; const reply = await conn.send(new protocol.Message("make-room", roomName, !shown, {})); if (reply.err) { console.log("Error: " + reply.err); } else { currentRoom = reply.res; return 2; } break; } case 1: { const roomName = await keyboard.prompt("Room id to join: "); const reply = await conn.send(new protocol.Message("join-room", roomName)); if (reply.err) { console.log("Error: " + reply.err); } else { currentRoom = reply.res; return 2; } break; } case 2: { const query = await keyboard.prompt("String to search for: "); const reply = await conn.send(new protocol.Message("search-rooms", query)); if (reply.err) { console.log("Error: " + reply.err); } else { console.log(reply.res.length + " results:"); for (const room of reply.res) { console.log(roomToString(room)); } } break; } case 3: return 1; } return 0; } // Returns: 0 for re-loop, 1 for quit, 2 for mainMenu, 3 for gameMenu async function roomMenu() { const title = "Room " + currentRoom.name + " (" + currentRoom.players.map(p => p.nick).join(", ") + ")"; const options = [ "Start a game (not fully implemented)", "Send room chat message", "Invite a player into this room", "Kick a player from the room", "Leave the room", "Quit", ]; switch (await showMenu(title, options)) { case 0: { const reply = await conn.send(new protocol.Message("start")); break; } case 1: { const line = await keyboard.prompt("Message to send: "); const reply = await conn.send(new protocol.Message("send-room-chat", line)); if (reply.err) fatalError(reply.err); break; } case 2: { const name = await keyboard.prompt("Player name to invite: "); const reply = await conn.send(new protocol.Message("invite-player", name)); if (reply.err) { console.log(reply.err); } else { console.log("Player successfully invited."); } break; } case 3: { const name = await keyboard.prompt("Player name to kick: "); const reason = await keyboard.prompt("Reason for kicking (optional): "); const reply = await conn.send(new protocol.Message("kick-player", name, reason)); console.log(reply); if (reply.err) { console.log(reply.err); } else { console.log("Player successfully kicked."); } break; } case 4: { const reply = await conn.send(new protocol.Message("leave-room")); if (reply.err) fatalError(reply.err); return 2; } case 5: return 1; } } const conn = new protocol.Connection(net.createConnection(+process.argv[3], process.argv[2]), messageHandler); conn.conn.on("connect", async () => { try { console.log("Connected to Doomrooms."); let choice = await showMenu("Register or log in?", [ "Register new account", "Log in to existing account", "Quit", ]); if (choice == 2) process.exit(0); const username = await keyboard.prompt("Username: "); const password = await keyboard.promptPassword("Password: "); let msg; if (choice == 0) { msg = await conn.send(new protocol.Message("make-player", username, password)); } else { msg = await conn.send(new protocol.Message("login", username, password)); } if (msg.err) fatalError(msg.err); msg = await conn.send(new protocol.Message("set-game", GAME_ID)); if (msg.err) fatalError(msg.err); let inGame = false; let doQuit = false; while (!doQuit) { if (inGame) { switch (await roomMenu()) { case 0: break; case 1: doQuit = true; break; case 2: inGame = false; break; } } else { switch (await mainMenu()) { case 0: break; case 1: doQuit = true; break; case 2: inGame = true; break; } } } } catch(e) { console.log(e); } conn.end(); process.exit(0); });