diff --git a/static/css/style.css b/static/css/style.css index 2e4884f..64abaf9 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -4,3 +4,22 @@ top: 0; padding: 12px; } + +#ready { + position: fixed; + bottom: 20px; + width: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +#ready-button { + font-size: 2em; + color: red; +} + +#ready-button.active { + font-size: 2em; + color: green; +} diff --git a/static/js/dom.js b/static/js/dom.js index 9d007ab..c9edde8 100644 --- a/static/js/dom.js +++ b/static/js/dom.js @@ -15,3 +15,24 @@ function updatePlayerDom() { } } } + +document.addEventListener("DOMContentLoaded", () => { + document.querySelector("#ready-button").addEventListener("click", async (ev) => { + let nowReady = ev.target.textContent === "Not ready"; + us.ready = nowReady; + + ev.target.classList.toggle("active"); + ev.target.textContent = nowReady ? "Ready" : "Not ready"; + + socket.emit("message", { + type: "SYNC", + author: ID, + ready: nowReady, + name: "", + }); + + if (allPlayersReady()) { + await startPregame(); + } + }); +}); diff --git a/static/js/index.js b/static/js/index.js index 2dbb86b..8a4e2bb 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -3,6 +3,7 @@ const ID = window.crypto.randomUUID(); const TIMEOUT = 30_000; let players = {}; +let us = null; const WAITING = 0; const PRE_GAME = 1; @@ -38,6 +39,7 @@ document.addEventListener("DOMContentLoaded", () => { }); // Create self players[ID] = new Player(ID, name); + us = players[ID]; }); socket.on("message", async (data) => { @@ -64,7 +66,7 @@ document.addEventListener("DOMContentLoaded", () => { break; case "RANDOM": - random.processCooperativeRandom(data); + await random.processCooperativeRandom(data); break; } }); @@ -131,16 +133,7 @@ async function sync(data) { players[data.author].ready = data.ready; if (allPlayersReady()) { - game_state = PRE_GAME; - // Decide turn order. "Master" begins a cooperative rng process. - if (ID === Array.min(...Object.keys(players))) { - random.coopRandom(Object.keys(players).length, "first-player"); - } - - // Wait for value to populate - let player1 = await random.get("first-player"); - - console.log(player1); + await startPregame(); } } @@ -153,3 +146,13 @@ function allPlayersReady() { return true; } + +async function startPregame() { + console.log("All players ready."); + + game_state = PRE_GAME; + + let player1 = await random.get(Object.keys(players).length, "first-player"); + + console.log(player1); +} diff --git a/static/js/random.js b/static/js/random.js index 6b6fd10..5312342 100644 --- a/static/js/random.js +++ b/static/js/random.js @@ -5,7 +5,7 @@ class RandomSession { this.cipherKeys = {}; this.ourKey = CryptoJS.lib.WordArray.random(32).toString(); // 32-bit as JavaScript does funny stuff at 64-bit levels. - this.ourNoise = CryptoJS.lib.WordArray.random(4).toString(); + this.ourNoise = CryptoJS.lib.WordArray.random(4); this.finalValue = null; this.resolvers = []; } @@ -17,33 +17,37 @@ class RandomSession { class Random { constructor() { - this.locked = false; this.sessions = {}; } - get(sessionId) { - // Spin until lock frees. - while (this.locked); - - if (this.sessions[sessionId].finalValue === null) { - let session = this.sessions[sessionId]; - let resolver; - let promise = new Promise((resolve) => { - resolver = resolve; - }); - session.resolvers.push(resolver); - return promise; - } else { - return new Promise((resolve) => { - resolve(this.sessions[sessionId].finalValue); - }); + async get(n, sessionId) { + if (this.sessions[sessionId] === undefined) { + this.initialiseSession(n, sessionId); } + + let promise; + await navigator.locks.request(`random-${sessionId}`, () => { + console.log("in lock now"); + if (this.sessions[sessionId].finalValue === null) { + let session = this.sessions[sessionId]; + let resolver; + promise = new Promise((resolve) => { + resolver = resolve; + }); + session.resolvers.push(resolver); + } else { + promise = new Promise((resolve) => { + resolve(this.sessions[sessionId].finalValue); + }); + } + }); + return promise; } /** * Start a cooperative random session. */ - coopRandom(n, sessionId) { + initialiseSession(n, sessionId) { let session = new RandomSession(n); if (sessionId === undefined) { sessionId = window.crypto.randomUUID(); @@ -60,7 +64,7 @@ class Random { cipherText: session.cipherText(), }); - return sessionId; + return session; } /** @@ -68,28 +72,13 @@ class Random { * * @param data Packet received */ - processCooperativeRandom(data) { + async processCooperativeRandom(data) { // Step 0: extract relevant information from data let session = this.sessions[data.session]; const stage = data.stage; if (session === undefined) { - // Step 1: generate and encrypt our random value. 8 bytes = 64 bit integer - const noise = CryptoJS.lib.WordArray.random(8).toString(); - console.log(`our noise: ${noise}`); - - session = new RandomSession(data.range); - this.sessions[data.session] = session; - - // Step 2: send our random value and wait for all responses - socket.emit("message", { - type: "RANDOM", - author: ID, - session: data.session, - stage: "CIPHERTEXT", - range: data.range, - cipherText: session.cipherText(), - }); + session = this.initialiseSession(data.range, data.session); } if (stage === "CIPHERTEXT") { @@ -117,22 +106,22 @@ class Random { Object.keys(players).length - 1 ) { // Lock out wait calls as they may resolve to never-ending promises. - this.locked = true; + await navigator.locks.request(`random-${data.session}`, () => { + let total = parseInt(session.ourNoise, 16); - let total = 0; + for (let participant of Object.keys(session.cipherKeys)) { + let decrypted = CryptoJS.AES.decrypt( + session.cipherTexts[participant], + session.cipherKeys[participant] + ).toString(); - for (let participant of Object.keys(session.cipherKeys)) { - total += CryptoJS.AES.decrypt( - session.cipherTexts[participant], - session.cipherKeys[participant] - ); - } + total += parseInt(decrypted, 16); + } - session.finalValue = total % session.range; + session.finalValue = total % session.range; - this.resolve(data.session); - - this.locked = false; + this.resolve(data.session); + }); } } } diff --git a/static/js/shim.js b/static/js/shim.js new file mode 100644 index 0000000..5041cd2 --- /dev/null +++ b/static/js/shim.js @@ -0,0 +1,3 @@ +Array.prototype.min = function () { + return this.reduce((min, val) => (min < val ? min : val)); +}; diff --git a/templates/index.html b/templates/index.html index 1709b74..13dbb53 100644 --- a/templates/index.html +++ b/templates/index.html @@ -7,6 +7,7 @@ + @@ -20,5 +21,9 @@ +
+ +
+