hzluz
This commit is contained in:
parent
9dde216cbf
commit
67a02cc646
235
app.js
235
app.js
@ -23,6 +23,8 @@ const equipment = require("./routes/equipment");
|
||||
const blackmarket = require("./routes/blackmarket");
|
||||
const mineRoute = require("./routes/mine_route");
|
||||
const arenaRoutes = require("./routes/routes_arena");
|
||||
const { registerArenaHandlers } = require("./sockets/arena");
|
||||
const { registerChatHandlers } = require("./sockets/chat");
|
||||
|
||||
const compression = require("compression");
|
||||
|
||||
@ -249,240 +251,13 @@ app.use((req, res) => {
|
||||
});
|
||||
|
||||
/* ========================
|
||||
Chat + 1v1 Matchmaking System
|
||||
Socket.io Handler
|
||||
======================== */
|
||||
|
||||
let onlineUsers = {};
|
||||
|
||||
// ── 1v1 Matchmaking Pool ─────────────────────────────────────────────────────
|
||||
// Map: socketId → { socket, player: { id, name, level } }
|
||||
const waitingPool = new Map();
|
||||
const LEVEL_RANGE = 5;
|
||||
|
||||
function tryMatchmaking(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) {
|
||||
// Match gefunden – beide aus dem Pool entfernen
|
||||
waitingPool.delete(newSocketId);
|
||||
waitingPool.delete(id);
|
||||
|
||||
const matchId = `match_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
||||
|
||||
// Spieler 1 benachrichtigen
|
||||
challenger.socket.emit("match_found", {
|
||||
matchId,
|
||||
opponent: entry.player,
|
||||
mySlot: "player1",
|
||||
});
|
||||
|
||||
// Spieler 2 benachrichtigen
|
||||
entry.socket.emit("match_found", {
|
||||
matchId,
|
||||
opponent: challenger.player,
|
||||
mySlot: "player2",
|
||||
});
|
||||
|
||||
console.log(
|
||||
`[1v1] Match gestartet: ${challenger.player.name} (Lvl ${challenger.player.level}) ` +
|
||||
`vs ${entry.player.name} (Lvl ${entry.player.level}) | ID: ${matchId}`
|
||||
);
|
||||
|
||||
return; // Nur ein Match pro Aufruf – nächste Iteration startet neu
|
||||
}
|
||||
}
|
||||
}
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
console.log("Spieler verbunden:", socket.id);
|
||||
|
||||
/* ── Chat: Registrierung ── */
|
||||
socket.on("register", async (username) => {
|
||||
const [rows] = await db.query(
|
||||
"SELECT ingame_name FROM accounts WHERE username = ?",
|
||||
[username],
|
||||
);
|
||||
|
||||
if (!rows.length) return;
|
||||
|
||||
const ingameName = rows[0].ingame_name;
|
||||
socket.user = ingameName;
|
||||
onlineUsers[ingameName] = socket.id;
|
||||
io.emit("onlineUsers", Object.keys(onlineUsers));
|
||||
});
|
||||
|
||||
/* ── 1v1: Queue beitreten ── */
|
||||
socket.on("join_1v1", (playerData) => {
|
||||
// Doppelt-Eintrag verhindern
|
||||
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}) betritt den Pool. Poolgröße: ${waitingPool.size}`);
|
||||
|
||||
tryMatchmaking(socket.id);
|
||||
});
|
||||
|
||||
/* ── 1v1: Queue verlassen ── */
|
||||
socket.on("leave_1v1", () => {
|
||||
if (waitingPool.delete(socket.id)) {
|
||||
socket.emit("queue_status", { status: "left" });
|
||||
console.log(`[1v1] Spieler ${socket.id} hat den Pool verlassen.`);
|
||||
}
|
||||
});
|
||||
|
||||
/* ── 1v1: Bereit-System ── */
|
||||
if (!io._arenaReady) io._arenaReady = new Map(); // matchId → Set of ready slots
|
||||
|
||||
socket.on("player_ready", (data) => {
|
||||
const { matchId, slot } = data;
|
||||
if (!matchId || !slot) return;
|
||||
|
||||
if (!io._arenaReady.has(matchId)) {
|
||||
io._arenaReady.set(matchId, new Set());
|
||||
}
|
||||
|
||||
const readySet = io._arenaReady.get(matchId);
|
||||
readySet.add(slot);
|
||||
|
||||
// Beide Spieler in der Arena-Room benachrichtigen
|
||||
io.to("arena_" + matchId).emit("ready_status", {
|
||||
readyCount: readySet.size,
|
||||
});
|
||||
|
||||
console.log(`[1v1] ${slot} ist bereit in Match ${matchId} (${readySet.size}/2)`);
|
||||
|
||||
// Aufräumen wenn beide bereit
|
||||
if (readySet.size >= 2) {
|
||||
io._arenaReady.delete(matchId);
|
||||
}
|
||||
});
|
||||
|
||||
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 });
|
||||
});
|
||||
|
||||
/* ── 1v1: Spielfeld-Verbindung (beide Spieler im iframe) ── */
|
||||
// Map: matchId → { player1: socketId, player2: socketId, names: {} }
|
||||
if (!io._arenaRooms) io._arenaRooms = new Map();
|
||||
|
||||
socket.on("arena_join", (data) => {
|
||||
const { matchId, slot } = data;
|
||||
if (!matchId || !slot) return;
|
||||
|
||||
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]) {
|
||||
// Beide sind da → arena_ready an alle im Raum senden
|
||||
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"]}`);
|
||||
} else {
|
||||
// Erster Spieler → dem Gegner mitteilen sobald er kommt
|
||||
socket.to("arena_" + matchId).emit("arena_opponent_joined", {
|
||||
name: room.names[slot],
|
||||
slot,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* ── Chat: Nachrichten ── */
|
||||
socket.on("chatMessage", (data) => {
|
||||
if (data.channel === "global") {
|
||||
io.emit("chatMessage", {
|
||||
user: socket.user,
|
||||
message: data.message,
|
||||
channel: "global",
|
||||
});
|
||||
}
|
||||
|
||||
if (data.channel === "guild") {
|
||||
io.to("guild_" + data.guild).emit("chatMessage", {
|
||||
user: socket.user,
|
||||
message: data.message,
|
||||
channel: "guild",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("whisper", (data) => {
|
||||
const targetSocket = onlineUsers[data.to];
|
||||
|
||||
if (!targetSocket) {
|
||||
socket.emit("systemMessage", { message: data.to + " ist offline" });
|
||||
return;
|
||||
}
|
||||
|
||||
io.to(targetSocket).emit("chatMessage", {
|
||||
user: socket.user,
|
||||
message: data.message,
|
||||
channel: "private",
|
||||
});
|
||||
|
||||
socket.emit("chatMessage", {
|
||||
user: "(an " + data.to + ")",
|
||||
message: data.message,
|
||||
channel: "private",
|
||||
});
|
||||
});
|
||||
|
||||
socket.on("privateMessage", (data) => {
|
||||
const target = onlineUsers[data.to];
|
||||
if (target) {
|
||||
io.to(target).emit("chatMessage", {
|
||||
user: socket.user,
|
||||
message: data.message,
|
||||
channel: "private",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* ── Disconnect ── */
|
||||
socket.on("disconnect", () => {
|
||||
// Aus Chat entfernen
|
||||
if (socket.user) {
|
||||
delete onlineUsers[socket.user];
|
||||
io.emit("onlineUsers", Object.keys(onlineUsers));
|
||||
}
|
||||
|
||||
// Aus 1v1 Pool entfernen
|
||||
if (waitingPool.delete(socket.id)) {
|
||||
console.log(`[1v1] Spieler ${socket.id} disconnected – aus Pool entfernt.`);
|
||||
}
|
||||
});
|
||||
registerChatHandlers(io, socket);
|
||||
registerArenaHandlers(io, socket);
|
||||
});
|
||||
|
||||
/* ========================
|
||||
|
||||
@ -593,3 +593,77 @@ body {
|
||||
backdrop-filter: blur(2px);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ── Bereit-Timer Box (zentriert im Lock-Overlay) ── */
|
||||
#board-lock-overlay {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
#ready-timer-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
background: rgba(10, 8, 5, 0.92);
|
||||
border: 1px solid rgba(255, 215, 80, 0.35);
|
||||
border-radius: 16px;
|
||||
padding: 32px 40px;
|
||||
box-shadow: 0 8px 40px rgba(0,0,0,0.8);
|
||||
min-width: 260px;
|
||||
}
|
||||
#ready-timer-label {
|
||||
font-family: "Cinzel", serif;
|
||||
font-size: 18px;
|
||||
letter-spacing: 4px;
|
||||
color: rgba(255, 215, 80, 0.9);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
#ready-timer-ring {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.timer-track {
|
||||
fill: none;
|
||||
stroke: rgba(255,255,255,0.1);
|
||||
stroke-width: 6;
|
||||
}
|
||||
.timer-fill {
|
||||
fill: none;
|
||||
stroke: #27ae60;
|
||||
stroke-width: 6;
|
||||
stroke-linecap: round;
|
||||
transform: rotate(-90deg);
|
||||
transform-origin: center;
|
||||
transition: stroke-dashoffset 0.9s linear, stroke 0.3s ease;
|
||||
}
|
||||
#ready-timer-number {
|
||||
position: absolute;
|
||||
font-family: "Cinzel", serif;
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-shadow: 0 0 10px rgba(0,0,0,0.8);
|
||||
}
|
||||
#ready-timer-sub {
|
||||
font-family: "Cinzel", serif;
|
||||
font-size: 11px;
|
||||
color: rgba(255,255,255,0.4);
|
||||
letter-spacing: 1px;
|
||||
text-align: center;
|
||||
}
|
||||
#ready-status-row {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
.ready-pip {
|
||||
font-family: "Cinzel", serif;
|
||||
font-size: 12px;
|
||||
color: rgba(255,255,255,0.5);
|
||||
letter-spacing: 1px;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
199
sockets/arena_socket.js
Normal file
199
sockets/arena_socket.js
Normal file
@ -0,0 +1,199 @@
|
||||
/* ============================================================
|
||||
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 };
|
||||
89
sockets/chat_socket.js
Normal file
89
sockets/chat_socket.js
Normal file
@ -0,0 +1,89 @@
|
||||
/* ============================================================
|
||||
sockets/chat.js
|
||||
Alle Socket-Events rund um Chat, Whisper & Online-Status
|
||||
============================================================ */
|
||||
|
||||
const db = require("../database/database");
|
||||
|
||||
const onlineUsers = {}; // ingameName → socketId
|
||||
|
||||
function registerChatHandlers(io, socket) {
|
||||
|
||||
/* ── Registrierung ── */
|
||||
socket.on("register", async (username) => {
|
||||
const [rows] = await db.query(
|
||||
"SELECT ingame_name FROM accounts WHERE username = ?",
|
||||
[username],
|
||||
);
|
||||
|
||||
if (!rows.length) return;
|
||||
|
||||
const ingameName = rows[0].ingame_name;
|
||||
socket.user = ingameName;
|
||||
onlineUsers[ingameName] = socket.id;
|
||||
io.emit("onlineUsers", Object.keys(onlineUsers));
|
||||
});
|
||||
|
||||
/* ── Chat-Nachrichten ── */
|
||||
socket.on("chatMessage", (data) => {
|
||||
if (data.channel === "global") {
|
||||
io.emit("chatMessage", {
|
||||
user: socket.user,
|
||||
message: data.message,
|
||||
channel: "global",
|
||||
});
|
||||
}
|
||||
|
||||
if (data.channel === "guild") {
|
||||
io.to("guild_" + data.guild).emit("chatMessage", {
|
||||
user: socket.user,
|
||||
message: data.message,
|
||||
channel: "guild",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* ── Flüstern ── */
|
||||
socket.on("whisper", (data) => {
|
||||
const targetSocket = onlineUsers[data.to];
|
||||
|
||||
if (!targetSocket) {
|
||||
socket.emit("systemMessage", { message: data.to + " ist offline" });
|
||||
return;
|
||||
}
|
||||
|
||||
io.to(targetSocket).emit("chatMessage", {
|
||||
user: socket.user,
|
||||
message: data.message,
|
||||
channel: "private",
|
||||
});
|
||||
|
||||
socket.emit("chatMessage", {
|
||||
user: "(an " + data.to + ")",
|
||||
message: data.message,
|
||||
channel: "private",
|
||||
});
|
||||
});
|
||||
|
||||
/* ── Private Nachricht ── */
|
||||
socket.on("privateMessage", (data) => {
|
||||
const target = onlineUsers[data.to];
|
||||
if (target) {
|
||||
io.to(target).emit("chatMessage", {
|
||||
user: socket.user,
|
||||
message: data.message,
|
||||
channel: "private",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* ── Disconnect: aus Online-Liste entfernen ── */
|
||||
socket.on("disconnect", () => {
|
||||
if (socket.user) {
|
||||
delete onlineUsers[socket.user];
|
||||
io.emit("onlineUsers", Object.keys(onlineUsers));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { registerChatHandlers };
|
||||
@ -42,7 +42,20 @@
|
||||
</div>
|
||||
|
||||
<!-- Spielfeld-Sperre bis beide Spieler bereit sind -->
|
||||
<div id="board-lock-overlay"></div>
|
||||
<div id="board-lock-overlay">
|
||||
<div id="ready-timer-box">
|
||||
<div id="ready-timer-label">Bereit machen</div>
|
||||
<div id="ready-timer-ring">
|
||||
<svg viewBox="0 0 80 80" width="80" height="80"><circle cx="40" cy="40" r="34" class="timer-track"/><circle cx="40" cy="40" r="34" class="timer-fill" id="timer-circle"/></svg>
|
||||
<div id="ready-timer-number">30</div>
|
||||
</div>
|
||||
<div id="ready-timer-sub">Beide Spieler müssen BEREIT klicken</div>
|
||||
<div id="ready-status-row">
|
||||
<div class="ready-pip" id="pip-player1">⬜ Spieler 1</div>
|
||||
<div class="ready-pip" id="pip-player2">⬜ Spieler 2</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- LEFT AVATAR (Spieler 1) -->
|
||||
<div class="avatar avatar-left" id="avLeft">
|
||||
@ -240,8 +253,37 @@
|
||||
socket.emit("player_ready", { matchId, slot: mySlot });
|
||||
}
|
||||
|
||||
// Timer-Kreis: Umfang des Kreises (r=34 → 2*π*34 ≈ 213.6)
|
||||
const CIRCUMFERENCE = 2 * Math.PI * 34;
|
||||
const timerCircle = document.getElementById("timer-circle");
|
||||
if (timerCircle) timerCircle.style.strokeDasharray = CIRCUMFERENCE;
|
||||
|
||||
socket.on("ready_timer", (data) => {
|
||||
const { remaining } = data;
|
||||
const num = document.getElementById("ready-timer-number");
|
||||
if (num) num.textContent = remaining;
|
||||
|
||||
// Kreis-Fortschritt aktualisieren
|
||||
if (timerCircle) {
|
||||
const progress = remaining / 30;
|
||||
const offset = CIRCUMFERENCE * (1 - progress);
|
||||
timerCircle.style.strokeDashoffset = offset;
|
||||
// Farbe: grün → gelb → rot
|
||||
if (remaining > 15) timerCircle.style.stroke = "#27ae60";
|
||||
else if (remaining > 7) timerCircle.style.stroke = "#f39c12";
|
||||
else timerCircle.style.stroke = "#e74c3c";
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("ready_status", (data) => {
|
||||
// data.readyCount = wie viele Spieler bereits bereit sind
|
||||
// Pips aktualisieren
|
||||
const pip1 = document.getElementById("pip-player1");
|
||||
const pip2 = document.getElementById("pip-player2");
|
||||
if (data.readySlots && pip1 && pip2) {
|
||||
if (data.readySlots.includes("player1")) pip1.textContent = "✅ " + (document.getElementById("nameLeft")?.textContent || "Spieler 1");
|
||||
if (data.readySlots.includes("player2")) pip2.textContent = "✅ " + (document.getElementById("nameRight")?.textContent || "Spieler 2");
|
||||
}
|
||||
|
||||
if (data.readyCount === 2) {
|
||||
// Beide bereit → Sperre aufheben
|
||||
const lock = document.getElementById("board-lock-overlay");
|
||||
@ -255,6 +297,19 @@
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("match_cancelled", (data) => {
|
||||
const lock = document.getElementById("board-lock-overlay");
|
||||
if (lock) {
|
||||
lock.innerHTML = `
|
||||
<div id="ready-timer-box">
|
||||
<div id="ready-timer-label" style="color:#e74c3c;">⏰ Zeit abgelaufen</div>
|
||||
<div id="ready-timer-sub">${data.message || "Match abgebrochen."}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
// Aufgabe-Logik kommt hier rein
|
||||
});
|
||||
|
||||
function handleAufgeben() {
|
||||
// Funktion offen – hier kann später die Aufgabe-Logik rein
|
||||
socket.emit("player_surrender", { matchId, slot: mySlot });
|
||||
|
||||
Loading…
Reference in New Issue
Block a user