200 lines
5.9 KiB
JavaScript
200 lines
5.9 KiB
JavaScript
/* ============================================================
|
||
sockets/arena.js
|
||
Alle Socket-Events rund um 1v1 Matchmaking, Spielfeld & Bereit-System
|
||
============================================================ */
|
||
|
||
const waitingPool = new Map(); // socketId → { socket, player }
|
||
const LEVEL_RANGE = 5;
|
||
|
||
// 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
|
||
|
||
const READY_TIMEOUT = 30; // Sekunden bis Match abgebrochen wird
|
||
|
||
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 };
|