Fix up selecting first player and some errors in the number generation.
This commit is contained in:
parent
b37f273b05
commit
8afe512062
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
3
static/js/shim.js
Normal file
@ -0,0 +1,3 @@
|
||||
Array.prototype.min = function () {
|
||||
return this.reduce((min, val) => (min < val ? min : val));
|
||||
};
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user