159 lines
6.3 KiB
JavaScript
159 lines
6.3 KiB
JavaScript
import { getMatch, addMatch } from "../state/match.js";
|
|
import BiscaMatch from "../classes/BiscaMatch.js";
|
|
import axios from "axios";
|
|
|
|
const API_URL = "http://localhost:8000/api/v1";
|
|
|
|
export default (io, socket) => {
|
|
|
|
const broadcastMatchUpdate = (matchId, match) => {
|
|
const roomName = `match_${matchId}`;
|
|
const room = io.sockets.adapter.rooms.get(roomName);
|
|
if (room) {
|
|
for (const socketId of room) {
|
|
const clientSocket = io.sockets.sockets.get(socketId);
|
|
if (!clientSocket || !clientSocket.user) continue;
|
|
const userId = clientSocket.user.id;
|
|
const publicState = match.getPublicState();
|
|
const gameState = match.getGameState(userId);
|
|
clientSocket.emit("match:update", { ...publicState, game: gameState });
|
|
}
|
|
}
|
|
};
|
|
|
|
const createMatchEmitter = (matchId) => (eventName, payload) => {
|
|
const match = getMatch(matchId);
|
|
if (!match) return;
|
|
|
|
if (eventName === 'match:new-round-start' || eventName === 'match:ended-signal') {
|
|
broadcastMatchUpdate(matchId, match);
|
|
} else if (eventName === 'match:round-ended') {
|
|
io.to(`match_${matchId}`).emit('match:round-ended', payload);
|
|
} else {
|
|
io.to(`match_${matchId}`).emit(eventName, payload);
|
|
}
|
|
};
|
|
|
|
// --- FIX: Timer Callback ---
|
|
const handleAutoPlay = (matchId) => (userId, cardId) => {
|
|
const match = getMatch(matchId);
|
|
if (!match || match.status !== "Playing") return;
|
|
|
|
console.log(`[Match ${matchId}] Auto-playing for User ${userId}`);
|
|
const result = match.playCard(userId, cardId);
|
|
|
|
if (result && result.action === "trick_resolved") {
|
|
io.to(`match_${matchId}`).emit("game:trick-end", result.trickResult);
|
|
setTimeout(() => { broadcastMatchUpdate(matchId, match); }, 1500);
|
|
} else {
|
|
broadcastMatchUpdate(matchId, match);
|
|
}
|
|
};
|
|
|
|
socket.on("match:enter", async (data) => {
|
|
const { matchId } = data;
|
|
if (!socket.user || !socket.user.id) return socket.emit("error", { message: "Auth failed" });
|
|
const userId = socket.user.id;
|
|
socket.activeMatchId = matchId;
|
|
|
|
const room = `match_${matchId}`;
|
|
let match = getMatch(matchId);
|
|
|
|
if (!match) {
|
|
try {
|
|
const res = await axios.get(`${API_URL}/matches/${matchId}`, {
|
|
headers: { Authorization: socket.token || socket.handshake.auth.token },
|
|
});
|
|
const matchData = res.data.data || res.data;
|
|
|
|
// Pass handleAutoPlay to constructor
|
|
match = new BiscaMatch(
|
|
matchData,
|
|
createMatchEmitter(matchId),
|
|
socket.token,
|
|
handleAutoPlay(matchId)
|
|
);
|
|
addMatch(match);
|
|
} catch (e) {
|
|
return socket.emit("error", "Match not found");
|
|
}
|
|
}
|
|
match.emitStateCallback = createMatchEmitter(matchId);
|
|
if (userId !== match.player1Id && match.player2Id === 1) await match.joinPlayer(userId, socket.user);
|
|
|
|
socket.join(room);
|
|
const publicState = match.getPublicState();
|
|
const gameState = match.getGameState(userId);
|
|
socket.emit("match:update", { ...publicState, game: gameState });
|
|
if (match.status === 'Playing') broadcastMatchUpdate(matchId, match);
|
|
});
|
|
|
|
socket.on("game:play-card", (data) => {
|
|
const { matchId, cardId } = data;
|
|
const match = getMatch(matchId);
|
|
if (!match || match.status !== "Playing") return;
|
|
const result = match.playCard(socket.user.id, cardId);
|
|
if (result && result.error) return socket.emit("error", { message: result.error });
|
|
if (result && result.action === "trick_resolved") {
|
|
io.to(`match_${matchId}`).emit("game:trick-end", result.trickResult);
|
|
setTimeout(() => { broadcastMatchUpdate(matchId, match); }, 1500);
|
|
} else {
|
|
broadcastMatchUpdate(matchId, match);
|
|
}
|
|
});
|
|
|
|
socket.on("game:surrender", async ({ matchId }) => {
|
|
const match = getMatch(matchId);
|
|
if(!match || match.status !== "Playing") return;
|
|
const userId = socket.user.id;
|
|
console.log(`[Match ${matchId}] User ${userId} surrendered ROUND.`);
|
|
const opponentId = match.player1Id == userId ? match.player2Id : match.player1Id;
|
|
const result = {
|
|
winnerId: opponentId,
|
|
loserId: userId,
|
|
isDraw: false,
|
|
player1_id: match.player1Id,
|
|
player2_id: match.player2Id,
|
|
player1_points: match.player1Id == opponentId ? 91 : 0,
|
|
player2_points: match.player2Id == opponentId ? 91 : 0,
|
|
reason: 'surrender'
|
|
};
|
|
await match.handleGameEnd(result);
|
|
});
|
|
|
|
socket.on("match:surrender", async ({ matchId }) => {
|
|
const match = getMatch(matchId);
|
|
if(!match || match.status !== "Playing") return;
|
|
const userId = socket.user.id;
|
|
|
|
// FIX: Abort current game to remove ghost
|
|
await match.abortCurrentGame(userId);
|
|
|
|
console.log(`[Match ${matchId}] User ${userId} surrendered MATCH.`);
|
|
const opponentId = match.player1Id == userId ? match.player2Id : match.player1Id;
|
|
match.marks[opponentId] = 4;
|
|
await match.finishMatch();
|
|
broadcastMatchUpdate(matchId, match);
|
|
});
|
|
|
|
socket.on("disconnect", async () => {
|
|
if (socket.activeMatchId) {
|
|
const match = getMatch(socket.activeMatchId);
|
|
if (match && match.status === 'Playing') {
|
|
const userId = socket.user?.id;
|
|
if (userId === match.player1Id || userId === match.player2Id) {
|
|
console.log(`[Match ${match.id}] Player ${userId} disconnected.`);
|
|
|
|
// FIX: Abort current game
|
|
await match.abortCurrentGame(userId);
|
|
|
|
const opponentId = match.player1Id == userId ? match.player2Id : match.player1Id;
|
|
match.marks[opponentId] = 4;
|
|
await match.finishMatch();
|
|
broadcastMatchUpdate(match.id, match);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
};
|