Fix up selecting first player and some errors in the number generation.

This commit is contained in:
jude 2023-02-01 18:09:32 +00:00
parent b37f273b05
commit 8afe512062
6 changed files with 100 additions and 60 deletions

View File

@ -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;
}

View File

@ -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();
}
});
});

View File

@ -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);
}

View File

@ -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);
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;
let promise = new Promise((resolve) => {
promise = new Promise((resolve) => {
resolver = resolve;
});
session.resolvers.push(resolver);
return promise;
} else {
return new Promise((resolve) => {
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;
let total = 0;
await navigator.locks.request(`random-${data.session}`, () => {
let total = parseInt(session.ourNoise, 16);
for (let participant of Object.keys(session.cipherKeys)) {
total += CryptoJS.AES.decrypt(
let decrypted = CryptoJS.AES.decrypt(
session.cipherTexts[participant],
session.cipherKeys[participant]
);
).toString();
total += parseInt(decrypted, 16);
}
session.finalValue = total % session.range;
this.resolve(data.session);
this.locked = false;
});
}
}
}

3
static/js/shim.js Normal file
View File

@ -0,0 +1,3 @@
Array.prototype.min = function () {
return this.reduce((min, val) => (min < val ? min : val));
};

View File

@ -7,6 +7,7 @@
<script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script src="{{ url_for('static', filename='js/shim.js') }}"></script>
<script src="{{ url_for('static', filename='js/index.js') }}"></script>
<script src="{{ url_for('static', filename='js/player.js') }}"></script>
<script src="{{ url_for('static', filename='js/dom.js') }}"></script>
@ -20,5 +21,9 @@
</ul>
</div>
<div id="ready">
<button id="ready-button">Not ready</button>
</div>
</body>
</html>