Many multiplayer games need a shared timer. For example many games are played in rounds with a fixed time: e.g. 10 seconds or 5 minutes. These rounds should start and stop at (very nearly) the same time for every player. If every client kept track of time themselves, it would be quite likely be confusing. Instead there should be a single, authoritative clock shared by everyone.
In p5.party, the best way to manage a shared timer is to have the “host” take care of it. The host should start the round, keep track of time, and end the round.
One might consider having the host use setTimeout()
for timekeeping, but setTimeout()
is local to individual clients, and it is possible that the “host” might change to a different client between when the round starts and when it ends.
A better approach is to have the host record the time when the round starts and then periodically compare the current time to the start time to see how much time has elapsed. The host can announce the start and end of each round by setting a state property in a shared object or by using events (partyEmit()
).
If you want the clients to display the elapsed or remaining time, it is probably best to have the host calculate the value and share it.
If the start time is kept in a shared object property, this approach will work even if the host changes during the round. This won’t work perfectly because the old host and the new host probably won’t have perfectly synchronized clocks. But in practice, this isn’t usually a significant problem when prototyping.
const WAITING_DURATION = 1000;
const PLAYING_DURATION = 4000;
let shared;
function preload() {
partyConnect("wss://demoserver.p5party.org", "timer");
shared = partyLoadShared("globals", {
gameState: "waiting", // waiting, playing
startTime: Date.now(),
displayTime: null,
});
}
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
// update
if (partyIsHost()) manageTimer();
// draw
background("#006666");
if (shared.gameState === "waiting") {
drawWaiting();
} else if (shared.gameState === "playing") {
drawPlaying();
}
}
function drawWaiting() {
push();
fill("#f00");
circle(width / 2, height / 2, 200);
fill("white");
textSize(32);
textAlign(CENTER, CENTER);
text("WAITING", width / 2, height / 2);
pop();
}
function drawPlaying() {
push();
fill("#0c0");
circle(width / 2, height / 2, 200);
fill("white");
textSize(32);
textAlign(CENTER, CENTER);
text("PLAYING", width / 2, height / 2);
text(shared.displayTime, width / 2, height / 2 + 50);
pop();
}
function manageTimer() {
if (shared.gameState === "waiting") {
const elapsed = Date.now() - shared.startTime;
if (elapsed > WAITING_DURATION) {
shared.startTime = Date.now();
shared.gameState = "playing";
shared.displayTime = floor(PLAYING_DURATION / 1000) + 1;
}
} else if (shared.gameState === "playing") {
const elapsed = Date.now() - shared.startTime;
shared.displayTime = floor((PLAYING_DURATION - elapsed) / 1000) + 1;
if (elapsed > PLAYING_DURATION) {
shared.startTime = Date.now();
shared.gameState = "waiting";
shared.displayTime = "";
}
}
}
<!DOCTYPE html>
<html>
<head> </head>
<body>
<main></main>
<div id="readme"></div>
<h2>index.js</h2>
<div id="source-javascript"></div>
<h2>index.html</h2>
<div id="source-html"></div>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.js"></script>
<script src="/dist/p5.party.js"></script>
<script src="index.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.3/highlight.min.js"></script>
<link rel="stylesheet" href="/examples.css" />
<script src="/examples.js" type="module"></script>
</body>
</html>