reiniciar por room, finished ya no estorba, control total desde el dashboard

This commit is contained in:
2025-08-16 00:13:31 -06:00
parent cc0a628145
commit 63eb9b2c7e
5 changed files with 35 additions and 33 deletions

View File

@@ -110,6 +110,13 @@
<option value="G5">G5</option> <option value="G5">G5</option>
</select> </select>
</div> </div>
<button
@click="$emit('restartRoom', room.roomId)"
class="btn btn-restart"
title="Reiniciar estado (mantiene variante)"
>
🔄 Reiniciar
</button>
<button <button
@click="$emit('closeRoom', room.roomId)" @click="$emit('closeRoom', room.roomId)"
class="btn btn-close" class="btn btn-close"
@@ -168,6 +175,7 @@ defineEmits<{
viewRoomModal: [roomId: string]; viewRoomModal: [roomId: string];
closeRoom: [roomId: string]; closeRoom: [roomId: string];
changeVariant: [roomId: string, variant: string]; changeVariant: [roomId: string, variant: string];
restartRoom: [roomId: string];
}>(); }>();
function getRoomDetails(roomId: string) { function getRoomDetails(roomId: string) {
@@ -279,6 +287,19 @@ function getReadableTextColor(hex?: string): string {
transform: translateY(-1px); transform: translateY(-1px);
} }
.btn-restart {
background: #1976d2;
color: white;
padding: 6px 12px;
font-size: 12px;
margin-left: 4px;
}
.btn-restart:hover {
background: #1565c0;
transform: translateY(-1px);
}
.variant-selector-container { .variant-selector-container {
display: inline-block; display: inline-block;
margin: 0 4px; margin: 0 4px;

View File

@@ -160,6 +160,7 @@
@view-room-modal="openRoomModal" @view-room-modal="openRoomModal"
@close-room="closeRoom" @close-room="closeRoom"
@change-variant="changeRoomVariant" @change-variant="changeRoomVariant"
@restart-room="restartRoom"
/> />
<!-- Cards View --> <!-- Cards View -->

View File

@@ -527,14 +527,17 @@ adminRouter.get("/admin/uuids-with-names", async (req: Request, res: Response) =
// SSE endpoint for real-time dashboard updates // SSE endpoint for real-time dashboard updates
adminRouter.get("/dashboard-stream", (req: Request, res: Response) => { adminRouter.get("/dashboard-stream", (req: Request, res: Response) => {
// Set SSE headers // Set SSE headers (hardened for reverse proxies)
res.writeHead(200, { res.writeHead(200, {
'Content-Type': 'text/event-stream', 'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache', 'Cache-Control': 'no-cache, no-transform', // avoid proxy transformations
'Connection': 'keep-alive', 'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Cache-Control' 'Access-Control-Allow-Headers': 'Cache-Control',
'X-Accel-Buffering': 'no' // hint nginx to disable buffering
}); });
// Flush headers immediately so proxies establish the stream
try { (res as any).flushHeaders?.(); } catch {}
// Add client to our set // Add client to our set
sseClients.add(res); sseClients.add(res);

View File

@@ -73,13 +73,9 @@ export class GameRoom extends Room<GameState> {
// Reset to round 1 and clear decisions when variant changes // Reset to round 1 and clear decisions when variant changes
this.state.currentRound = 1; this.state.currentRound = 1;
this.state.resetRound(); this.state.resetRound();
// Reset game status if it was finished // Update metadata with new variant and round (don't special-case FINISHED)
if (this.state.gameStatus === GameStatus.FINISHED) {
this.state.gameStatus = GameStatus.PLAYING;
}
// Update metadata with new variant and round
this.setMetadata({ this.setMetadata({
gameStatus: this.state.gameStatus === GameStatus.WAITING ? 'waiting' : 'playing', gameStatus: this.state.gameStatus === GameStatus.WAITING ? 'waiting' : (this.state.gameStatus === GameStatus.PAUSED ? 'paused' : (this.state.gameStatus === GameStatus.FINISHED ? 'finished' : 'playing')),
currentRound: this.state.currentRound, currentRound: this.state.currentRound,
currentVariant: this.state.currentVariant currentVariant: this.state.currentVariant
}); });
@@ -478,20 +474,6 @@ export class GameRoom extends Room<GameState> {
}); });
// Notify dashboard of game end // Notify dashboard of game end
broadcastDashboardUpdate(); broadcastDashboardUpdate();
// Clear UUID -> current room mapping and reconnection tokens for participants
try {
const { NameManager } = require("../utils/nameManager");
const toClear: string[] = [];
this.state.players.forEach((p) => {
const u = (p as any)?.uuid;
if (u) toClear.push(u);
});
toClear.forEach(u => {
NameManager.getInstance().clearCurrentRoom(u);
NameManager.getInstance().clearReconnectToken(u);
});
} catch {}
} }
private resolveP2Action() { private resolveP2Action() {
@@ -583,12 +565,9 @@ export class GameRoom extends Room<GameState> {
this.state.currentRound = 1; this.state.currentRound = 1;
this.state.resetRound(); this.state.resetRound();
if (this.state.gameStatus === GameStatus.FINISHED) { // Update metadata without altering FINISHED status
this.state.gameStatus = GameStatus.PLAYING;
}
this.setMetadata({ this.setMetadata({
gameStatus: this.state.gameStatus === GameStatus.WAITING ? 'waiting' : 'playing', gameStatus: this.state.gameStatus === GameStatus.WAITING ? 'waiting' : (this.state.gameStatus === GameStatus.PAUSED ? 'paused' : (this.state.gameStatus === GameStatus.FINISHED ? 'finished' : 'playing')),
currentRound: this.state.currentRound, currentRound: this.state.currentRound,
currentVariant: this.state.currentVariant currentVariant: this.state.currentVariant
}); });

View File

@@ -80,8 +80,7 @@ export class LobbyRoom extends Room<LobbyState> {
if (currentRoomId) { if (currentRoomId) {
const rooms = await matchMaker.query({ roomId: currentRoomId }); const rooms = await matchMaker.query({ roomId: currentRoomId });
const room = rooms[0]; const room = rooms[0];
const status = room?.metadata?.gameStatus || "waiting"; if (room) {
if (room && status !== "finished") {
const token = NameManager.getInstance().getReconnectToken(uuid); const token = NameManager.getInstance().getReconnectToken(uuid);
if (token) { client.send("resumeReconnection", { token }); } if (token) { client.send("resumeReconnection", { token }); }
else { client.send("resumeGame", { roomId: currentRoomId }); } else { client.send("resumeGame", { roomId: currentRoomId }); }
@@ -146,8 +145,7 @@ export class LobbyRoom extends Room<LobbyState> {
if (currentRoomId) { if (currentRoomId) {
const rooms = await matchMaker.query({ roomId: currentRoomId }); const rooms = await matchMaker.query({ roomId: currentRoomId });
const room = rooms[0]; const room = rooms[0];
const status = room?.metadata?.gameStatus || "waiting"; if (room) {
if (room && status !== "finished") {
const token = NameManager.getInstance().getReconnectToken(options.uuid); const token = NameManager.getInstance().getReconnectToken(options.uuid);
if (token) { if (token) {
client.send("resumeReconnection", { token }); client.send("resumeReconnection", { token });