Mid refactor to stop polluting the namespace so much
This commit is contained in:
		@@ -1,4 +1,4 @@
 | 
				
			|||||||
#players {
 | 
					#players-div {
 | 
				
			||||||
    position: absolute;
 | 
					    position: absolute;
 | 
				
			||||||
    right: 0;
 | 
					    right: 0;
 | 
				
			||||||
    top: 0;
 | 
					    top: 0;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,199 +0,0 @@
 | 
				
			|||||||
const ID = window.crypto.randomUUID();
 | 
					 | 
				
			||||||
// Timeout to consider a player disconnected
 | 
					 | 
				
			||||||
const TIMEOUT = 30_000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let players = {};
 | 
					 | 
				
			||||||
let us = null;
 | 
					 | 
				
			||||||
let currentPlayer = () => Object.values(players).filter((p) => p.isPlaying)[0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const WAITING = 0;
 | 
					 | 
				
			||||||
const PRE_GAME = 1;
 | 
					 | 
				
			||||||
const PLAYING = 2;
 | 
					 | 
				
			||||||
const POST_GAME = 3;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let gameState = WAITING;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let socket;
 | 
					 | 
				
			||||||
let random;
 | 
					 | 
				
			||||||
let barrier;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Not totally reliable but better than nothing.
 | 
					 | 
				
			||||||
window.addEventListener("beforeunload", () => {
 | 
					 | 
				
			||||||
    socket.emit("message", Packet.createDisconnect());
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
document.addEventListener("DOMContentLoaded", () => {
 | 
					 | 
				
			||||||
    socket = io();
 | 
					 | 
				
			||||||
    random = new Random();
 | 
					 | 
				
			||||||
    barrier = new Barrier();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    socket.on("connect", () => {
 | 
					 | 
				
			||||||
        console.log("Connected!");
 | 
					 | 
				
			||||||
        socket.emit("message", Packet.createAnnounce());
 | 
					 | 
				
			||||||
        // Create self
 | 
					 | 
				
			||||||
        players[ID] = new Player(ID, name);
 | 
					 | 
				
			||||||
        us = players[ID];
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    socket.on("message", async (data) => {
 | 
					 | 
				
			||||||
        switch (data.type) {
 | 
					 | 
				
			||||||
            case "ANNOUNCE":
 | 
					 | 
				
			||||||
                if (data.author === ID) {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                playerConnected(data);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            case "DISCONNECT":
 | 
					 | 
				
			||||||
                playerDisconnected(data);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            case "KEEPALIVE":
 | 
					 | 
				
			||||||
                if (data.author === ID) {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                keepAlive(data);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            case "READY":
 | 
					 | 
				
			||||||
                if (data.author === ID) {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                await setReady(data);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            case "RANDOM":
 | 
					 | 
				
			||||||
                if (data.author === ID) {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                await random.processCooperativeRandom(data);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            case "BARRIER":
 | 
					 | 
				
			||||||
                if (data.author === ID) {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                barrier.resolve(data);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            case "ACT":
 | 
					 | 
				
			||||||
                if (data.author !== currentPlayer().id) {
 | 
					 | 
				
			||||||
                    if (data.action === "DEFENSE") {
 | 
					 | 
				
			||||||
                        await players[data.author].setDefense(data.amount);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (gameState === PRE_GAME) {
 | 
					 | 
				
			||||||
                    if (!allRegionsClaimed()) {
 | 
					 | 
				
			||||||
                        // Claim a region in the pregame.
 | 
					 | 
				
			||||||
                        if (currentPlayer().claim(data)) {
 | 
					 | 
				
			||||||
                            // Increment to next player.
 | 
					 | 
				
			||||||
                            currentPlayer().endTurn();
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    } else if (!allReinforcementsPlaced()) {
 | 
					 | 
				
			||||||
                        if (currentPlayer().reinforce(data)) {
 | 
					 | 
				
			||||||
                            currentPlayer().endTurn();
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (allReinforcementsPlaced()) {
 | 
					 | 
				
			||||||
                        gameState = PLAYING;
 | 
					 | 
				
			||||||
                        updateDom();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    if (await currentPlayer().act(data)) {
 | 
					 | 
				
			||||||
                        currentPlayer().endTurn();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                updateDom();
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Emit keepalive messages to inform other players we are still here
 | 
					 | 
				
			||||||
    window.setInterval(() => {
 | 
					 | 
				
			||||||
        socket.emit("message", Packet.createKeepAlive());
 | 
					 | 
				
			||||||
    }, TIMEOUT / 5);
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Process player connect packets: these inform that a new player has joined.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param data Packet received
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
function playerConnected(data) {
 | 
					 | 
				
			||||||
    // Block players from joining mid-game
 | 
					 | 
				
			||||||
    if (gameState !== WAITING) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // When a new player is seen, all announce to ensure they know all players.
 | 
					 | 
				
			||||||
    if (players[data.author] === undefined) {
 | 
					 | 
				
			||||||
        players[data.author] = new Player(data.author, data.name);
 | 
					 | 
				
			||||||
        socket.emit("message", Packet.createAnnounce());
 | 
					 | 
				
			||||||
        players[data.author].resetTimeout();
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    updateDom();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function playerDisconnected(data) {
 | 
					 | 
				
			||||||
    console.log("deleting player");
 | 
					 | 
				
			||||||
    delete players[data.author];
 | 
					 | 
				
			||||||
    updateDom();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Process keep-alive packets: these are packets that check players are still online.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param data Packet received
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
function keepAlive(data) {
 | 
					 | 
				
			||||||
    players[data.author].resetTimeout();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Process sync packets: update player details like status and name.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param data Packet received
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
async function setReady(data) {
 | 
					 | 
				
			||||||
    players[data.author].name = data.name;
 | 
					 | 
				
			||||||
    players[data.author].ready = data.ready;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    updateDom();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (allPlayersReady()) {
 | 
					 | 
				
			||||||
        await startPregame();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function allPlayersReady() {
 | 
					 | 
				
			||||||
    for (let player of Object.values(players)) {
 | 
					 | 
				
			||||||
        if (!player.ready) {
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function startPregame() {
 | 
					 | 
				
			||||||
    console.log("all players ready.");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    gameState = PRE_GAME;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let firstPlayerIndex = await random.get(Object.keys(players).length, "first-player");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let firstPlayer = Object.values(players).sort((a, b) => (a.id < b.id ? -1 : 1))[
 | 
					 | 
				
			||||||
        firstPlayerIndex
 | 
					 | 
				
			||||||
    ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    firstPlayer.isPlaying = true;
 | 
					 | 
				
			||||||
    await barrier.wait();
 | 
					 | 
				
			||||||
    updateDom();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										2
									
								
								static/js/modules/crypto/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								static/js/modules/crypto/main.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					import { generate_keypair } from "./paillier.js";
 | 
				
			||||||
 | 
					export { generate_keypair };
 | 
				
			||||||
							
								
								
									
										14
									
								
								static/js/modules/crypto/math.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								static/js/modules/crypto/math.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					export function mod_exp(a, b, n) {
 | 
				
			||||||
 | 
					    let res = 1n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (b > 0n) {
 | 
				
			||||||
 | 
					        if (b % 2n === 1n) {
 | 
				
			||||||
 | 
					            res = (res * a) % n;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        b >>= 1n;
 | 
				
			||||||
 | 
					        a = (a * a) % n;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,3 +1,6 @@
 | 
				
			|||||||
 | 
					import { random2048, generate_prime } from "./random_primes.js";
 | 
				
			||||||
 | 
					import { mod_exp } from "./math.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let p, q, pubKey, privKey;
 | 
					let p, q, pubKey, privKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PubKey {
 | 
					class PubKey {
 | 
				
			||||||
@@ -18,7 +21,7 @@ class PubKey {
 | 
				
			|||||||
        // Compute g^m by binomial theorem.
 | 
					        // Compute g^m by binomial theorem.
 | 
				
			||||||
        let gm = (1n + this.n * m) % this.n ** 2n;
 | 
					        let gm = (1n + this.n * m) % this.n ** 2n;
 | 
				
			||||||
        // Compute g^m r^n from crt
 | 
					        // Compute g^m r^n from crt
 | 
				
			||||||
        return (gm * fastModularExponentiation(r, this.n, this.n ** 2n)) % this.n ** 2n;
 | 
					        return (gm * mod_exp(r, this.n, this.n ** 2n)) % this.n ** 2n;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,19 +29,17 @@ class PrivKey {
 | 
				
			|||||||
    constructor(p, q) {
 | 
					    constructor(p, q) {
 | 
				
			||||||
        this.n = p * q;
 | 
					        this.n = p * q;
 | 
				
			||||||
        this.lambda = (p - 1n) * (q - 1n);
 | 
					        this.lambda = (p - 1n) * (q - 1n);
 | 
				
			||||||
        this.mu = fastModularExponentiation(this.lambda, this.lambda - 1n, this.n);
 | 
					        this.mu = mod_exp(this.lambda, this.lambda - 1n, this.n);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    decrypt(c) {
 | 
					    decrypt(c) {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            (((fastModularExponentiation(c, this.lambda, this.n ** 2n) - 1n) / this.n) *
 | 
					            (((mod_exp(c, this.lambda, this.n ** 2n) - 1n) / this.n) * this.mu) % this.n
 | 
				
			||||||
                this.mu) %
 | 
					 | 
				
			||||||
            this.n
 | 
					 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
document.addEventListener("DOMContentLoaded", () => {
 | 
					export function generate_keypair() {
 | 
				
			||||||
    if (window.sessionStorage.getItem("p") === null) {
 | 
					    if (window.sessionStorage.getItem("p") === null) {
 | 
				
			||||||
        p = generate_prime();
 | 
					        p = generate_prime();
 | 
				
			||||||
        window.sessionStorage.setItem("p", p);
 | 
					        window.sessionStorage.setItem("p", p);
 | 
				
			||||||
@@ -55,4 +56,6 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    pubKey = new PubKey(p, q);
 | 
					    pubKey = new PubKey(p, q);
 | 
				
			||||||
    privKey = new PrivKey(p, q);
 | 
					    privKey = new PrivKey(p, q);
 | 
				
			||||||
});
 | 
					
 | 
				
			||||||
 | 
					    return { pubKey, privKey };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,6 @@
 | 
				
			|||||||
function random2048() {
 | 
					import { mod_exp } from "./math.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function random2048() {
 | 
				
			||||||
    const byteArray = new BigUint64Array(32);
 | 
					    const byteArray = new BigUint64Array(32);
 | 
				
			||||||
    window.crypto.getRandomValues(byteArray);
 | 
					    window.crypto.getRandomValues(byteArray);
 | 
				
			||||||
    let intRepr = 0n;
 | 
					    let intRepr = 0n;
 | 
				
			||||||
@@ -55,7 +57,7 @@ function miller_rabin(n, k) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    for (; k > 0; k--) {
 | 
					    for (; k > 0; k--) {
 | 
				
			||||||
        let a = random2048();
 | 
					        let a = random2048();
 | 
				
			||||||
        let x = fastModularExponentiation(a, d, n);
 | 
					        let x = mod_exp(a, d, n);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (x === 1n || x === n - 1n) {
 | 
					        if (x === 1n || x === n - 1n) {
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
@@ -77,7 +79,7 @@ function miller_rabin(n, k) {
 | 
				
			|||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function generate_prime() {
 | 
					export function generate_prime() {
 | 
				
			||||||
    while (true) {
 | 
					    while (true) {
 | 
				
			||||||
        let n = generate_bigint();
 | 
					        let n = generate_bigint();
 | 
				
			||||||
        if (small_prime_test(n) && miller_rabin(n, 40)) {
 | 
					        if (small_prime_test(n) && miller_rabin(n, 40)) {
 | 
				
			||||||
@@ -86,21 +88,6 @@ function generate_prime() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function fastModularExponentiation(a, b, n) {
 | 
					 | 
				
			||||||
    let res = 1n;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    while (b > 0n) {
 | 
					 | 
				
			||||||
        if (b % 2n === 1n) {
 | 
					 | 
				
			||||||
            res = (res * a) % n;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        b >>= 1n;
 | 
					 | 
				
			||||||
        a = (a * a) % n;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return res;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const SMALL_PRIMES = [
 | 
					const SMALL_PRIMES = [
 | 
				
			||||||
    2n,
 | 
					    2n,
 | 
				
			||||||
    3n,
 | 
					    3n,
 | 
				
			||||||
@@ -1,9 +1,12 @@
 | 
				
			|||||||
 | 
					import { socket, players } from "./main.js";
 | 
				
			||||||
 | 
					import { Packet } from "./packet.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Typical barrier type.
 | 
					 * Typical barrier type.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Block all clients until everyone has hit the barrier.
 | 
					 * Block all clients until everyone has hit the barrier.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class Barrier {
 | 
					export class Barrier {
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        let resolver;
 | 
					        let resolver;
 | 
				
			||||||
        this.promise = new Promise((resolve) => {
 | 
					        this.promise = new Promise((resolve) => {
 | 
				
			||||||
@@ -1,4 +1,19 @@
 | 
				
			|||||||
function unlockMapDom() {
 | 
					import {
 | 
				
			||||||
 | 
					    gameState,
 | 
				
			||||||
 | 
					    WAITING,
 | 
				
			||||||
 | 
					    PRE_GAME,
 | 
				
			||||||
 | 
					    PLAYING,
 | 
				
			||||||
 | 
					    us,
 | 
				
			||||||
 | 
					    socket,
 | 
				
			||||||
 | 
					    players,
 | 
				
			||||||
 | 
					    ID,
 | 
				
			||||||
 | 
					    allPlayersReady,
 | 
				
			||||||
 | 
					    startPregame,
 | 
				
			||||||
 | 
					} from "./main.js";
 | 
				
			||||||
 | 
					import { Region } from "./map.js";
 | 
				
			||||||
 | 
					import { Packet } from "./packet.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function unlockMapDom() {
 | 
				
			||||||
    Object.values(REGIONS).forEach((region) => {
 | 
					    Object.values(REGIONS).forEach((region) => {
 | 
				
			||||||
        if (!allRegionsClaimed() && region.owner === null) {
 | 
					        if (!allRegionsClaimed() && region.owner === null) {
 | 
				
			||||||
            document
 | 
					            document
 | 
				
			||||||
@@ -12,11 +27,11 @@ function unlockMapDom() {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function lockMapDom() {
 | 
					export function lockMapDom() {
 | 
				
			||||||
    document.querySelectorAll(".actions").forEach((e) => e.classList.add("hidden"));
 | 
					    document.querySelectorAll(".actions").forEach((e) => e.classList.add("hidden"));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function updateDom() {
 | 
					export function updateDom() {
 | 
				
			||||||
    if (gameState !== WAITING) {
 | 
					    if (gameState !== WAITING) {
 | 
				
			||||||
        document.querySelector("#ready-button").style.display = "none";
 | 
					        document.querySelector("#ready-button").style.display = "none";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -49,7 +64,7 @@ function updateMapDom() {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (let region of Object.values(REGIONS)) {
 | 
					    for (let region of Region.getAllRegions()) {
 | 
				
			||||||
        const element = document.querySelector(`.node[data-name=${region.name}]`);
 | 
					        const element = document.querySelector(`.node[data-name=${region.name}]`);
 | 
				
			||||||
        element.querySelector(".strength").textContent = region.strength || "";
 | 
					        element.querySelector(".strength").textContent = region.strength || "";
 | 
				
			||||||
        element.style.backgroundColor =
 | 
					        element.style.backgroundColor =
 | 
				
			||||||
							
								
								
									
										82
									
								
								static/js/modules/interface/game.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								static/js/modules/interface/game.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					import { Player } from "./player";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const WAITING = 0;
 | 
				
			||||||
 | 
					const PRE_GAME = 1;
 | 
				
			||||||
 | 
					const PLAYING = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Game {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        this.us = null;
 | 
				
			||||||
 | 
					        this.players = {};
 | 
				
			||||||
 | 
					        this.state = WAITING;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    isWaiting() {
 | 
				
			||||||
 | 
					        return this.state === WAITING;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    isPregame() {
 | 
				
			||||||
 | 
					        return this.state === PRE_GAME;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    isPlaying() {
 | 
				
			||||||
 | 
					        return this.state === PLAYING;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    incrementState() {
 | 
				
			||||||
 | 
					        this.state += 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    currentPlayer() {
 | 
				
			||||||
 | 
					        return Object.values(this.players).filter((p) => p.isPlaying)[0];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    addPlayer(id, name, is_us) {
 | 
				
			||||||
 | 
					        let is_new = this.players[id] === undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.isWaiting()) {
 | 
				
			||||||
 | 
					            this.players[id] = new Player(id, name, is_us);
 | 
				
			||||||
 | 
					            if (is_us === true) {
 | 
				
			||||||
 | 
					                this.us = this.players[id];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (is_new) {
 | 
				
			||||||
 | 
					            const event = new CustomEvent("addPlayer");
 | 
				
			||||||
 | 
					            document.dispatchEvent(event);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return is_new;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    removePlayer(id) {
 | 
				
			||||||
 | 
					        if (this.players[id] !== undefined) {
 | 
				
			||||||
 | 
					            const event = new CustomEvent("removePlayer");
 | 
				
			||||||
 | 
					            document.dispatchEvent(event);
 | 
				
			||||||
 | 
					            delete this.players[id];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    keepAlive(id) {
 | 
				
			||||||
 | 
					        if (id !== this.us.id) {
 | 
				
			||||||
 | 
					            this.players[id].resetTimeout(this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setReady(id, ready) {
 | 
				
			||||||
 | 
					        this.players[id].readyState = ready;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this._allPlayersReady()) {
 | 
				
			||||||
 | 
					            this.incrementState();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _allPlayersReady() {
 | 
				
			||||||
 | 
					        for (let player of Object.values(this.players)) {
 | 
				
			||||||
 | 
					            if (!player.readyState) {
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										135
									
								
								static/js/modules/interface/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								static/js/modules/interface/main.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
				
			|||||||
 | 
					import { generate_keypair } from "../crypto/main.js";
 | 
				
			||||||
 | 
					import { Random } from "./random.js";
 | 
				
			||||||
 | 
					import { Barrier } from "./barrier.js";
 | 
				
			||||||
 | 
					import { Packet } from "./packet.js";
 | 
				
			||||||
 | 
					import { updateDom } from "./dom.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ID = window.crypto.randomUUID();
 | 
				
			||||||
 | 
					export let us = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const game = new Game();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export let socket;
 | 
				
			||||||
 | 
					let random;
 | 
				
			||||||
 | 
					let barrier;
 | 
				
			||||||
 | 
					let keys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Not totally reliable but better than nothing.
 | 
				
			||||||
 | 
					window.addEventListener("beforeunload", () => {
 | 
				
			||||||
 | 
					    socket.emit("message", Packet.createDisconnect());
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					document.addEventListener("DOMContentLoaded", () => {
 | 
				
			||||||
 | 
					    socket = io();
 | 
				
			||||||
 | 
					    random = new Random();
 | 
				
			||||||
 | 
					    barrier = new Barrier();
 | 
				
			||||||
 | 
					    keys = generate_keypair();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    socket.on("connect", () => {
 | 
				
			||||||
 | 
					        window.console.log("Connected!");
 | 
				
			||||||
 | 
					        socket.emit("message", Packet.createAnnounce());
 | 
				
			||||||
 | 
					        game.addPlayer(ID, name, true);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    socket.on("message", async (data) => {
 | 
				
			||||||
 | 
					        // todo validate signature
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (data.type) {
 | 
				
			||||||
 | 
					            case "RANDOM":
 | 
				
			||||||
 | 
					                if (data.author === ID) {
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                await random.processCooperativeRandom(data);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case "BARRIER":
 | 
				
			||||||
 | 
					                if (data.author === ID) {
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                barrier.resolve(data);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                const event = new CustomEvent(data.type, { detail: data });
 | 
				
			||||||
 | 
					                document.dispatchEvent(event);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Process player connect packets: these inform that a new player has joined.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param data Packet received
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					document.addEventListener("ANNOUNCE", (data) => {
 | 
				
			||||||
 | 
					    if (data.author === ID) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let is_new = game.addPlayer(data.author, data.name, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // When a new player is seen, all announce to ensure they know all players.
 | 
				
			||||||
 | 
					    if (is_new) {
 | 
				
			||||||
 | 
					        socket.emit("message", Packet.createAnnounce());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					document.addEventListener("DISCONNECT", (data) => {
 | 
				
			||||||
 | 
					    game.removePlayer(data.author);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Process keep-alive packets: these are packets that check players are still online.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param data Packet received
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					document.addEventListener("KEEPALIVE", (data) => {
 | 
				
			||||||
 | 
					    game.keepAlive(data.author);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					document.addEventListener("ACT", async (data) => {
 | 
				
			||||||
 | 
					    if (data.author !== game.currentPlayer().id) {
 | 
				
			||||||
 | 
					        if (data.action === "DEFENSE") {
 | 
				
			||||||
 | 
					            await game.players[data.author].setDefense(data.amount);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (game.isWaiting()) {
 | 
				
			||||||
 | 
					        game.setReady(data.author, data.ready);
 | 
				
			||||||
 | 
					    } else if (game.isPregame()) {
 | 
				
			||||||
 | 
					        if (!Region.allRegionsClaimed()) {
 | 
				
			||||||
 | 
					            // Claim a region in the pregame.
 | 
				
			||||||
 | 
					            if (game.currentPlayer().claim(data)) {
 | 
				
			||||||
 | 
					                // Increment to next player.
 | 
				
			||||||
 | 
					                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() {
 | 
				
			||||||
 | 
					    gameState = PRE_GAME;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let firstPlayerIndex = await random.get(Object.keys(players).length, "first-player");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let firstPlayer = Object.values(players).sort((a, b) => (a.id < b.id ? -1 : 1))[
 | 
				
			||||||
 | 
					        firstPlayerIndex
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    firstPlayer.isPlaying = true;
 | 
				
			||||||
 | 
					    await barrier.wait();
 | 
				
			||||||
 | 
					    updateDom();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,3 +1,10 @@
 | 
				
			|||||||
 | 
					let allPlaced = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// In standard Risk, this is 5
 | 
				
			||||||
 | 
					const _REINFORCEMENT_MULTIPLIER = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const REGIONS = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Continent {
 | 
					class Continent {
 | 
				
			||||||
    constructor(name) {
 | 
					    constructor(name) {
 | 
				
			||||||
        this.name = name;
 | 
					        this.name = name;
 | 
				
			||||||
@@ -5,9 +12,7 @@ class Continent {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const REGIONS = {};
 | 
					export class Region {
 | 
				
			||||||
 | 
					 | 
				
			||||||
class Region {
 | 
					 | 
				
			||||||
    constructor(name, continent) {
 | 
					    constructor(name, continent) {
 | 
				
			||||||
        this.name = name;
 | 
					        this.name = name;
 | 
				
			||||||
        this.owner = null;
 | 
					        this.owner = null;
 | 
				
			||||||
@@ -23,10 +28,50 @@ class Region {
 | 
				
			|||||||
        region2.neighbours.add(region1);
 | 
					        region2.neighbours.add(region1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static allRegionsClaimed() {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            Object.values(REGIONS).find((region) => region.owner === null) === undefined
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static reinforcementsRemaining() {
 | 
				
			||||||
 | 
					        if (allPlaced) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            let totalStrength = Object.values(REGIONS)
 | 
				
			||||||
 | 
					                .filter((region) => region.owner === us)
 | 
				
			||||||
 | 
					                .reduce((counter, region) => counter + region.strength, 0);
 | 
				
			||||||
 | 
					            let numPlayers = Object.values(players).length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return _REINFORCEMENT_MULTIPLIER * (10 - numPlayers) - totalStrength;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static allReinforcementsPlaced() {
 | 
				
			||||||
 | 
					        if (allPlaced) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            let totalStrength = Object.values(REGIONS).reduce(
 | 
				
			||||||
 | 
					                (counter, region) => counter + region.strength,
 | 
				
			||||||
 | 
					                0
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            let numPlayers = Object.values(players).length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            allPlaced =
 | 
				
			||||||
 | 
					                totalStrength >=
 | 
				
			||||||
 | 
					                numPlayers * _REINFORCEMENT_MULTIPLIER * (10 - numPlayers);
 | 
				
			||||||
 | 
					            return allPlaced;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static getRegion(name) {
 | 
					    static getRegion(name) {
 | 
				
			||||||
        return REGIONS[name];
 | 
					        return REGIONS[name];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static getAllRegions() {
 | 
				
			||||||
 | 
					        return Object.values(REGIONS);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    claim(player) {
 | 
					    claim(player) {
 | 
				
			||||||
        this.owner = player;
 | 
					        this.owner = player;
 | 
				
			||||||
        this.strength = 1;
 | 
					        this.strength = 1;
 | 
				
			||||||
@@ -66,41 +111,3 @@ Region.setNeighbours(F, G);
 | 
				
			|||||||
Region.setNeighbours(G, H);
 | 
					Region.setNeighbours(G, H);
 | 
				
			||||||
Region.setNeighbours(G, I);
 | 
					Region.setNeighbours(G, I);
 | 
				
			||||||
Region.setNeighbours(H, I);
 | 
					Region.setNeighbours(H, I);
 | 
				
			||||||
 | 
					 | 
				
			||||||
function allRegionsClaimed() {
 | 
					 | 
				
			||||||
    return Object.values(REGIONS).find((region) => region.owner === null) === undefined;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let allPlaced = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// In standard Risk, this is 5
 | 
					 | 
				
			||||||
const _REINFORCEMENT_MULTIPLIER = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function reinforcementsRemaining() {
 | 
					 | 
				
			||||||
    if (allPlaced) {
 | 
					 | 
				
			||||||
        return 0;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        let totalStrength = Object.values(REGIONS)
 | 
					 | 
				
			||||||
            .filter((region) => region.owner === us)
 | 
					 | 
				
			||||||
            .reduce((counter, region) => counter + region.strength, 0);
 | 
					 | 
				
			||||||
        let numPlayers = Object.values(players).length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return _REINFORCEMENT_MULTIPLIER * (10 - numPlayers) - totalStrength;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function allReinforcementsPlaced() {
 | 
					 | 
				
			||||||
    if (allPlaced) {
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        let totalStrength = Object.values(REGIONS).reduce(
 | 
					 | 
				
			||||||
            (counter, region) => counter + region.strength,
 | 
					 | 
				
			||||||
            0
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        let numPlayers = Object.values(players).length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        allPlaced =
 | 
					 | 
				
			||||||
            totalStrength >= numPlayers * _REINFORCEMENT_MULTIPLIER * (10 - numPlayers);
 | 
					 | 
				
			||||||
        return allPlaced;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,4 +1,6 @@
 | 
				
			|||||||
class Packet {
 | 
					import { ID } from "./main.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Packet {
 | 
				
			||||||
    static _createBase(name) {
 | 
					    static _createBase(name) {
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            type: name,
 | 
					            type: name,
 | 
				
			||||||
@@ -24,7 +26,8 @@ class Packet {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    static createSetReady(nowReady) {
 | 
					    static createSetReady(nowReady) {
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            ...this._createBase("READY"),
 | 
					            ...this._createBase("ACT"),
 | 
				
			||||||
 | 
					            action: "READY",
 | 
				
			||||||
            ready: nowReady,
 | 
					            ready: nowReady,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -1,12 +1,18 @@
 | 
				
			|||||||
 | 
					import { Packet } from "./packet.js";
 | 
				
			||||||
 | 
					import { socket } from "./main.js";
 | 
				
			||||||
 | 
					import { updateDom } from "./dom.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Timeout to consider a player disconnected
 | 
				
			||||||
 | 
					const TIMEOUT = 30_000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PHASE_REINFORCE = 1;
 | 
					const PHASE_REINFORCE = 1;
 | 
				
			||||||
const PHASE_ATTACK = 2;
 | 
					const PHASE_ATTACK = 2;
 | 
				
			||||||
const PHASE_FORTIFY = 3;
 | 
					const PHASE_FORTIFY = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let totalDice = 0;
 | 
					let totalDice = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Player {
 | 
					export class Player {
 | 
				
			||||||
    constructor(id, name) {
 | 
					    constructor(id, local) {
 | 
				
			||||||
        this.name = name;
 | 
					 | 
				
			||||||
        this.timeout = null;
 | 
					        this.timeout = null;
 | 
				
			||||||
        this.id = id;
 | 
					        this.id = id;
 | 
				
			||||||
        this.ready = false;
 | 
					        this.ready = false;
 | 
				
			||||||
@@ -23,18 +29,22 @@ class Player {
 | 
				
			|||||||
        this.defenderAmount = null;
 | 
					        this.defenderAmount = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.resetColor();
 | 
					        this.resetColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (local) {
 | 
				
			||||||
 | 
					            // Emit keepalive messages to inform other players we are still here
 | 
				
			||||||
 | 
					            window.setInterval(() => {
 | 
				
			||||||
 | 
					                socket.emit("message", Packet.createKeepAlive());
 | 
				
			||||||
 | 
					            }, TIMEOUT / 5);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    resetTimeout() {
 | 
					    resetTimeout(game) {
 | 
				
			||||||
        if (this.timeout !== null) {
 | 
					        if (this.timeout !== null) {
 | 
				
			||||||
            window.clearTimeout(this.timeout);
 | 
					            window.clearTimeout(this.timeout);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.timeout = window.setTimeout(() => {
 | 
					        this.timeout = window.setTimeout(() => {
 | 
				
			||||||
            if (players[this.id] !== undefined) {
 | 
					            game.removePlayer(this);
 | 
				
			||||||
                delete players[this.id];
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            updateDom();
 | 
					 | 
				
			||||||
        }, TIMEOUT);
 | 
					        }, TIMEOUT);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					import { socket, ID, players } from "./main.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RandomSession {
 | 
					class RandomSession {
 | 
				
			||||||
    constructor(range) {
 | 
					    constructor(range) {
 | 
				
			||||||
        this.range = range;
 | 
					        this.range = range;
 | 
				
			||||||
@@ -15,7 +17,7 @@ class RandomSession {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Random {
 | 
					export class Random {
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        this.sessions = {};
 | 
					        this.sessions = {};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -7,15 +7,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    <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/index.js') }}"></script>
 | 
					    <script src="{{ url_for('static', filename='js/modules/interface/main.js') }}" type="module"></script>
 | 
				
			||||||
    <script src="{{ url_for('static', filename='js/player.js') }}"></script>
 | 
					    <script src="{{ url_for('static', filename='js/modules/crypto/main.js') }}" type="module"></script>
 | 
				
			||||||
    <script src="{{ url_for('static', filename='js/dom.js') }}"></script>
 | 
					 | 
				
			||||||
    <script src="{{ url_for('static', filename='js/random.js') }}"></script>
 | 
					 | 
				
			||||||
    <script src="{{ url_for('static', filename='js/barrier.js') }}"></script>
 | 
					 | 
				
			||||||
    <script src="{{ url_for('static', filename='js/packet.js') }}"></script>
 | 
					 | 
				
			||||||
    <script src="{{ url_for('static', filename='js/map.js') }}"></script>
 | 
					 | 
				
			||||||
    <script src="{{ url_for('static', filename='js/random_primes.js') }}"></script>
 | 
					 | 
				
			||||||
    <script src="{{ url_for('static', filename='js/paillier.js') }}"></script>
 | 
					 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
    <div id="modal" class="hidden modal">
 | 
					    <div id="modal" class="hidden modal">
 | 
				
			||||||
@@ -52,7 +45,7 @@
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div id="players">
 | 
					    <div id="players-div">
 | 
				
			||||||
        <strong>Players</strong>
 | 
					        <strong>Players</strong>
 | 
				
			||||||
        <ul id="playerList">
 | 
					        <ul id="playerList">
 | 
				
			||||||
        </ul>
 | 
					        </ul>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user