Mid refactor to stop polluting the namespace so much
This commit is contained in:
parent
7d9531f0d9
commit
cf0c9135e1
@ -1,4 +1,4 @@
|
||||
#players {
|
||||
#players-div {
|
||||
position: absolute;
|
||||
right: 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;
|
||||
|
||||
class PubKey {
|
||||
@ -18,7 +21,7 @@ class PubKey {
|
||||
// Compute g^m by binomial theorem.
|
||||
let gm = (1n + this.n * m) % this.n ** 2n;
|
||||
// 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) {
|
||||
this.n = p * q;
|
||||
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) {
|
||||
return (
|
||||
(((fastModularExponentiation(c, this.lambda, this.n ** 2n) - 1n) / this.n) *
|
||||
this.mu) %
|
||||
this.n
|
||||
(((mod_exp(c, this.lambda, this.n ** 2n) - 1n) / this.n) * this.mu) % this.n
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
export function generate_keypair() {
|
||||
if (window.sessionStorage.getItem("p") === null) {
|
||||
p = generate_prime();
|
||||
window.sessionStorage.setItem("p", p);
|
||||
@ -55,4 +56,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
pubKey = new PubKey(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);
|
||||
window.crypto.getRandomValues(byteArray);
|
||||
let intRepr = 0n;
|
||||
@ -55,7 +57,7 @@ function miller_rabin(n, k) {
|
||||
|
||||
for (; k > 0; k--) {
|
||||
let a = random2048();
|
||||
let x = fastModularExponentiation(a, d, n);
|
||||
let x = mod_exp(a, d, n);
|
||||
|
||||
if (x === 1n || x === n - 1n) {
|
||||
continue;
|
||||
@ -77,7 +79,7 @@ function miller_rabin(n, k) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function generate_prime() {
|
||||
export function generate_prime() {
|
||||
while (true) {
|
||||
let n = generate_bigint();
|
||||
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 = [
|
||||
2n,
|
||||
3n,
|
@ -1,9 +1,12 @@
|
||||
import { socket, players } from "./main.js";
|
||||
import { Packet } from "./packet.js";
|
||||
|
||||
/**
|
||||
* Typical barrier type.
|
||||
*
|
||||
* Block all clients until everyone has hit the barrier.
|
||||
*/
|
||||
class Barrier {
|
||||
export class Barrier {
|
||||
constructor() {
|
||||
let resolver;
|
||||
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) => {
|
||||
if (!allRegionsClaimed() && region.owner === null) {
|
||||
document
|
||||
@ -12,11 +27,11 @@ function unlockMapDom() {
|
||||
});
|
||||
}
|
||||
|
||||
function lockMapDom() {
|
||||
export function lockMapDom() {
|
||||
document.querySelectorAll(".actions").forEach((e) => e.classList.add("hidden"));
|
||||
}
|
||||
|
||||
function updateDom() {
|
||||
export function updateDom() {
|
||||
if (gameState !== WAITING) {
|
||||
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}]`);
|
||||
element.querySelector(".strength").textContent = region.strength || "";
|
||||
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 {
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
@ -5,9 +12,7 @@ class Continent {
|
||||
}
|
||||
}
|
||||
|
||||
const REGIONS = {};
|
||||
|
||||
class Region {
|
||||
export class Region {
|
||||
constructor(name, continent) {
|
||||
this.name = name;
|
||||
this.owner = null;
|
||||
@ -23,10 +28,50 @@ class Region {
|
||||
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) {
|
||||
return REGIONS[name];
|
||||
}
|
||||
|
||||
static getAllRegions() {
|
||||
return Object.values(REGIONS);
|
||||
}
|
||||
|
||||
claim(player) {
|
||||
this.owner = player;
|
||||
this.strength = 1;
|
||||
@ -66,41 +111,3 @@ Region.setNeighbours(F, G);
|
||||
Region.setNeighbours(G, H);
|
||||
Region.setNeighbours(G, 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) {
|
||||
return {
|
||||
type: name,
|
||||
@ -24,7 +26,8 @@ class Packet {
|
||||
|
||||
static createSetReady(nowReady) {
|
||||
return {
|
||||
...this._createBase("READY"),
|
||||
...this._createBase("ACT"),
|
||||
action: "READY",
|
||||
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_ATTACK = 2;
|
||||
const PHASE_FORTIFY = 3;
|
||||
|
||||
let totalDice = 0;
|
||||
|
||||
class Player {
|
||||
constructor(id, name) {
|
||||
this.name = name;
|
||||
export class Player {
|
||||
constructor(id, local) {
|
||||
this.timeout = null;
|
||||
this.id = id;
|
||||
this.ready = false;
|
||||
@ -23,18 +29,22 @@ class Player {
|
||||
this.defenderAmount = null;
|
||||
|
||||
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) {
|
||||
window.clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
this.timeout = window.setTimeout(() => {
|
||||
if (players[this.id] !== undefined) {
|
||||
delete players[this.id];
|
||||
}
|
||||
updateDom();
|
||||
game.removePlayer(this);
|
||||
}, TIMEOUT);
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { socket, ID, players } from "./main.js";
|
||||
|
||||
class RandomSession {
|
||||
constructor(range) {
|
||||
this.range = range;
|
||||
@ -15,7 +17,7 @@ class RandomSession {
|
||||
}
|
||||
}
|
||||
|
||||
class Random {
|
||||
export class Random {
|
||||
constructor() {
|
||||
this.sessions = {};
|
||||
}
|
@ -7,15 +7,8 @@
|
||||
|
||||
<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/index.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/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>
|
||||
<script src="{{ url_for('static', filename='js/modules/interface/main.js') }}" type="module"></script>
|
||||
<script src="{{ url_for('static', filename='js/modules/crypto/main.js') }}" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="modal" class="hidden modal">
|
||||
@ -52,7 +45,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="players">
|
||||
<div id="players-div">
|
||||
<strong>Players</strong>
|
||||
<ul id="playerList">
|
||||
</ul>
|
||||
|
Loading…
Reference in New Issue
Block a user