Add turn timer functionality to Table class; update environment configuration

This commit is contained in:
2025-12-20 23:33:55 +01:00
parent f10714b2c8
commit 6e0a97df53
2 changed files with 44 additions and 0 deletions

View File

@@ -20,3 +20,4 @@ MIN_BET=10
MAX_BET=500
ROUND_START_DELAY_MS=3000
ROUND_RESET_DELAY_MS=5000
TURN_TIME_MS=15000

View File

@@ -5,6 +5,7 @@ const MIN_BET = Number(process.env.MIN_BET || 10);
const MAX_BET = Number(process.env.MAX_BET || 100);
const ROUND_START_DELAY_MS = Number(process.env.ROUND_START_DELAY_MS || 3000);
const ROUND_RESET_DELAY_MS = Number(process.env.ROUND_RESET_DELAY_MS || 5000);
const TURN_TIME_MS = Number(process.env.TURN_TIME_MS || 15000);
class Table {
constructor(id, seatCount) {
@@ -26,6 +27,9 @@ class Table {
this.roundId = 0;
this.roundTimeout = null;
this.resetTimeout = null;
this.turnTimeout = null;
this.turnEndsAt = null;
this.turnCounter = 0;
this.clients = new Set();
}
@@ -55,6 +59,7 @@ class Table {
minBet: MIN_BET,
maxBet: MAX_BET,
roundId: this.roundId,
turnEndsAt: this.turnEndsAt,
dealerHand,
currentSeatIndex: this.currentSeatIndex,
seats: this.seats.map((seat, index) => ({
@@ -103,6 +108,36 @@ class Table {
}
}
clearTurnTimer() {
if (this.turnTimeout) {
clearTimeout(this.turnTimeout);
}
this.turnTimeout = null;
this.turnEndsAt = null;
}
startTurnTimer() {
this.clearTurnTimer();
if (this.currentSeatIndex === null) {
return;
}
this.turnEndsAt = Date.now() + TURN_TIME_MS;
const turnToken = ++this.turnCounter;
this.turnTimeout = setTimeout(() => {
if (this.turnCounter !== turnToken) {
return;
}
const seat = this.seats[this.currentSeatIndex];
if (!seat || seat.status !== 'playing') {
return;
}
seat.status = 'stood';
void this.advanceTurn();
}, TURN_TIME_MS);
}
findSeatIndexByUser(userId) {
return this.seats.findIndex((seat) => seat.userId === userId);
}
@@ -251,6 +286,7 @@ class Table {
return;
}
this.clearTurnTimer();
this.phase = 'playing';
this.roundId += 1;
this.deck = shuffle(createDeck());
@@ -272,6 +308,7 @@ class Table {
return;
}
this.startTurnTimer();
this.broadcastState();
}
@@ -310,6 +347,8 @@ class Table {
} else if (value === 21) {
seat.status = 'stood';
await this.advanceTurn();
} else {
this.startTurnTimer();
}
} else if (action === 'stand') {
seat.status = 'stood';
@@ -343,15 +382,18 @@ class Table {
async advanceTurn() {
const nextIndex = this.nextPlayableSeatIndex(this.currentSeatIndex ?? -1);
if (nextIndex === null) {
this.clearTurnTimer();
await this.dealerTurn();
return;
}
this.currentSeatIndex = nextIndex;
this.startTurnTimer();
this.broadcastState();
}
async dealerTurn() {
this.clearTurnTimer();
this.phase = 'dealer';
this.currentSeatIndex = null;
@@ -423,6 +465,7 @@ class Table {
}
resetRound() {
this.clearTurnTimer();
for (const seat of this.seats) {
if (!seat.userId) {
continue;