Add barrier type. Transition into playing phase properly.

This commit is contained in:
2023-02-02 11:27:52 +00:00
parent 8afe512062
commit 8d1a7e14e3
10 changed files with 155 additions and 46 deletions

View File

@ -23,3 +23,17 @@
font-size: 2em;
color: green;
}
.status-span {
display: inline-block;
font-weight: bold;
width: 20px;
}
.ready {
color: green;
}
.not-ready {
color: red;
}

30
static/js/barrier.js Normal file
View File

@ -0,0 +1,30 @@
/**
* Typical barrier type.
*
* Block all clients until everyone has hit the barrier.
*/
class Barrier {
constructor() {
let resolver;
this.promise = new Promise((resolve) => {
resolver = resolve;
});
this.resolver = resolver;
this.hits = new Set();
}
wait() {
socket.emit("message", Packet.createBarrierSignal());
return this.promise;
}
resolve(data) {
this.hits.add(data.author);
if (this.hits.size === Object.keys(players).length - 1) {
this.hits = new Set();
this.resolver();
}
}
}

View File

@ -2,17 +2,36 @@ function updatePlayerDom() {
let list = document.querySelector("#playerList");
list.replaceChildren();
let newDom = document.createElement("li");
newDom.textContent = ID + " (you)";
newDom.style.color = "grey";
list.appendChild(newDom);
for (let playerId of Object.keys(players).sort()) {
let player = players[playerId];
for (let playerId of Object.keys(players)) {
if (playerId !== ID) {
let newDom = document.createElement("li");
newDom.textContent = playerId;
list.appendChild(newDom);
let statusSpan = document.createElement("div");
statusSpan.classList.add("status-span");
if (game_state === WAITING) {
if (player.ready) {
statusSpan.textContent = "R";
statusSpan.classList.add("ready");
} else {
statusSpan.textContent = "N";
statusSpan.classList.add("not-ready");
}
} else {
if (player.isPlaying) {
statusSpan.textContent = "P";
}
}
let idSpan = document.createElement("span");
if (playerId === ID) {
idSpan.textContent = `${playerId} (you)`;
} else {
idSpan.textContent = playerId;
}
let newDom = document.createElement("li");
newDom.appendChild(statusSpan);
newDom.appendChild(idSpan);
list.appendChild(newDom);
}
}
@ -24,12 +43,9 @@ document.addEventListener("DOMContentLoaded", () => {
ev.target.classList.toggle("active");
ev.target.textContent = nowReady ? "Ready" : "Not ready";
socket.emit("message", {
type: "SYNC",
author: ID,
ready: nowReady,
name: "",
});
socket.emit("message", Packet.createSetReady(nowReady));
updatePlayerDom();
if (allPlayersReady()) {
await startPregame();

View File

@ -14,29 +14,21 @@ let game_state = WAITING;
let socket;
let random;
let barrier;
// Not totally reliable but better than nothing.
window.addEventListener("beforeunload", () => {
socket.emit("message", {
type: "DISCONNECT",
id: window.crypto.randomUUID(),
author: ID,
name: "",
});
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", {
type: "ANNOUNCE",
id: window.crypto.randomUUID(),
author: ID,
name: "",
});
socket.emit("message", Packet.createAnnounce());
// Create self
players[ID] = new Player(ID, name);
us = players[ID];
@ -61,23 +53,23 @@ document.addEventListener("DOMContentLoaded", () => {
keepAlive(data);
break;
case "SYNC":
await sync(data);
case "READY":
await setReady(data);
break;
case "RANDOM":
await random.processCooperativeRandom(data);
break;
case "BARRIER":
barrier.resolve(data);
break;
}
});
// Emit keepalive messages to inform other players we are still here
window.setInterval(() => {
socket.emit("message", {
type: "KEEPALIVE",
id: window.crypto.randomUUID(),
author: ID,
});
socket.emit("message", Packet.createKeepAlive());
}, TIMEOUT / 5);
});
@ -95,12 +87,7 @@ function playerConnected(data) {
// 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", {
type: "ANNOUNCE",
id: window.crypto.randomUUID(),
author: ID,
name: "",
});
socket.emit("message", Packet.createAnnounce());
players[data.author].resetTimeout();
} else {
}
@ -128,10 +115,12 @@ function keepAlive(data) {
*
* @param data Packet received
*/
async function sync(data) {
async function setReady(data) {
players[data.author].name = data.name;
players[data.author].ready = data.ready;
updatePlayerDom();
if (allPlayersReady()) {
await startPregame();
}
@ -148,11 +137,20 @@ function allPlayersReady() {
}
async function startPregame() {
console.log("All players ready.");
console.log("all players ready.");
game_state = PRE_GAME;
let player1 = await random.get(Object.keys(players).length, "first-player");
let firstPlayerIndex = await random.get(Object.keys(players).length, "first-player");
console.log(player1);
let firstPlayer = Object.values(players).sort((a, b) => (a.id < b.id ? -1 : 1))[
firstPlayerIndex
];
firstPlayer.isPlaying = true;
game_state = PLAYING;
await barrier.wait();
updatePlayerDom();
}

35
static/js/packet.js Normal file
View File

@ -0,0 +1,35 @@
class Packet {
static _createBase(name) {
return {
type: name,
id: window.crypto.randomUUID(),
author: ID,
};
}
static createAnnounce() {
return {
...this._createBase("ANNOUNCE"),
name: "",
};
}
static createDisconnect() {
return this._createBase("DISCONNECT");
}
static createKeepAlive() {
return this._createBase("KEEPALIVE");
}
static createSetReady(nowReady) {
return {
...this._createBase("READY"),
ready: nowReady,
};
}
static createBarrierSignal() {
return this._createBase("BARRIER");
}
}

View File

@ -4,6 +4,7 @@ class Player {
this.timeout = null;
this.id = id;
this.ready = false;
this.isPlaying = false;
}
resetTimeout() {

View File

@ -27,7 +27,6 @@ class Random {
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;