summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rwxr-xr-xclient.js67
-rw-r--r--keyboard.js70
-rw-r--r--[-rwxr-xr-x]protocol.js (renamed from main.js)58
-rwxr-xr-xserver.js42
5 files changed, 204 insertions, 35 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ef5f69b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.swp
+node_modules/
diff --git a/client.js b/client.js
new file mode 100755
index 0000000..9493789
--- /dev/null
+++ b/client.js
@@ -0,0 +1,67 @@
+#!/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 <hostname> <port>");
+ 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 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 messageHandler(msg) {
+ console.log("Message: " + util.inspect(msg));
+}
+
+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);
+
+ msg = await conn.send(new protocol.Message("set-game", GAME_ID));
+ if (msg.err) fatalError(msg.err);
+
+ setTimeout(() => {process.exit(0);}, 2000);
+});
diff --git a/keyboard.js b/keyboard.js
new file mode 100644
index 0000000..e1101d8
--- /dev/null
+++ b/keyboard.js
@@ -0,0 +1,70 @@
+module.exports = {};
+
+let inputBuffer = "";
+let initialised = false, haveEOF = false;
+let waitQueue = [];
+
+function stdinDataListener(data) {
+ data = data.toString();
+ inputBuffer += data;
+
+ if (waitQueue.length == 0) return;
+
+ const idx = inputBuffer.indexOf("\n");
+ if (idx == -1) return;
+
+ waitQueue.shift()[0](inputBuffer.slice(0, idx));
+ inputBuffer = inputBuffer.slice(idx + 1);
+}
+
+function stdinEndListener() {
+ haveEOF = true;
+ for (const [resolve, reject] of waitQueue) {
+ reject("End-of-file reached");
+ }
+ waitQueue = [];
+}
+
+function init() {
+ process.stdin.on("data", stdinDataListener);
+ process.stdin.on("end", stdinEndListener);
+ initialised = true;
+}
+module.exports.init = init;
+
+function end() {
+ process.stdin.removeListener("data", stdinDataListener);
+ process.stdin.removeListener("end", stdinEndListener);
+ process.stdin.end();
+}
+module.exports.end = end;
+
+function getline() {
+ return new Promise((resolve, reject) => {
+ if (haveEOF) {
+ reject("End-of-file reached");
+ return;
+ }
+
+ const idx = inputBuffer.indexOf("\n");
+ if (idx != -1) {
+ const line = inputBuffer.slice(0, idx);
+ inputBuffer = inputBuffer.slice(idx + 1);
+ resolve(line);
+ } else {
+ waitQueue.push([resolve, reject]);
+ }
+ });
+}
+module.exports.getline = getline;
+
+async function prompt(str) {
+ process.stdout.write(str);
+ return await getline();
+}
+module.exports.prompt = prompt;
+
+function eof() {
+ return haveEOF;
+}
+module.exports.eof = eof;
diff --git a/main.js b/protocol.js
index b19143a..a1be542 100755..100644
--- a/main.js
+++ b/protocol.js
@@ -1,16 +1,8 @@
-#!/usr/bin/env node
-
-const net = require("net");
const util = require("util");
-if (process.argv.length != 4) {
- console.log("Usage: ./main.js <hostname> <port>");
- console.log("Pass address of doomrooms server as arguments.");
- process.exit(1);
-}
+module.exports = {};
-const GAME_ID = "tictactoe-tom";
-const GAME_NAME = "Tic Tac Toe";
+module.exports.debug = true;
{
let id = 0;
@@ -18,6 +10,7 @@ const GAME_NAME = "Tic Tac Toe";
return id++;
}
}
+module.exports.uniqid = uniqid;
function lineReader(conn, lineCb) {
let buffer = "";
@@ -31,6 +24,7 @@ function lineReader(conn, lineCb) {
}
});
}
+module.exports.lineReader = lineReader;
class Message {
constructor(/*...*/) {
@@ -68,6 +62,7 @@ class Message {
}
}
}
+module.exports.Message = Message;
class Connection {
constructor(conn, msgCb) {
@@ -77,7 +72,7 @@ class Connection {
try {
obj = JSON.parse(line);
} catch (e) {
- console.log("Invalid JSON received: " + line);
+ if (module.exports.debug) console.log("Invalid JSON received: " + line);
return;
}
if (obj.res || obj.err) {
@@ -86,7 +81,7 @@ class Connection {
this.replyHandlers[msg.id](msg);
this.replyHandlers[msg.id] = null;
} else {
- console.log("Reply for unexpected message id " + msg.id);
+ if (module.exports.debug) console.log("Reply for unexpected message id " + msg.id);
}
} else {
msgCb(new Message(obj.method, ...obj.args));
@@ -94,33 +89,26 @@ class Connection {
});
this.replyHandlers = {};
+
+ // Send a ping once in a while
+ this.pingInterval = setInterval(async () => {
+ const msg = await this.send(new Message("ping"));
+ if (module.exports.debug) console.log("Ping reply: " + util.inspect(msg));
+ }, 30000);
}
- send(msg, replyCb) {
+ send(msg) {
const data = msg.toJSON();
- console.log("Writing to conn: " + data);
+ if (module.exports.debug) console.log("Writing to conn: " + data);
this.conn.write(data + "\n");
- if (replyCb && msg.isCommand) {
- this.replyHandlers[msg.id] = replyCb;
- }
+ return new Promise((resolve, reject) => {
+ this.replyHandlers[msg.id] = resolve;
+ });
}
-}
-function messageHandler(msg) {
- console.log("Message: " + util.inspect(msg));
+ end() {
+ clearInterval(this.pingInterval);
+ this.conn.end();
+ }
}
-
-const conn = new Connection(net.createConnection(+process.argv[3], process.argv[2]), messageHandler);
-conn.conn.on("connect", () => {
- console.log("Connected to roomserver");
-
- conn.conn.on("end", () => {
- console.log("Connection with roomserver unexpectedly closed!");
- });
-
- setInterval(() => {
- conn.send(new Message("ping"), (msg) => console.log("Ping reply: " + util.inspect(msg)));
- }, 30000);
-
- conn.send(new Message("make-game", GAME_ID, GAME_NAME), (msg) => console.log(msg));
-});
+module.exports.Connection = Connection;
diff --git a/server.js b/server.js
new file mode 100755
index 0000000..996bb92
--- /dev/null
+++ b/server.js
@@ -0,0 +1,42 @@
+#!/usr/bin/env node
+
+const net = require("net");
+const util = require("util");
+const protocol = require("./protocol.js");
+
+if (process.argv.length != 4) {
+ console.log("Usage: ./server.js <hostname> <port>");
+ console.log("Pass address of doomrooms server as arguments.");
+ process.exit(1);
+}
+
+const GAME_ID = "tictactoe-tom";
+const GAME_NAME = "Tic Tac Toe";
+
+function messageHandler(msg) {
+ console.log("Message: " + util.inspect(msg));
+}
+
+async function registerGame(conn) {
+ let msg = await conn.send(new protocol.Message("make-game", GAME_ID, GAME_NAME));
+ if (msg.err) {
+ msg = await conn.send(new protocol.Message("attach-game", GAME_ID, false));
+ if (msg.err) {
+ console.log("Game already managed by another gameserver, and not forcing");
+ process.exit(1);
+ }
+ }
+ console.log(msg);
+}
+
+const conn = new protocol.Connection(net.createConnection(+process.argv[3], process.argv[2]), messageHandler);
+conn.conn.on("connect", () => {
+ console.log("Connected to roomserver");
+
+ conn.conn.on("end", () => {
+ console.log("Connection with roomserver unexpectedly closed!");
+ process.exit(1);
+ });
+
+ registerGame(conn);
+});