Fix up selecting first player and some errors in the number generation.
This commit is contained in:
		@@ -4,3 +4,22 @@
 | 
				
			|||||||
    top: 0;
 | 
					    top: 0;
 | 
				
			||||||
    padding: 12px;
 | 
					    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;
 | 
					const TIMEOUT = 30_000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let players = {};
 | 
					let players = {};
 | 
				
			||||||
 | 
					let us = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const WAITING = 0;
 | 
					const WAITING = 0;
 | 
				
			||||||
const PRE_GAME = 1;
 | 
					const PRE_GAME = 1;
 | 
				
			||||||
@@ -38,6 +39,7 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
        // Create self
 | 
					        // Create self
 | 
				
			||||||
        players[ID] = new Player(ID, name);
 | 
					        players[ID] = new Player(ID, name);
 | 
				
			||||||
 | 
					        us = players[ID];
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    socket.on("message", async (data) => {
 | 
					    socket.on("message", async (data) => {
 | 
				
			||||||
@@ -64,7 +66,7 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case "RANDOM":
 | 
					            case "RANDOM":
 | 
				
			||||||
                random.processCooperativeRandom(data);
 | 
					                await random.processCooperativeRandom(data);
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -131,16 +133,7 @@ async function sync(data) {
 | 
				
			|||||||
    players[data.author].ready = data.ready;
 | 
					    players[data.author].ready = data.ready;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (allPlayersReady()) {
 | 
					    if (allPlayersReady()) {
 | 
				
			||||||
        game_state = PRE_GAME;
 | 
					        await startPregame();
 | 
				
			||||||
        // 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);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -153,3 +146,13 @@ function allPlayersReady() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return true;
 | 
					    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.cipherKeys = {};
 | 
				
			||||||
        this.ourKey = CryptoJS.lib.WordArray.random(32).toString();
 | 
					        this.ourKey = CryptoJS.lib.WordArray.random(32).toString();
 | 
				
			||||||
        // 32-bit as JavaScript does funny stuff at 64-bit levels.
 | 
					        // 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.finalValue = null;
 | 
				
			||||||
        this.resolvers = [];
 | 
					        this.resolvers = [];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -17,33 +17,37 @@ class RandomSession {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Random {
 | 
					class Random {
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        this.locked = false;
 | 
					 | 
				
			||||||
        this.sessions = {};
 | 
					        this.sessions = {};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get(sessionId) {
 | 
					    async get(n, sessionId) {
 | 
				
			||||||
        // Spin until lock frees.
 | 
					        if (this.sessions[sessionId] === undefined) {
 | 
				
			||||||
        while (this.locked);
 | 
					            this.initialiseSession(n, sessionId);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        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);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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.
 | 
					     * Start a cooperative random session.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    coopRandom(n, sessionId) {
 | 
					    initialiseSession(n, sessionId) {
 | 
				
			||||||
        let session = new RandomSession(n);
 | 
					        let session = new RandomSession(n);
 | 
				
			||||||
        if (sessionId === undefined) {
 | 
					        if (sessionId === undefined) {
 | 
				
			||||||
            sessionId = window.crypto.randomUUID();
 | 
					            sessionId = window.crypto.randomUUID();
 | 
				
			||||||
@@ -60,7 +64,7 @@ class Random {
 | 
				
			|||||||
            cipherText: session.cipherText(),
 | 
					            cipherText: session.cipherText(),
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return sessionId;
 | 
					        return session;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -68,28 +72,13 @@ class Random {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param data Packet received
 | 
					     * @param data Packet received
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    processCooperativeRandom(data) {
 | 
					    async processCooperativeRandom(data) {
 | 
				
			||||||
        // Step 0: extract relevant information from data
 | 
					        // Step 0: extract relevant information from data
 | 
				
			||||||
        let session = this.sessions[data.session];
 | 
					        let session = this.sessions[data.session];
 | 
				
			||||||
        const stage = data.stage;
 | 
					        const stage = data.stage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (session === undefined) {
 | 
					        if (session === undefined) {
 | 
				
			||||||
            // Step 1: generate and encrypt our random value. 8 bytes = 64 bit integer
 | 
					            session = this.initialiseSession(data.range, data.session);
 | 
				
			||||||
            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(),
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (stage === "CIPHERTEXT") {
 | 
					        if (stage === "CIPHERTEXT") {
 | 
				
			||||||
@@ -117,22 +106,22 @@ class Random {
 | 
				
			|||||||
                Object.keys(players).length - 1
 | 
					                Object.keys(players).length - 1
 | 
				
			||||||
            ) {
 | 
					            ) {
 | 
				
			||||||
                // Lock out wait calls as they may resolve to never-ending promises.
 | 
					                // 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 += parseInt(decrypted, 16);
 | 
				
			||||||
                    total += CryptoJS.AES.decrypt(
 | 
					                    }
 | 
				
			||||||
                        session.cipherTexts[participant],
 | 
					 | 
				
			||||||
                        session.cipherKeys[participant]
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                session.finalValue = total % session.range;
 | 
					                    session.finalValue = total % session.range;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                this.resolve(data.session);
 | 
					                    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://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="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/index.js') }}"></script>
 | 
				
			||||||
        <script src="{{ url_for('static', filename='js/player.js') }}"></script>
 | 
					        <script src="{{ url_for('static', filename='js/player.js') }}"></script>
 | 
				
			||||||
        <script src="{{ url_for('static', filename='js/dom.js') }}"></script>
 | 
					        <script src="{{ url_for('static', filename='js/dom.js') }}"></script>
 | 
				
			||||||
@@ -20,5 +21,9 @@
 | 
				
			|||||||
        </ul>
 | 
					        </ul>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div id="ready">
 | 
				
			||||||
 | 
					        <button id="ready-button">Not ready</button>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </body>
 | 
					    </body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user