diff --git a/sockets/arena_socket.js b/sockets/arena_socket.js index 020de9e..b6f2b34 100644 --- a/sockets/arena_socket.js +++ b/sockets/arena_socket.js @@ -1,19 +1,19 @@ /* ============================================================ sockets/arena.js 1v1 Matchmaking + 2v2 Team-Lobby + 4v4 Team-Lobby - inkl. Kick-Funktion für Team-Leader + inkl. Kick-Funktion für Team-Leader wurde getestet ============================================================ */ -const waitingPool = new Map(); -const LEVEL_RANGE = 5; +const waitingPool = new Map(); +const LEVEL_RANGE = 5; const READY_TIMEOUT = 30; /* ── 2v2 State ── */ -const teams2v2 = new Map(); +const teams2v2 = new Map(); const readyTeams2v2 = new Map(); /* ── 4v4 State ── */ -const teams4v4 = new Map(); +const teams4v4 = new Map(); const readyTeams4v4 = new Map(); function generateId() { @@ -23,21 +23,27 @@ function generateId() { /* ═══════════════════════════════════════════════════════════ HELPER: Generisch für 2v2 UND 4v4 ═══════════════════════════════════════════════════════════ */ -function getTeamMap(mode) { return mode === "4v4" ? teams4v4 : teams2v2; } -function getReadyMap(mode) { return mode === "4v4" ? readyTeams4v4 : readyTeams2v2; } -function getMaxPlayers(mode) { return mode === "4v4" ? 4 : 2; } +function getTeamMap(mode) { + return mode === "4v4" ? teams4v4 : teams2v2; +} +function getReadyMap(mode) { + return mode === "4v4" ? readyTeams4v4 : readyTeams2v2; +} +function getMaxPlayers(mode) { + return mode === "4v4" ? 4 : 2; +} function broadcastLobbies(io, mode) { const teams = getTeamMap(mode); - const max = getMaxPlayers(mode); - const list = []; + const max = getMaxPlayers(mode); + const list = []; for (const [teamId, team] of teams) { if (team.players.length < max) { list.push({ teamId, - leader: team.players[0]?.player?.name || "?", + leader: team.players[0]?.player?.name || "?", leaderLevel: team.players[0]?.player?.level || 1, - count: team.players.length, + count: team.players.length, max, }); } @@ -50,15 +56,15 @@ function broadcastTeamStatus(io, teamId, mode) { if (!team) return; const data = { teamId, - leaderId: team.leaderId, // ← Leader-SocketId mitschicken - players: team.players.map(p => ({ - socketId: p.socketId, // ← für Kick-Button im Frontend - name: p.player.name, - level: p.player.level, - ready: team.ready.has(p.socketId), + leaderId: team.leaderId, // ← Leader-SocketId mitschicken + players: team.players.map((p) => ({ + socketId: p.socketId, // ← für Kick-Button im Frontend + name: p.player.name, + level: p.player.level, + ready: team.ready.has(p.socketId), })), count: team.players.length, - max: getMaxPlayers(mode), + max: getMaxPlayers(mode), }; for (const p of team.players) { io.to(p.socketId).emit(`${mode}_team_update`, data); @@ -66,7 +72,7 @@ function broadcastTeamStatus(io, teamId, mode) { } function tryMatchmaking_nvn(io, mode) { - const readyMap = getReadyMap(mode); + const readyMap = getReadyMap(mode); const readyList = Array.from(readyMap.values()); if (readyList.length < 2) return; @@ -78,17 +84,22 @@ function tryMatchmaking_nvn(io, mode) { getTeamMap(mode).delete(team1.id); getTeamMap(mode).delete(team2.id); - const matchId = `${mode}_${generateId()}`; + const matchId = `${mode}_${generateId()}`; const allPlayers = [ - ...team1.players.map(p => ({ team: 1, ...p })), - ...team2.players.map(p => ({ team: 2, ...p })), + ...team1.players.map((p) => ({ team: 1, ...p })), + ...team2.players.map((p) => ({ team: 2, ...p })), ]; for (const p of allPlayers) { - const opponents = allPlayers.filter(x => x.team !== p.team).map(x => x.player.name); - const teammates = allPlayers.filter(x => x.team === p.team && x.socketId !== p.socketId).map(x => x.player.name); - const teamRef = p.team === 1 ? team1 : team2; - const slotIndex = teamRef.players.findIndex(x => x.socketId === p.socketId) + 1; + const opponents = allPlayers + .filter((x) => x.team !== p.team) + .map((x) => x.player.name); + const teammates = allPlayers + .filter((x) => x.team === p.team && x.socketId !== p.socketId) + .map((x) => x.player.name); + const teamRef = p.team === 1 ? team1 : team2; + const slotIndex = + teamRef.players.findIndex((x) => x.socketId === p.socketId) + 1; io.to(p.socketId).emit(`match_found_${mode}`, { matchId, @@ -100,8 +111,8 @@ function tryMatchmaking_nvn(io, mode) { } console.log( - `[${mode}] Match: Team1(${team1.players.map(p => p.player.name)})` + - ` vs Team2(${team2.players.map(p => p.player.name)}) | ${matchId}` + `[${mode}] Match: Team1(${team1.players.map((p) => p.player.name)})` + + ` vs Team2(${team2.players.map((p) => p.player.name)}) | ${matchId}`, ); } @@ -110,7 +121,7 @@ function leaveAllTeams_nvn(socketId, io, mode) { const ready = getReadyMap(mode); for (const [teamId, team] of teams) { - const idx = team.players.findIndex(p => p.socketId === socketId); + const idx = team.players.findIndex((p) => p.socketId === socketId); if (idx === -1) continue; const playerName = team.players[idx].player.name; @@ -125,7 +136,9 @@ function leaveAllTeams_nvn(socketId, io, mode) { // Falls Leader das Team verlässt → nächsten Spieler zum Leader machen if (team.leaderId === socketId) { team.leaderId = team.players[0].socketId; - console.log(`[${mode}] Neuer Leader in Team ${teamId}: ${team.players[0].player.name}`); + console.log( + `[${mode}] Neuer Leader in Team ${teamId}: ${team.players[0].player.name}`, + ); } broadcastTeamStatus(io, teamId, mode); for (const p of team.players) { @@ -150,10 +163,20 @@ function tryMatchmaking(io, newSocketId) { waitingPool.delete(id); const matchId = `match_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`; - challenger.socket.emit("match_found", { matchId, opponent: entry.player, mySlot: "player1" }); - entry.socket.emit("match_found", { matchId, opponent: challenger.player, mySlot: "player2" }); + challenger.socket.emit("match_found", { + matchId, + opponent: entry.player, + mySlot: "player1", + }); + entry.socket.emit("match_found", { + matchId, + opponent: challenger.player, + mySlot: "player2", + }); - console.log(`[1v1] ${challenger.player.name} vs ${entry.player.name} | ${matchId}`); + console.log( + `[1v1] ${challenger.player.name} vs ${entry.player.name} | ${matchId}`, + ); return; } } @@ -174,7 +197,10 @@ function startReadyTimer(io, matchId) { clearInterval(interval); io._arenaTimers.delete(matchId); console.log(`[1v1] Match ${matchId} abgebrochen – Zeit abgelaufen.`); - io.to("arena_" + matchId).emit("match_cancelled", { reason: "timeout", message: "Zeit abgelaufen." }); + io.to("arena_" + matchId).emit("match_cancelled", { + reason: "timeout", + message: "Zeit abgelaufen.", + }); } }, 1000); @@ -199,14 +225,14 @@ function registerTeamModeHandlers(io, socket, mode) { /* Lobby-Liste anfordern */ socket.on(`get_${mode}_lobbies`, () => { const teams = getTeamMap(mode); - const list = []; + const list = []; for (const [teamId, team] of teams) { if (team.players.length < max) { list.push({ teamId, - leader: team.players[0]?.player?.name || "?", + leader: team.players[0]?.player?.name || "?", leaderLevel: team.players[0]?.player?.level || 1, - count: team.players.length, + count: team.players.length, max, }); } @@ -218,14 +244,18 @@ function registerTeamModeHandlers(io, socket, mode) { socket.on(`create_${mode}_team`, (playerData) => { leaveAllTeams_nvn(socket.id, io, mode); - const player = { id: playerData.id, name: playerData.name, level: Number(playerData.level) || 1 }; + const player = { + id: playerData.id, + name: playerData.name, + level: Number(playerData.level) || 1, + }; const teamId = `team_${mode}_${generateId()}`; getTeamMap(mode).set(teamId, { - id: teamId, - leaderId: socket.id, // ← Ersteller wird Leader - players: [{ socketId: socket.id, player }], - ready: new Set(), + id: teamId, + leaderId: socket.id, // ← Ersteller wird Leader + players: [{ socketId: socket.id, player }], + ready: new Set(), }); socket.emit(`${mode}_team_joined`, { teamId, isLeader: true }); @@ -239,13 +269,23 @@ function registerTeamModeHandlers(io, socket, mode) { const { teamId, playerData } = data; const team = getTeamMap(mode).get(teamId); - if (!team) return socket.emit(`${mode}_error`, { message: "Team nicht mehr verfügbar." }); - if (team.players.length >= max) return socket.emit(`${mode}_error`, { message: "Team ist bereits voll." }); - if (team.players.find(p => p.socketId===socket.id)) return; + if (!team) + return socket.emit(`${mode}_error`, { + message: "Team nicht mehr verfügbar.", + }); + if (team.players.length >= max) + return socket.emit(`${mode}_error`, { + message: "Team ist bereits voll.", + }); + if (team.players.find((p) => p.socketId === socket.id)) return; leaveAllTeams_nvn(socket.id, io, mode); - const player = { id: playerData.id, name: playerData.name, level: Number(playerData.level) || 1 }; + const player = { + id: playerData.id, + name: playerData.name, + level: Number(playerData.level) || 1, + }; team.players.push({ socketId: socket.id, player }); socket.emit(`${mode}_team_joined`, { teamId, isLeader: false }); @@ -268,13 +308,15 @@ function registerTeamModeHandlers(io, socket, mode) { // Nur der Leader darf kicken if (team.leaderId !== socket.id) { - return socket.emit(`${mode}_error`, { message: "Nur der Team-Leader kann Spieler entfernen." }); + return socket.emit(`${mode}_error`, { + message: "Nur der Team-Leader kann Spieler entfernen.", + }); } // Sich selbst kicken nicht erlaubt if (targetSocketId === socket.id) return; - const idx = team.players.findIndex(p => p.socketId === targetSocketId); + const idx = team.players.findIndex((p) => p.socketId === targetSocketId); if (idx === -1) return; const kickedName = team.players[idx].player.name; @@ -300,12 +342,16 @@ function registerTeamModeHandlers(io, socket, mode) { team.ready.add(socket.id); broadcastTeamStatus(io, teamId, mode); - console.log(`[${mode}] Bereit in Team ${teamId}: ${team.ready.size}/${max}`); + console.log( + `[${mode}] Bereit in Team ${teamId}: ${team.ready.size}/${max}`, + ); if (team.ready.size >= max) { getReadyMap(mode).set(teamId, team); for (const p of team.players) { - io.to(p.socketId).emit(`${mode}_searching`, { message: "Suche nach Gegnerteam…" }); + io.to(p.socketId).emit(`${mode}_searching`, { + message: "Suche nach Gegnerteam…", + }); } console.log(`[${mode}] Team ${teamId} sucht Gegner.`); tryMatchmaking_nvn(io, mode); @@ -317,54 +363,71 @@ function registerTeamModeHandlers(io, socket, mode) { HAUPT-HANDLER ═══════════════════════════════════════════════════════════ */ function registerArenaHandlers(io, socket) { - /* ── 1v1 ── */ socket.on("join_1v1", (playerData) => { if (waitingPool.has(socket.id)) return; - const player = { id: playerData.id, name: playerData.name, level: Number(playerData.level) || 1 }; + const player = { + id: playerData.id, + name: playerData.name, + level: Number(playerData.level) || 1, + }; waitingPool.set(socket.id, { socket, player }); - socket.emit("queue_status", { status: "waiting", poolSize: waitingPool.size }); - console.log(`[1v1] ${player.name} (Lvl ${player.level}) im Pool. Größe: ${waitingPool.size}`); + socket.emit("queue_status", { + status: "waiting", + poolSize: waitingPool.size, + }); + console.log( + `[1v1] ${player.name} (Lvl ${player.level}) im Pool. Größe: ${waitingPool.size}`, + ); tryMatchmaking(io, socket.id); }); socket.on("leave_1v1", () => { - if (waitingPool.delete(socket.id)) socket.emit("queue_status", { status: "left" }); + if (waitingPool.delete(socket.id)) + socket.emit("queue_status", { status: "left" }); }); socket.on("arena_join", (data) => { const { matchId, slot, playerName } = data; - console.log(`[1v1] arena_join empfangen: matchId=${matchId}, slot=${slot}, name=${playerName}, socketId=${socket.id}`); + console.log( + `[1v1] arena_join empfangen: matchId=${matchId}, slot=${slot}, name=${playerName}, socketId=${socket.id}`, + ); if (!matchId || !slot) { console.warn(`[1v1] arena_join abgewiesen – matchId oder slot fehlt`); return; } if (!io._arenaRooms) io._arenaRooms = new Map(); - if (!io._arenaRooms.has(matchId)) io._arenaRooms.set(matchId, { sockets: {}, names: {} }); + if (!io._arenaRooms.has(matchId)) + io._arenaRooms.set(matchId, { sockets: {}, names: {} }); const room = io._arenaRooms.get(matchId); room.sockets[slot] = socket.id; // Name aus socket.user (Objekt) oder vom Client mitgesendet const u = socket.user; - room.names[slot] = (u && (u.ingame_name || u.username || u.name)) - || playerName - || "Spieler"; + room.names[slot] = + (u && (u.ingame_name || u.username || u.name)) || playerName || "Spieler"; socket.join("arena_" + matchId); const otherSlot = slot === "player1" ? "player2" : "player1"; if (room.sockets[otherSlot]) { - console.log(`[1v1] Beide Spieler da → arena_ready senden | Match ${matchId}`); + console.log( + `[1v1] Beide Spieler da → arena_ready senden | Match ${matchId}`, + ); io.to("arena_" + matchId).emit("arena_ready", { player1: room.names["player1"] || "Spieler 1", player2: room.names["player2"] || "Spieler 2", }); startReadyTimer(io, matchId); } else { - console.log(`[1v1] Erster Spieler joined, warte auf zweiten | Match ${matchId}`); - socket.to("arena_" + matchId).emit("arena_opponent_joined", { name: room.names[slot], slot }); + console.log( + `[1v1] Erster Spieler joined, warte auf zweiten | Match ${matchId}`, + ); + socket + .to("arena_" + matchId) + .emit("arena_opponent_joined", { name: room.names[slot], slot }); } }); @@ -375,8 +438,14 @@ function registerArenaHandlers(io, socket) { if (!io._arenaReady.has(matchId)) io._arenaReady.set(matchId, new Set()); const readySet = io._arenaReady.get(matchId); readySet.add(slot); - io.to("arena_" + matchId).emit("ready_status", { readyCount: readySet.size, readySlots: Array.from(readySet) }); - if (readySet.size >= 2) { stopReadyTimer(io, matchId); io._arenaReady.delete(matchId); } + io.to("arena_" + matchId).emit("ready_status", { + readyCount: readySet.size, + readySlots: Array.from(readySet), + }); + if (readySet.size >= 2) { + stopReadyTimer(io, matchId); + io._arenaReady.delete(matchId); + } }); socket.on("player_surrender", (data) => {