From 5940dfb01a6cbc8f6f86a9bc8c5a70663d85f3d3 Mon Sep 17 00:00:00 2001 From: cay Date: Wed, 8 Apr 2026 17:02:16 +0100 Subject: [PATCH] fxghjnyxdf --- sockets/arena_socket.js | 504 +++++++++++++--------------------------- 1 file changed, 167 insertions(+), 337 deletions(-) diff --git a/sockets/arena_socket.js b/sockets/arena_socket.js index 2f634ec..27e4237 100644 --- a/sockets/arena_socket.js +++ b/sockets/arena_socket.js @@ -1,6 +1,6 @@ /* ============================================================ sockets/arena.js - Alle Socket-Events rund um 1v1 Matchmaking, Spielfeld & Bereit-System + Alle Socket-Events rund um 1v1 Matchmaking & 2v2 Team-Lobby ============================================================ */ const waitingPool = new Map(); // socketId → { socket, player } @@ -8,37 +8,43 @@ const LEVEL_RANGE = 5; // 2v2 Team-Lobby // teams2v2: teamId → { id, players: [{socketId, player}], ready: Set } -const teams2v2 = new Map(); -const readyTeams = new Map(); // teamId → team (fertige Teams warten auf Matchmaking) +const teams2v2 = new Map(); +const readyTeams = new Map(); // teamId → team (fertige Teams warten auf Matchmaking) + +const READY_TIMEOUT = 30; // Sekunden bis Match abgebrochen wird function generateId() { return `${Date.now()}_${Math.random().toString(36).slice(2, 7)}`; } -/* ── 2v2 Lobby-Liste an alle schicken ── */ +/* ───────────────────────────────────────────────── + HELPER: 2v2 Lobby-Liste an alle senden +───────────────────────────────────────────────── */ function broadcastLobbies(io) { const list = []; for (const [teamId, team] of teams2v2) { if (team.players.length < 2) { 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, }); } } io.emit("2v2_lobbies", list); } -/* ── 2v2 Team-Status an Teammitglieder senden ── */ +/* ───────────────────────────────────────────────── + HELPER: 2v2 Team-Status an Teammitglieder senden +───────────────────────────────────────────────── */ function broadcastTeamStatus(io, teamId) { const team = teams2v2.get(teamId); if (!team) return; const data = { teamId, players: team.players.map(p => ({ - name: p.player.name, + name: p.player.name, level: p.player.level, ready: team.ready.has(p.socketId), })), @@ -49,7 +55,9 @@ function broadcastTeamStatus(io, teamId) { } } -/* ── 2v2 Matchmaking ── */ +/* ───────────────────────────────────────────────── + HELPER: 2v2 Matchmaking +───────────────────────────────────────────────── */ function tryMatchmaking2v2(io) { const readyList = Array.from(readyTeams.values()); if (readyList.length < 2) return; @@ -74,25 +82,64 @@ function tryMatchmaking2v2(io) { 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; + io.to(p.socketId).emit("match_found_2v2", { matchId, myTeam: p.team, teammates, opponents, - mySlot: `team${p.team}_player${team1.players.indexOf(p) >= 0 ? team1.players.findIndex(x=>x.socketId===p.socketId)+1 : team2.players.findIndex(x=>x.socketId===p.socketId)+1}`, + mySlot: `team${p.team}_player${slotIndex}`, }); } - console.log(`[2v2] Match: Team1(${team1.players.map(p=>p.player.name)}) vs Team2(${team2.players.map(p=>p.player.name)}) | ${matchId}`); + console.log( + `[2v2] Match: Team1(${team1.players.map(p => p.player.name)})` + + ` vs Team2(${team2.players.map(p => p.player.name)}) | ${matchId}` + ); } -// Werden beim ersten Event lazy initialisiert (auf io gespeichert) -// io._arenaRooms → matchId → { sockets, names } -// io._arenaReady → matchId → Set of ready slots -// io._arenaTimers → matchId → intervalId +/* ───────────────────────────────────────────────── + HELPER: 1v1 Matchmaking +───────────────────────────────────────────────── */ +function tryMatchmaking(io, newSocketId) { + const challenger = waitingPool.get(newSocketId); + if (!challenger) return; -const READY_TIMEOUT = 30; // Sekunden bis Match abgebrochen wird + for (const [id, entry] of waitingPool) { + if (id === newSocketId) continue; + const levelDiff = Math.abs(entry.player.level - challenger.player.level); + if (levelDiff <= LEVEL_RANGE) { + waitingPool.delete(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", + }); + + console.log( + `[1v1] Match: ${challenger.player.name} (Lvl ${challenger.player.level})` + + ` vs ${entry.player.name} (Lvl ${entry.player.level}) | ${matchId}` + ); + return; + } + } +} + +/* ───────────────────────────────────────────────── + HELPER: Bereit-Timer (1v1) +───────────────────────────────────────────────── */ function startReadyTimer(io, matchId) { if (!io._arenaTimers) io._arenaTimers = new Map(); if (io._arenaTimers.has(matchId)) return; @@ -109,7 +156,7 @@ function startReadyTimer(io, matchId) { io._arenaTimers.delete(matchId); console.log(`[1v1] Match ${matchId} abgebrochen – Zeit abgelaufen.`); io.to("arena_" + matchId).emit("match_cancelled", { - reason: "timeout", + reason: "timeout", message: "Zeit abgelaufen – Match wird abgebrochen.", }); } @@ -128,41 +175,37 @@ function stopReadyTimer(io, matchId) { } } -function tryMatchmaking(io, newSocketId) { - const challenger = waitingPool.get(newSocketId); - if (!challenger) return; +/* ───────────────────────────────────────────────── + HELPER: Spieler aus allen 2v2-Teams entfernen +───────────────────────────────────────────────── */ +function leaveAllTeams(socketId, io) { + for (const [teamId, team] of teams2v2) { + const idx = team.players.findIndex(p => p.socketId === socketId); + if (idx === -1) continue; - for (const [id, entry] of waitingPool) { - if (id === newSocketId) continue; + const playerName = team.players[idx].player.name; + team.players.splice(idx, 1); + team.ready.delete(socketId); + readyTeams.delete(teamId); - const levelDiff = Math.abs(entry.player.level - challenger.player.level); - if (levelDiff <= LEVEL_RANGE) { - waitingPool.delete(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", - }); - - console.log( - `[1v1] Match: ${challenger.player.name} (Lvl ${challenger.player.level})` + - ` vs ${entry.player.name} (Lvl ${entry.player.level}) | ${matchId}` - ); - return; + if (team.players.length === 0) { + teams2v2.delete(teamId); + console.log(`[2v2] Team ${teamId} aufgelöst.`); + } else { + broadcastTeamStatus(io, teamId); + for (const p of team.players) { + io.to(p.socketId).emit("2v2_partner_left", { name: playerName }); + } + console.log(`[2v2] ${playerName} hat Team ${teamId} verlassen.`); } + broadcastLobbies(io); + break; } } +/* ═══════════════════════════════════════════════════════════ + HAUPT-HANDLER – wird einmal pro Socket-Verbindung gerufen +═══════════════════════════════════════════════════════════ */ function registerArenaHandlers(io, socket) { /* ── 1v1: Queue beitreten ── */ @@ -176,7 +219,6 @@ function registerArenaHandlers(io, socket) { }; waitingPool.set(socket.id, { socket, player }); - socket.emit("queue_status", { status: "waiting", poolSize: waitingPool.size, @@ -195,9 +237,70 @@ function registerArenaHandlers(io, socket) { } }); - /* ══════════════════════════════════════════ + /* ── 1v1: Spielfeld betreten ── */ + socket.on("arena_join", (data) => { + const { matchId, slot } = data; + if (!matchId || !slot) return; + + if (!io._arenaRooms) io._arenaRooms = new Map(); + if (!io._arenaRooms.has(matchId)) { + io._arenaRooms.set(matchId, { sockets: {}, names: {} }); + } + + const room = io._arenaRooms.get(matchId); + room.sockets[slot] = socket.id; + room.names[slot] = socket.user || "Spieler"; + socket.join("arena_" + matchId); + + const otherSlot = slot === "player1" ? "player2" : "player1"; + if (room.sockets[otherSlot]) { + io.to("arena_" + matchId).emit("arena_ready", { + player1: room.names["player1"] || "Spieler 1", + player2: room.names["player2"] || "Spieler 2", + }); + console.log(`[Arena] Match ${matchId} bereit: ${room.names["player1"]} vs ${room.names["player2"]}`); + startReadyTimer(io, matchId); + } else { + socket.to("arena_" + matchId).emit("arena_opponent_joined", { + name: room.names[slot], + slot, + }); + } + }); + + /* ── 1v1: Bereit-System ── */ + socket.on("player_ready", (data) => { + const { matchId, slot } = data; + if (!matchId || !slot) return; + + if (!io._arenaReady) io._arenaReady = new Map(); + 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), + }); + console.log(`[1v1] ${slot} bereit in ${matchId} (${readySet.size}/2)`); + + if (readySet.size >= 2) { + stopReadyTimer(io, matchId); + io._arenaReady.delete(matchId); + } + }); + + /* ── 1v1: Aufgeben ── */ + socket.on("player_surrender", (data) => { + const { matchId, slot } = data; + console.log(`[1v1] ${slot} hat aufgegeben in Match ${matchId}`); + io.to("arena_" + matchId).emit("player_surrendered", { slot }); + }); + + /* ════════════════════════════════════════════ 2v2 TEAM LOBBY - ══════════════════════════════════════════ */ + ════════════════════════════════════════════ */ /* ── Lobby-Liste anfordern ── */ socket.on("get_2v2_lobbies", () => { @@ -206,9 +309,9 @@ function registerArenaHandlers(io, socket) { if (team.players.length < 2) { 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, }); } } @@ -217,16 +320,19 @@ function registerArenaHandlers(io, socket) { /* ── Neues Team erstellen ── */ socket.on("create_2v2_team", (playerData) => { - // Erst aus alten Teams entfernen leaveAllTeams(socket.id, io); - 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_${generateId()}`; teams2v2.set(teamId, { - id: teamId, + id: teamId, players: [{ socketId: socket.id, player }], - ready: new Set(), + ready: new Set(), }); socket.emit("2v2_team_joined", { teamId, isLeader: true }); @@ -246,12 +352,16 @@ function registerArenaHandlers(io, socket) { leaveAllTeams(socket.id, io); - 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("2v2_team_joined", { teamId, isLeader: false }); broadcastTeamStatus(io, teamId); - broadcastLobbies(io); // Team aus offener Liste entfernen + broadcastLobbies(io); console.log(`[2v2] ${player.name} hat Team ${teamId} beigetreten`); }); @@ -268,10 +378,8 @@ function registerArenaHandlers(io, socket) { team.ready.add(socket.id); broadcastTeamStatus(io, teamId); - console.log(`[2v2] Bereit in Team ${teamId}: ${team.ready.size}/2`); - // Beide bereit → Team in Matchmaking-Pool if (team.ready.size >= 2) { readyTeams.set(teamId, team); for (const p of team.players) { @@ -282,72 +390,6 @@ function registerArenaHandlers(io, socket) { } }); - /* ── Spielfeld: Spieler betritt Arena-Room ── */ - socket.on("arena_join", (data) => { - const { matchId, slot } = data; - if (!matchId || !slot) return; - - if (!io._arenaRooms) io._arenaRooms = new Map(); - if (!io._arenaRooms.has(matchId)) { - io._arenaRooms.set(matchId, { sockets: {}, names: {} }); - } - - const room = io._arenaRooms.get(matchId); - room.sockets[slot] = socket.id; - room.names[slot] = socket.user || "Spieler"; - - socket.join("arena_" + matchId); - - const otherSlot = slot === "player1" ? "player2" : "player1"; - - if (room.sockets[otherSlot]) { - io.to("arena_" + matchId).emit("arena_ready", { - player1: room.names["player1"] || "Spieler 1", - player2: room.names["player2"] || "Spieler 2", - }); - console.log(`[Arena] Match ${matchId} bereit: ${room.names["player1"]} vs ${room.names["player2"]}`); - startReadyTimer(io, matchId); - } else { - socket.to("arena_" + matchId).emit("arena_opponent_joined", { - name: room.names[slot], - slot, - }); - } - }); - - /* ── Bereit-System ── */ - socket.on("player_ready", (data) => { - const { matchId, slot } = data; - if (!matchId || !slot) return; - - if (!io._arenaReady) io._arenaReady = new Map(); - 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), - }); - - console.log(`[1v1] ${slot} bereit in ${matchId} (${readySet.size}/2)`); - - if (readySet.size >= 2) { - stopReadyTimer(io, matchId); - io._arenaReady.delete(matchId); - } - }); - - /* ── Aufgeben ── */ - socket.on("player_surrender", (data) => { - const { matchId, slot } = data; - console.log(`[1v1] ${slot} hat aufgegeben in Match ${matchId}`); - io.to("arena_" + matchId).emit("player_surrendered", { slot }); - }); - /* ── Disconnect ── */ socket.on("disconnect", () => { if (waitingPool.delete(socket.id)) { @@ -357,216 +399,4 @@ function registerArenaHandlers(io, socket) { }); } -/* ── Hilfsfunktion: Spieler aus allen 2v2-Teams entfernen ── */ -function leaveAllTeams(socketId, io) { - for (const [teamId, team] of teams2v2) { - const idx = team.players.findIndex(p => p.socketId === socketId); - if (idx === -1) continue; - - const playerName = team.players[idx].player.name; - team.players.splice(idx, 1); - team.ready.delete(socketId); - readyTeams.delete(teamId); - - if (team.players.length === 0) { - teams2v2.delete(teamId); - console.log(`[2v2] Team ${teamId} aufgelöst.`); - } else { - // Verbleibenden Spieler informieren - broadcastTeamStatus(io, teamId); - for (const p of team.players) { - io.to(p.socketId).emit("2v2_partner_left", { name: playerName }); - } - console.log(`[2v2] ${playerName} hat Team ${teamId} verlassen.`); - } - broadcastLobbies(io); - break; - } -} - -module.exports = { registerArenaHandlers }; - -function startReadyTimer(io, matchId) { - if (!io._arenaTimers) io._arenaTimers = new Map(); - if (io._arenaTimers.has(matchId)) return; // läuft bereits - - let remaining = READY_TIMEOUT; - - // Sofort ersten Tick senden - io.to("arena_" + matchId).emit("ready_timer", { remaining }); - - const interval = setInterval(() => { - remaining--; - io.to("arena_" + matchId).emit("ready_timer", { remaining }); - - if (remaining <= 0) { - clearInterval(interval); - io._arenaTimers.delete(matchId); - - // Match abbrechen – Funktion noch offen - console.log(`[1v1] Match ${matchId} abgebrochen – Zeit abgelaufen.`); - io.to("arena_" + matchId).emit("match_cancelled", { - reason: "timeout", - message: "Zeit abgelaufen – Match wird abgebrochen.", - }); - } - }, 1000); - - io._arenaTimers.set(matchId, interval); -} - -function stopReadyTimer(io, matchId) { - if (!io._arenaTimers) return; - const interval = io._arenaTimers.get(matchId); - if (interval) { - clearInterval(interval); - io._arenaTimers.delete(matchId); - console.log(`[1v1] Timer für Match ${matchId} gestoppt (beide bereit).`); - } -} - -function tryMatchmaking(io, newSocketId) { - const challenger = waitingPool.get(newSocketId); - if (!challenger) return; - - for (const [id, entry] of waitingPool) { - if (id === newSocketId) continue; - - const levelDiff = Math.abs(entry.player.level - challenger.player.level); - if (levelDiff <= LEVEL_RANGE) { - waitingPool.delete(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", - }); - - console.log( - `[1v1] Match: ${challenger.player.name} (Lvl ${challenger.player.level})` + - ` vs ${entry.player.name} (Lvl ${entry.player.level}) | ${matchId}` - ); - return; - } - } -} - -function registerArenaHandlers(io, socket) { - - /* ── Queue beitreten ── */ - socket.on("join_1v1", (playerData) => { - if (waitingPool.has(socket.id)) return; - - 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, - message: `Suche Gegner (Level ${player.level - LEVEL_RANGE}–${player.level + LEVEL_RANGE})…`, - }); - - console.log(`[1v1] ${player.name} (Lvl ${player.level}) im Pool. Größe: ${waitingPool.size}`); - tryMatchmaking(io, socket.id); - }); - - /* ── Queue verlassen ── */ - socket.on("leave_1v1", () => { - if (waitingPool.delete(socket.id)) { - socket.emit("queue_status", { status: "left" }); - console.log(`[1v1] ${socket.id} hat Pool verlassen.`); - } - }); - - /* ── Spielfeld: Spieler betritt Arena-Room ── */ - socket.on("arena_join", (data) => { - const { matchId, slot } = data; - if (!matchId || !slot) return; - - if (!io._arenaRooms) io._arenaRooms = new Map(); - if (!io._arenaRooms.has(matchId)) { - io._arenaRooms.set(matchId, { sockets: {}, names: {} }); - } - - const room = io._arenaRooms.get(matchId); - room.sockets[slot] = socket.id; - room.names[slot] = socket.user || "Spieler"; - - socket.join("arena_" + matchId); - - const otherSlot = slot === "player1" ? "player2" : "player1"; - - if (room.sockets[otherSlot]) { - io.to("arena_" + matchId).emit("arena_ready", { - player1: room.names["player1"] || "Spieler 1", - player2: room.names["player2"] || "Spieler 2", - }); - console.log(`[Arena] Match ${matchId} bereit: ${room.names["player1"]} vs ${room.names["player2"]}`); - - // 30-Sekunden Bereit-Timer starten - startReadyTimer(io, matchId); - } else { - socket.to("arena_" + matchId).emit("arena_opponent_joined", { - name: room.names[slot], - slot, - }); - } - }); - - /* ── Bereit-System ── */ - socket.on("player_ready", (data) => { - const { matchId, slot } = data; - if (!matchId || !slot) return; - - if (!io._arenaReady) io._arenaReady = new Map(); - 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), - }); - - console.log(`[1v1] ${slot} bereit in ${matchId} (${readySet.size}/2)`); - - if (readySet.size >= 2) { - stopReadyTimer(io, matchId); - io._arenaReady.delete(matchId); - } - }); - - /* ── Aufgeben ── */ - socket.on("player_surrender", (data) => { - const { matchId, slot } = data; - console.log(`[1v1] ${slot} hat aufgegeben in Match ${matchId}`); - // Aufgabe-Logik kommt hier rein - io.to("arena_" + matchId).emit("player_surrendered", { slot }); - }); - - /* ── Disconnect: aus Pool entfernen ── */ - socket.on("disconnect", () => { - if (waitingPool.delete(socket.id)) { - console.log(`[1v1] ${socket.id} disconnected – aus Pool entfernt.`); - } - }); -} - module.exports = { registerArenaHandlers };