summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xclient.js210
-rw-r--r--keyboard.js31
-rw-r--r--package-lock.json21
-rw-r--r--package.json5
-rw-r--r--protocol.js9
-rwxr-xr-xserver.js3
6 files changed, 240 insertions, 39 deletions
diff --git a/client.js b/client.js
index 9493789..d4282ea 100755
--- a/client.js
+++ b/client.js
@@ -21,47 +21,193 @@ function fatalError(err) {
process.exit(1);
}
-function showMenu(title, options) {
- return new Promise(async (resolve, reject) => {
- 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) {
- resolve(num - 1);
- break;
- }
- console.log(
- "That's an invalid choice. " +
- "Please enter a number between 1 and " + options.length + ".");
+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",
+ ];
+
+ 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;
+ }
+ }
+ 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",
+ "Send room chat message",
+ "Invite a player into this room",
+ "Kick a player from the room",
+ "Leave the room",
+ ];
+
+ switch (await showMenu(title, options)) {
+ case 0: {
+ 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: {
+ break;
+ }
+
+ case 3: {
+ const name = await keyboard.prompt("Player name: ");
+ const reply = await conn.send(new protocol.Message("kick-player", name));
+ if (reply.err) {
+ console.log(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;
+ }
+ }
+}
+
const conn = new protocol.Connection(net.createConnection(+process.argv[3], process.argv[2]), messageHandler);
conn.conn.on("connect", async () => {
- console.log("Connected to Doomrooms.");
- let choice = await showMenu("Register or log in?", ["Register new account", "Log in to existing account"])
- const username = await keyboard.prompt("Username: ");
- const password = await keyboard.prompt("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);
- console.log(msg);
+ 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);
- msg = await conn.send(new protocol.Message("set-game", GAME_ID));
- if (msg.err) fatalError(msg.err);
+ 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);
- setTimeout(() => {process.exit(0);}, 2000);
+ 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);
+ }
});
diff --git a/keyboard.js b/keyboard.js
index e1101d8..1cd14dd 100644
--- a/keyboard.js
+++ b/keyboard.js
@@ -1,3 +1,5 @@
+const termios = require("node-termios");
+
module.exports = {};
let inputBuffer = "";
@@ -58,12 +60,41 @@ function getline() {
}
module.exports.getline = getline;
+function getpassword() {
+ return new Promise(async (resolve, reject) => {
+ const origtty = new termios.Termios(0);
+
+ const tty = new termios.Termios(0);
+ tty.c_lflag &= ~(termios.native.ALL_SYMBOLS.ECHO | termios.native.ALL_SYMBOLS.ECHONL);
+ tty.writeTo(0);
+
+ let handler;
+
+ try {
+ const line = await getline();
+ handler = () => resolve(line);
+ } catch (e) {
+ handler = () => reject(e);
+ }
+ console.log(); // ~ECHO eats newline
+ origtty.writeTo(0);
+ handler();
+ });
+}
+module.exports.getpassword = getpassword;
+
async function prompt(str) {
process.stdout.write(str);
return await getline();
}
module.exports.prompt = prompt;
+async function promptPassword(str) {
+ process.stdout.write(str);
+ return await getpassword();
+}
+module.exports.promptPassword = promptPassword;
+
function eof() {
return haveEOF;
}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..fb2a937
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,21 @@
+{
+ "name": "doomrooms-ttt",
+ "version": "0.1.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "nan": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.9.2.tgz",
+ "integrity": "sha512-ltW65co7f3PQWBDbqVvaU1WtFJUsNW7sWWm4HINhbMQIyVyzIeyZ8toX5TC5eeooE6piZoaEh4cZkueSKG3KYw=="
+ },
+ "node-termios": {
+ "version": "0.0.12",
+ "resolved": "https://registry.npmjs.org/node-termios/-/node-termios-0.0.12.tgz",
+ "integrity": "sha1-CU93zsG+tDnTt8JmEtp7dUpSdAU=",
+ "requires": {
+ "nan": "2.9.2"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index c13ac27..faf3b08 100644
--- a/package.json
+++ b/package.json
@@ -7,5 +7,8 @@
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
- "license": "MIT"
+ "license": "MIT",
+ "dependencies": {
+ "node-termios": "0.0.12"
+ }
}
diff --git a/protocol.js b/protocol.js
index a1be542..22baf03 100644
--- a/protocol.js
+++ b/protocol.js
@@ -68,6 +68,7 @@ class Connection {
constructor(conn, msgCb) {
this.conn = conn;
lineReader(conn, (line) => {
+ console.log("Received line '" + line + "'");
let obj;
try {
obj = JSON.parse(line);
@@ -75,7 +76,9 @@ class Connection {
if (module.exports.debug) console.log("Invalid JSON received: " + line);
return;
}
- if (obj.res || obj.err) {
+ if (obj.method) {
+ msgCb(new Message(obj.method, ...obj.args));
+ } else {
const msg = new Message(obj.id, obj.res, obj.err);
if (this.replyHandlers[msg.id]) {
this.replyHandlers[msg.id](msg);
@@ -83,8 +86,6 @@ class Connection {
} else {
if (module.exports.debug) console.log("Reply for unexpected message id " + msg.id);
}
- } else {
- msgCb(new Message(obj.method, ...obj.args));
}
});
@@ -94,7 +95,7 @@ class Connection {
this.pingInterval = setInterval(async () => {
const msg = await this.send(new Message("ping"));
if (module.exports.debug) console.log("Ping reply: " + util.inspect(msg));
- }, 30000);
+ }, 600000);
}
send(msg) {
diff --git a/server.js b/server.js
index 996bb92..618e033 100755
--- a/server.js
+++ b/server.js
@@ -14,7 +14,7 @@ const GAME_ID = "tictactoe-tom";
const GAME_NAME = "Tic Tac Toe";
function messageHandler(msg) {
- console.log("Message: " + util.inspect(msg));
+ console.log("Message:", msg);
}
async function registerGame(conn) {
@@ -26,7 +26,6 @@ async function registerGame(conn) {
process.exit(1);
}
}
- console.log(msg);
}
const conn = new protocol.Connection(net.createConnection(+process.argv[3], process.argv[2]), messageHandler);