From 5e42eb7d5476a7621e8f30fdf37e92aaaf109625 Mon Sep 17 00:00:00 2001 From: josedario87 Date: Fri, 15 Aug 2025 19:09:49 -0600 Subject: [PATCH] colores asignados al azar y simulador de jugadores en el dashboard --- client/src/services/colyseus.ts | 11 +++++ client/src/views/Dashboard.vue | 72 +++++++++++++++++++++++++++++++- server/src/adminApi.ts | 14 ++++++- server/src/rooms/LobbyRoom.ts | 41 ++++++++++++++---- server/src/utils/uuidRegistry.ts | 4 ++ 5 files changed, 131 insertions(+), 11 deletions(-) diff --git a/client/src/services/colyseus.ts b/client/src/services/colyseus.ts index 80650bf..84475cb 100644 --- a/client/src/services/colyseus.ts +++ b/client/src/services/colyseus.ts @@ -331,6 +331,17 @@ class ColyseusService { return room; } + async fetchAllowedUuids(): Promise { + try { + const res = await fetch(`${this.apiBase}/admin/uuids`); + const data = await res.json(); + return Array.isArray(data?.uuids) ? data.uuids : []; + } catch (e) { + console.error('Failed to fetch allowed UUIDs', e); + return []; + } + } + private getUuidFromPath(): string { if (typeof window === 'undefined') return ''; const path = window.location.pathname.replace(/^\/+/, ''); diff --git a/client/src/views/Dashboard.vue b/client/src/views/Dashboard.vue index dd4f013..cd003ef 100644 --- a/client/src/views/Dashboard.vue +++ b/client/src/views/Dashboard.vue @@ -25,6 +25,29 @@
+ +
+
+

🧪 Abrir Pestañas de Jugadores

+
+
+ + + + + Abre /{uuid} en nuevas pestañas. +
+
@@ -213,13 +236,26 @@ const maxReconnectAttempts = 5; const selectedGlobalVariant = ref(''); const isLoadingGlobal = ref(false); +// Open tabs UI state +const allowedUuids = ref([]); +const openCount = ref<1|2|6|10>(1); +const selectedUuid = ref(''); + const gameRooms = computed(() => rooms.value.filter(r => r.name === 'game')); const lobbyRooms = computed(() => rooms.value.filter(r => r.name === 'lobby')); const totalPlayers = computed(() => rooms.value.reduce((sum, room) => sum + room.clients, 0)); -onMounted(() => { +onMounted(async () => { // Try SSE first, fallback to polling if it fails initSSE(); + // Load allowed UUIDs + try { + const list = await colyseusService.fetchAllowedUuids(); + allowedUuids.value = list || []; + if (!selectedUuid.value && allowedUuids.value.length > 0) { + selectedUuid.value = allowedUuids.value[0]; + } + } catch {} }); onUnmounted(() => { @@ -302,6 +338,31 @@ function goToLobby() { router.push('/'); } +function openTabs() { + const base = window.location.origin; + const openOne = (uuid: string) => { + const url = `${base}/${uuid}`; + window.open(url, '_blank'); + }; + + if (openCount.value === 1) { + if (!selectedUuid.value) return; + openOne(selectedUuid.value); + return; + } + + // Randomly select N distinct UUIDs + const N = openCount.value as number; + const pool = [...allowedUuids.value]; + if (pool.length === 0) return; + for (let i = pool.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [pool[i], pool[j]] = [pool[j], pool[i]]; + } + const pick = pool.slice(0, Math.min(N, pool.length)); + pick.forEach(u => openOne(u)); +} + // Global control functions async function pauseAllGames() { if (!confirm('Are you sure you want to pause ALL active games?')) return; @@ -649,6 +710,13 @@ const selectedRoom = computed(() => { margin: 0 auto; } +.open-tabs-section { margin: 16px 0 24px; padding: 12px; background:#f8f9fa; border-radius: 10px; color:#333; } +.open-tabs-controls { display:flex; flex-wrap:wrap; gap:10px; align-items:center; } +.open-tabs-controls .select { padding:6px 10px; border-radius:6px; border:1px solid #ddd; } +.btn-open { background:#2196f3; color:white; padding: 8px 12px; border:none; border-radius:8px; cursor:pointer; } +.btn-open:hover { background:#1976d2; } +.hint { color:#666; font-size:12px; } + .global-controls-section { margin-bottom: 40px; } @@ -860,4 +928,4 @@ const selectedRoom = computed(() => { .btn-lobby:hover { background: rgba(255, 255, 255, 0.3); } - \ No newline at end of file + diff --git a/server/src/adminApi.ts b/server/src/adminApi.ts index 0fb1297..f982eeb 100644 --- a/server/src/adminApi.ts +++ b/server/src/adminApi.ts @@ -2,6 +2,7 @@ import { Request, Response, Router } from "express"; import { matchMaker } from "colyseus"; import { GameRoom } from "./rooms/GameRoom"; import { NameManager } from "./utils/nameManager"; +import { getAllowedUuidCount, listAllowedUuids } from "./utils/uuidRegistry"; // SSE connections storage const sseClients = new Set(); @@ -432,6 +433,17 @@ adminRouter.post("/admin/shuffle-players", async (req: Request, res: Response) = } }); +// UUID allowlist endpoint +adminRouter.get("/admin/uuids", async (req: Request, res: Response) => { + try { + const uuids = listAllowedUuids(); + res.json({ count: getAllowedUuidCount(), uuids }); + } catch (error) { + console.error("[AdminAPI] Error fetching UUIDs:", error); + res.status(500).json({ error: "Failed to fetch UUIDs" }); + } +}); + // SSE endpoint for real-time dashboard updates adminRouter.get("/dashboard-stream", (req: Request, res: Response) => { // Set SSE headers @@ -558,4 +570,4 @@ function broadcastDashboardUpdate() { sendDashboardUpdate(); } -export { adminRouter, broadcastDashboardUpdate }; \ No newline at end of file +export { adminRouter, broadcastDashboardUpdate }; diff --git a/server/src/rooms/LobbyRoom.ts b/server/src/rooms/LobbyRoom.ts index 31d6322..ed60577 100644 --- a/server/src/rooms/LobbyRoom.ts +++ b/server/src/rooms/LobbyRoom.ts @@ -6,6 +6,23 @@ import { isUuidAllowed } from "../utils/uuidRegistry"; export class LobbyRoom extends Room { private updateInterval?: NodeJS.Timeout; private sessionToUuid: Map = new Map(); + + // Generate a random dark-ish color (for white backgrounds) + private randomDarkHex(): string { + const h = Math.floor(Math.random() * 360); + const s = 65 + Math.floor(Math.random() * 20); // 65% - 85% + const l = 30 + Math.floor(Math.random() * 10); // 30% - 40% + return this.hslToHex(h, s, l); + } + + private hslToHex(h: number, s: number, l: number): string { + s /= 100; l /= 100; + const k = (n: number) => (n + h / 30) % 12; + const a = s * Math.min(l, 1 - l); + const f = (n: number) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1))); + const toHex = (x: number) => Math.round(255 * x).toString(16).padStart(2, '0'); + return `#${toHex(f(0))}${toHex(f(8))}${toHex(f(4))}`; + } onCreate(options: any) { this.setState(new LobbyState()); @@ -80,10 +97,14 @@ export class LobbyRoom extends Room { // Add player temporarily to lobby state const existingName = NameManager.getInstance().getPlayerName(options.uuid); this.state.addPlayer(client.sessionId, existingName || ""); - const existingColor = NameManager.getInstance().getPlayerColor(options.uuid); - if (existingColor) { - const p = this.state.players.get(client.sessionId); - if (p) p.color = existingColor; + const p = this.state.players.get(client.sessionId); + if (p) { + let color = NameManager.getInstance().getPlayerColor(options.uuid); + if (!color) { + color = this.randomDarkHex(); + NameManager.getInstance().setPlayerColor(options.uuid, color); + } + p.color = color; } // Send welcome first @@ -106,10 +127,14 @@ export class LobbyRoom extends Room { // Check if this UUID already has a name const existingName = NameManager.getInstance().getPlayerName(options.uuid); this.state.addPlayer(client.sessionId, existingName || ""); - const existingColor = NameManager.getInstance().getPlayerColor(options.uuid); - if (existingColor) { - const p = this.state.players.get(client.sessionId); - if (p) p.color = existingColor; + const p = this.state.players.get(client.sessionId); + if (p) { + let color = NameManager.getInstance().getPlayerColor(options.uuid); + if (!color) { + color = this.randomDarkHex(); + NameManager.getInstance().setPlayerColor(options.uuid, color); + } + p.color = color; } client.send("welcome", { diff --git a/server/src/utils/uuidRegistry.ts b/server/src/utils/uuidRegistry.ts index 8a1390e..c365214 100644 --- a/server/src/utils/uuidRegistry.ts +++ b/server/src/utils/uuidRegistry.ts @@ -62,3 +62,7 @@ export function isUuidAllowed(uuid: string | undefined | null): boolean { export function getAllowedUuidCount(): number { return allowedUuids.size; } + +export function listAllowedUuids(): string[] { + return Array.from(allowedUuids.values()); +}