RSA keygen. some other stuff
This commit is contained in:
parent
cf0c9135e1
commit
ed171bf77f
@ -1,2 +1,4 @@
|
|||||||
import { generate_keypair } from "./paillier.js";
|
import { generate_keypair } from "./paillier.js";
|
||||||
export { generate_keypair };
|
import { generate_rsa_keypair } from "./rsa.js";
|
||||||
|
|
||||||
|
export { generate_keypair, generate_rsa_keypair };
|
||||||
|
@ -12,3 +12,24 @@ export function mod_exp(a, b, n) {
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mod_inv(a, n) {
|
||||||
|
let t = 0n;
|
||||||
|
let new_t = 1n;
|
||||||
|
let r = n;
|
||||||
|
let new_r = a;
|
||||||
|
|
||||||
|
while (new_r !== 0n) {
|
||||||
|
let quotient = r / new_r;
|
||||||
|
t = new_t;
|
||||||
|
new_t = t - quotient * new_t;
|
||||||
|
r = new_r;
|
||||||
|
new_r = r - quotient * new_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t < 0) {
|
||||||
|
t = t + n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
47
static/js/modules/crypto/rsa.js
Normal file
47
static/js/modules/crypto/rsa.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { generate_prime } from "./random_primes.js";
|
||||||
|
import { mod_exp, mod_inv } from "./math.js";
|
||||||
|
|
||||||
|
let p, q, pubKey, privKey;
|
||||||
|
|
||||||
|
class PubKey {
|
||||||
|
constructor(p, q) {
|
||||||
|
this.n = p * q;
|
||||||
|
this.e = 65537;
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypt(m) {
|
||||||
|
return mod_exp(m, this.e, this.n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PrivKey {
|
||||||
|
constructor(p, q) {
|
||||||
|
this.n = p * q;
|
||||||
|
this.d = mod_inv(65537, (q - 1) * (p - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypt(c) {
|
||||||
|
return mod_exp(c, this.d, this.n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generate_rsa_keypair() {
|
||||||
|
if (window.sessionStorage.getItem("rsa_p") === null) {
|
||||||
|
p = generate_prime();
|
||||||
|
window.sessionStorage.setItem("rsa_p", p);
|
||||||
|
} else {
|
||||||
|
p = BigInt(window.sessionStorage.getItem("rsa_p"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.sessionStorage.getItem("rsa_q") === null) {
|
||||||
|
q = generate_prime();
|
||||||
|
window.sessionStorage.setItem("rsa_q", q);
|
||||||
|
} else {
|
||||||
|
q = BigInt(window.sessionStorage.getItem("rsa_q"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey = new PubKey(p, q);
|
||||||
|
privKey = new PrivKey(p, q);
|
||||||
|
|
||||||
|
return { pubKey, privKey };
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { socket, players } from "./main.js";
|
import { socket, game } from "./main.js";
|
||||||
import { Packet } from "./packet.js";
|
import { Packet } from "./packet.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +25,7 @@ export class Barrier {
|
|||||||
resolve(data) {
|
resolve(data) {
|
||||||
this.hits.add(data.author);
|
this.hits.add(data.author);
|
||||||
|
|
||||||
if (this.hits.size === Object.keys(players).length - 1) {
|
if (this.hits.size === Object.keys(game.players).length - 1) {
|
||||||
this.hits = new Set();
|
this.hits = new Set();
|
||||||
this.resolver();
|
this.resolver();
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,4 @@
|
|||||||
import {
|
import { game, socket, ID } from "./main.js";
|
||||||
gameState,
|
|
||||||
WAITING,
|
|
||||||
PRE_GAME,
|
|
||||||
PLAYING,
|
|
||||||
us,
|
|
||||||
socket,
|
|
||||||
players,
|
|
||||||
ID,
|
|
||||||
allPlayersReady,
|
|
||||||
startPregame,
|
|
||||||
} from "./main.js";
|
|
||||||
import { Region } from "./map.js";
|
import { Region } from "./map.js";
|
||||||
import { Packet } from "./packet.js";
|
import { Packet } from "./packet.js";
|
||||||
|
|
||||||
@ -31,28 +20,18 @@ export function lockMapDom() {
|
|||||||
document.querySelectorAll(".actions").forEach((e) => e.classList.add("hidden"));
|
document.querySelectorAll(".actions").forEach((e) => e.classList.add("hidden"));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateDom() {
|
|
||||||
if (gameState !== WAITING) {
|
|
||||||
document.querySelector("#ready-button").style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePlayerDom();
|
|
||||||
showRemainingReinforcements();
|
|
||||||
updateMapDom();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateMapDom() {
|
function updateMapDom() {
|
||||||
if (us.isPlaying) {
|
if (game.us.isPlaying) {
|
||||||
unlockMapDom();
|
unlockMapDom();
|
||||||
} else {
|
} else {
|
||||||
lockMapDom();
|
lockMapDom();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gameState === PRE_GAME) {
|
if (game.isPregame()) {
|
||||||
document.querySelectorAll(".fortify, .attack").forEach((e) => {
|
document.querySelectorAll(".fortify, .attack").forEach((e) => {
|
||||||
e.classList.add("hidden");
|
e.classList.add("hidden");
|
||||||
});
|
});
|
||||||
} else if (gameState === PLAYING) {
|
} else if (game.isPlaying()) {
|
||||||
document.querySelectorAll(".node button").forEach((e) => {
|
document.querySelectorAll(".node button").forEach((e) => {
|
||||||
e.classList.remove("hidden");
|
e.classList.remove("hidden");
|
||||||
});
|
});
|
||||||
@ -72,16 +51,23 @@ function updateMapDom() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener("gameStateUpdate", () => {
|
||||||
|
if (!game.isWaiting()) {
|
||||||
|
document.querySelector("#ready-button").style.display = "none";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.addEventListener("gameStateUpdate", updateMapDom);
|
||||||
|
|
||||||
function updatePlayerDom() {
|
function updatePlayerDom() {
|
||||||
let list = document.querySelector("#playerList");
|
let list = document.querySelector("#playerList");
|
||||||
list.replaceChildren();
|
list.replaceChildren();
|
||||||
|
|
||||||
for (let playerId of Object.keys(players).sort()) {
|
for (let playerId of Object.keys(game.players).sort()) {
|
||||||
let player = players[playerId];
|
let player = game.players[playerId];
|
||||||
|
|
||||||
let statusSpan = document.createElement("div");
|
let statusSpan = document.createElement("div");
|
||||||
statusSpan.classList.add("status-span");
|
statusSpan.classList.add("status-span");
|
||||||
if (gameState === WAITING) {
|
if (game.isWaiting()) {
|
||||||
if (player.ready) {
|
if (player.ready) {
|
||||||
statusSpan.textContent = "R";
|
statusSpan.textContent = "R";
|
||||||
statusSpan.classList.add("ready");
|
statusSpan.classList.add("ready");
|
||||||
@ -110,21 +96,19 @@ function updatePlayerDom() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener("addPlayer", updatePlayerDom);
|
||||||
|
document.addEventListener("removePlayer", updatePlayerDom);
|
||||||
|
document.addEventListener("updatePlayer", updatePlayerDom);
|
||||||
|
document.addEventListener("gameStateUpdate", updatePlayerDom);
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
document.querySelector("#ready-button").addEventListener("click", async (ev) => {
|
document.querySelector("#ready-button").addEventListener("click", async (ev) => {
|
||||||
let nowReady = ev.target.textContent === "Not ready";
|
let nowReady = ev.target.textContent === "Not ready";
|
||||||
us.ready = nowReady;
|
|
||||||
|
|
||||||
ev.target.classList.toggle("active");
|
ev.target.classList.toggle("active");
|
||||||
ev.target.textContent = nowReady ? "Ready" : "Not ready";
|
ev.target.textContent = nowReady ? "Ready" : "Not ready";
|
||||||
|
|
||||||
socket.emit("message", Packet.createSetReady(nowReady));
|
socket.emit("message", Packet.createSetReady(nowReady));
|
||||||
|
|
||||||
updatePlayerDom();
|
|
||||||
|
|
||||||
if (allPlayersReady()) {
|
|
||||||
await startPregame();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector("#end-turn").addEventListener("click", async (ev) => {
|
document.querySelector("#end-turn").addEventListener("click", async (ev) => {
|
||||||
@ -212,7 +196,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector("#shuffleColors").addEventListener("click", () => {
|
document.querySelector("#shuffleColors").addEventListener("click", () => {
|
||||||
Object.values(players).forEach((player) => {
|
Object.values(game.players).forEach((player) => {
|
||||||
player.resetColor();
|
player.resetColor();
|
||||||
});
|
});
|
||||||
updatePlayerDom();
|
updatePlayerDom();
|
||||||
@ -220,11 +204,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function showRemainingReinforcements() {
|
function showRemainingReinforcements() {
|
||||||
if (gameState === PRE_GAME) {
|
if (game.isPregame()) {
|
||||||
document.querySelector(
|
document.querySelector(
|
||||||
"#remaining-reinforcements"
|
"#remaining-reinforcements"
|
||||||
).innerHTML = `<span>Remaining placements: ${reinforcementsRemaining()}</span>`;
|
).innerHTML = `<span>Remaining placements: ${reinforcementsRemaining()}</span>`;
|
||||||
} else if (gameState === PLAYING) {
|
} else if (game.isPlaying()) {
|
||||||
document.querySelector(
|
document.querySelector(
|
||||||
"#remaining-reinforcements"
|
"#remaining-reinforcements"
|
||||||
).innerHTML = `<span>Remaining placements: ${
|
).innerHTML = `<span>Remaining placements: ${
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Player } from "./player";
|
import { Player } from "./player.js";
|
||||||
|
|
||||||
const WAITING = 0;
|
const WAITING = 0;
|
||||||
const PRE_GAME = 1;
|
const PRE_GAME = 1;
|
||||||
@ -25,18 +25,23 @@ export class Game {
|
|||||||
|
|
||||||
incrementState() {
|
incrementState() {
|
||||||
this.state += 1;
|
this.state += 1;
|
||||||
|
|
||||||
|
const event = new CustomEvent("gameStateUpdate", {
|
||||||
|
detail: { newState: this.state },
|
||||||
|
});
|
||||||
|
document.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPlayer() {
|
currentPlayer() {
|
||||||
return Object.values(this.players).filter((p) => p.isPlaying)[0];
|
return Object.values(this.players).filter((p) => p.isPlaying)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
addPlayer(id, name, is_us) {
|
addPlayer(id, is_us) {
|
||||||
let is_new = this.players[id] === undefined;
|
let is_new = this.players[id] === undefined;
|
||||||
|
|
||||||
if (this.isWaiting()) {
|
if (this.isWaiting()) {
|
||||||
this.players[id] = new Player(id, name, is_us);
|
this.players[id] = new Player(id, is_us);
|
||||||
if (is_us === true) {
|
if (is_us) {
|
||||||
this.us = this.players[id];
|
this.us = this.players[id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,7 +69,10 @@ export class Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setReady(id, ready) {
|
setReady(id, ready) {
|
||||||
this.players[id].readyState = ready;
|
this.players[id].ready = ready;
|
||||||
|
|
||||||
|
const event = new CustomEvent("updatePlayer");
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
|
||||||
if (this._allPlayersReady()) {
|
if (this._allPlayersReady()) {
|
||||||
this.incrementState();
|
this.incrementState();
|
||||||
@ -73,7 +81,7 @@ export class Game {
|
|||||||
|
|
||||||
_allPlayersReady() {
|
_allPlayersReady() {
|
||||||
for (let player of Object.values(this.players)) {
|
for (let player of Object.values(this.players)) {
|
||||||
if (!player.readyState) {
|
if (!player.ready) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,16 @@ import { generate_keypair } from "../crypto/main.js";
|
|||||||
import { Random } from "./random.js";
|
import { Random } from "./random.js";
|
||||||
import { Barrier } from "./barrier.js";
|
import { Barrier } from "./barrier.js";
|
||||||
import { Packet } from "./packet.js";
|
import { Packet } from "./packet.js";
|
||||||
import { updateDom } from "./dom.js";
|
import { Game } from "./game.js";
|
||||||
|
import "./dom.js";
|
||||||
|
|
||||||
export const ID = window.crypto.randomUUID();
|
export const ID = window.crypto.randomUUID();
|
||||||
export let us = null;
|
|
||||||
|
|
||||||
export const game = new Game();
|
export const game = new Game();
|
||||||
|
|
||||||
export let socket;
|
export let socket;
|
||||||
let random;
|
let random;
|
||||||
let barrier;
|
let barrier;
|
||||||
let keys;
|
const paillier = generate_keypair();
|
||||||
|
const rsa = generate_rsa_keypair();
|
||||||
|
|
||||||
// Not totally reliable but better than nothing.
|
// Not totally reliable but better than nothing.
|
||||||
window.addEventListener("beforeunload", () => {
|
window.addEventListener("beforeunload", () => {
|
||||||
@ -23,15 +22,17 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
socket = io();
|
socket = io();
|
||||||
random = new Random();
|
random = new Random();
|
||||||
barrier = new Barrier();
|
barrier = new Barrier();
|
||||||
keys = generate_keypair();
|
|
||||||
|
|
||||||
socket.on("connect", () => {
|
socket.on("connect", () => {
|
||||||
window.console.log("Connected!");
|
window.console.log("Connected!");
|
||||||
|
window.console.log(`We are: ${ID}`);
|
||||||
socket.emit("message", Packet.createAnnounce());
|
socket.emit("message", Packet.createAnnounce());
|
||||||
game.addPlayer(ID, name, true);
|
game.addPlayer(ID, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("message", async (data) => {
|
socket.on("message", async (data) => {
|
||||||
|
window.console.log(data);
|
||||||
|
|
||||||
// todo validate signature
|
// todo validate signature
|
||||||
|
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
@ -62,10 +63,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
*
|
*
|
||||||
* @param data Packet received
|
* @param data Packet received
|
||||||
*/
|
*/
|
||||||
document.addEventListener("ANNOUNCE", (data) => {
|
document.addEventListener("ANNOUNCE", (ev) => {
|
||||||
|
const data = ev.detail;
|
||||||
if (data.author === ID) return;
|
if (data.author === ID) return;
|
||||||
|
|
||||||
let is_new = game.addPlayer(data.author, data.name, false);
|
let is_new = game.addPlayer(data.author, false);
|
||||||
|
|
||||||
// When a new player is seen, all announce to ensure they know all players.
|
// When a new player is seen, all announce to ensure they know all players.
|
||||||
if (is_new) {
|
if (is_new) {
|
||||||
@ -73,7 +75,8 @@ document.addEventListener("ANNOUNCE", (data) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("DISCONNECT", (data) => {
|
document.addEventListener("DISCONNECT", (ev) => {
|
||||||
|
const data = ev.detail;
|
||||||
game.removePlayer(data.author);
|
game.removePlayer(data.author);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -82,54 +85,61 @@ document.addEventListener("DISCONNECT", (data) => {
|
|||||||
*
|
*
|
||||||
* @param data Packet received
|
* @param data Packet received
|
||||||
*/
|
*/
|
||||||
document.addEventListener("KEEPALIVE", (data) => {
|
document.addEventListener("KEEPALIVE", (ev) => {
|
||||||
|
const data = ev.detail;
|
||||||
game.keepAlive(data.author);
|
game.keepAlive(data.author);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("ACT", async (data) => {
|
document.addEventListener("ACT", async (ev) => {
|
||||||
if (data.author !== game.currentPlayer().id) {
|
const data = ev.detail;
|
||||||
if (data.action === "DEFENSE") {
|
|
||||||
await game.players[data.author].setDefense(data.amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game.isWaiting()) {
|
if (game.isWaiting()) {
|
||||||
game.setReady(data.author, data.ready);
|
game.setReady(data.author, data.ready);
|
||||||
} else if (game.isPregame()) {
|
} else {
|
||||||
if (!Region.allRegionsClaimed()) {
|
if (data.author !== game.currentPlayer().id) {
|
||||||
// Claim a region in the pregame.
|
if (data.action === "DEFENSE") {
|
||||||
if (game.currentPlayer().claim(data)) {
|
await game.players[data.author].setDefense(data.amount);
|
||||||
// Increment to next player.
|
|
||||||
game.currentPlayer().endTurn();
|
|
||||||
}
|
|
||||||
} else if (!Region.allReinforcementsPlaced()) {
|
|
||||||
if (game.currentPlayer().reinforce(data)) {
|
|
||||||
game.currentPlayer().endTurn();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Region.allReinforcementsPlaced()) {
|
if (game.isPregame()) {
|
||||||
game.incrementState();
|
if (!Region.allRegionsClaimed()) {
|
||||||
}
|
// Claim a region in the pregame.
|
||||||
} else {
|
if (game.currentPlayer().claim(data)) {
|
||||||
if (await game.currentPlayer().act(data)) {
|
// Increment to next player.
|
||||||
game.currentPlayer().endTurn();
|
game.currentPlayer().endTurn();
|
||||||
|
}
|
||||||
|
} else if (!Region.allReinforcementsPlaced()) {
|
||||||
|
if (game.currentPlayer().reinforce(data)) {
|
||||||
|
game.currentPlayer().endTurn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Region.allReinforcementsPlaced()) {
|
||||||
|
game.incrementState();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (await game.currentPlayer().act(data)) {
|
||||||
|
game.currentPlayer().endTurn();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function startPregame() {
|
document.addEventListener("gameStateUpdate", async () => {
|
||||||
gameState = PRE_GAME;
|
if (game.isPregame()) {
|
||||||
|
let firstPlayerIndex = await random.get(
|
||||||
|
Object.keys(game.players).length,
|
||||||
|
"first-player"
|
||||||
|
);
|
||||||
|
|
||||||
let firstPlayerIndex = await random.get(Object.keys(players).length, "first-player");
|
let firstPlayer = Object.values(game.players).sort((a, b) =>
|
||||||
|
a.id < b.id ? -1 : 1
|
||||||
|
)[firstPlayerIndex];
|
||||||
|
|
||||||
let firstPlayer = Object.values(players).sort((a, b) => (a.id < b.id ? -1 : 1))[
|
firstPlayer.isPlaying = true;
|
||||||
firstPlayerIndex
|
await barrier.wait();
|
||||||
];
|
}
|
||||||
|
});
|
||||||
firstPlayer.isPlaying = true;
|
|
||||||
await barrier.wait();
|
|
||||||
updateDom();
|
|
||||||
}
|
|
||||||
|
@ -12,7 +12,6 @@ export class Packet {
|
|||||||
static createAnnounce() {
|
static createAnnounce() {
|
||||||
return {
|
return {
|
||||||
...this._createBase("ANNOUNCE"),
|
...this._createBase("ANNOUNCE"),
|
||||||
name: "",
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Packet } from "./packet.js";
|
import { Packet } from "./packet.js";
|
||||||
import { socket } from "./main.js";
|
import { socket } from "./main.js";
|
||||||
import { updateDom } from "./dom.js";
|
|
||||||
|
|
||||||
// Timeout to consider a player disconnected
|
// Timeout to consider a player disconnected
|
||||||
const TIMEOUT = 30_000;
|
const TIMEOUT = 30_000;
|
||||||
@ -34,7 +33,7 @@ export class Player {
|
|||||||
// Emit keepalive messages to inform other players we are still here
|
// Emit keepalive messages to inform other players we are still here
|
||||||
window.setInterval(() => {
|
window.setInterval(() => {
|
||||||
socket.emit("message", Packet.createKeepAlive());
|
socket.emit("message", Packet.createKeepAlive());
|
||||||
}, TIMEOUT / 5);
|
}, TIMEOUT / 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { socket, ID, players } from "./main.js";
|
import { socket, ID, game } from "./main.js";
|
||||||
|
|
||||||
class RandomSession {
|
class RandomSession {
|
||||||
constructor(range) {
|
constructor(range) {
|
||||||
@ -87,7 +87,7 @@ export class Random {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
Object.keys(session.cipherTexts).length ===
|
Object.keys(session.cipherTexts).length ===
|
||||||
Object.keys(players).length - 1
|
Object.keys(game.players).length - 1
|
||||||
) {
|
) {
|
||||||
// Step 3: release our key once all players have sent a ciphertext
|
// Step 3: release our key once all players have sent a ciphertext
|
||||||
socket.emit("message", {
|
socket.emit("message", {
|
||||||
@ -104,7 +104,7 @@ export class Random {
|
|||||||
// Step 4: get final random value
|
// Step 4: get final random value
|
||||||
if (
|
if (
|
||||||
Object.keys(session.cipherKeys).length ===
|
Object.keys(session.cipherKeys).length ===
|
||||||
Object.keys(players).length - 1
|
Object.keys(game.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.
|
||||||
await navigator.locks.request(`random-${data.session}`, () => {
|
await navigator.locks.request(`random-${data.session}`, () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user