const { v4: uuidv4 } = require('uuid'); const { logger } = require('../../utils/logger'); module.exports = function(io, socket, gameState) { socket.on('registerPlayer', function(data) { try { const rating = data.rating; const nickname = data.nickname; const imageIndex = data.imageIndex; gameState.playerRating.set(socket.id, rating); gameState.nickname.set(socket.id, nickname); gameState.imageIndex.set(socket.id, imageIndex); logger.info(`플레이어 등록: ID ${socket.id}, 닉네임: ${nickname}, 급수 ${rating}, 프로필 이미지 인덱스: ${imageIndex}`); // 급수에 따른 매칭 진행 findMatch(socket, rating, nickname, imageIndex); } catch (err) { logger.error(`플레이어 등록 중 오류: ${err}`); socket.emit('error', { message: "플레이어 등록 중 오류가 발생했습니다." }); } }); const switchToAI = (roomId, socket) => { // 대기방 목록에서 제거 gameState.rooms = gameState.rooms.filter(room => room.roomId !== roomId); // 플레이어 매칭 정보 제거 gameState.playerRating.delete(socket.id); gameState.nickname.delete(socket.id); gameState.imageIndex.delete(socket.id); gameState.matchingTimeouts.delete(socket.id); socket.emit('switchAI', { message: "타임아웃 AI로 전환" }); } const findMatch = (socket, playerRating, nickname, imageIndex) => { let matchedRoom = null; // 1. 같은 급수의 방 찾기 matchedRoom = gameState.rooms.find(room => Math.abs(gameState.playerRating.get(room.hostId) - playerRating) === 0); // 2. 같은 급수 방이 없으면 ±1급 범위로 확장 if (!matchedRoom) { matchedRoom = gameState.rooms.find(room => Math.abs(gameState.playerRating.get(room.hostId) - playerRating) <= 1); } if (matchedRoom) { const roomId = matchedRoom.roomId; // 매칭된 방은 대기 목록에서 제거 gameState.rooms = gameState.rooms.filter(room => room.roomId !== roomId); // 방 입장 socket.join(roomId); gameState.socketRooms.set(socket.id, roomId); // 흑백 여부 결정 50% 확률 let isHostFirst = Math.random() < 0.5; // 클라이언트에게 방 정보 전송 (상대 정보 포함) socket.emit('joinRoom', { roomId: roomId, opponentRating: gameState.playerRating.get(matchedRoom.hostId), opponentNickname: gameState.nickname.get(matchedRoom.hostId), opponentImageIndex: gameState.imageIndex.get(matchedRoom.imageIndex), isBlack: !isHostFirst }); // 상대방에게 게임 시작 알림 (내 정보 포함) socket.to(roomId).emit('startGame', { opponentId: socket.id, opponentRating: playerRating, opponentNickname: nickname, opponentImageIndex: imageIndex, isBlack: isHostFirst }); logger.info(`매칭 성공: ${socket.id}(${playerRating}급) - ${matchedRoom.hostId}(${gameState.playerRating.get(matchedRoom.hostId)}급)`); } // 4. 매칭된 방이 없으면 새 방 생성 else { const roomId = uuidv4(); socket.join(roomId); gameState.rooms.push({ roomId: roomId, hostId: socket.id, rating: playerRating, imageIndex: imageIndex }); gameState.socketRooms.set(socket.id, roomId); socket.emit('createRoom', { roomId: roomId, message: "대기 중... 비슷한 상대를 찾고 있습니다." }); logger.info(`대기방 생성: ID ${socket.id}, 급수 ${playerRating}, 방 ID ${roomId}`); // 15초 타임아웃 설정 const timeoutId = setTimeout(() => { logger.info("타이머 종료, 15초 경과"); // 타임 아웃 AI로 던지기 // 아직 방에 혼자인지 확인 const room = gameState.rooms.find(r => r.roomId === roomId); if (room) { // 방이 남아 있으면 매칭 전임 logger.info("방이 남아 있어 방 삭제 후 AI로 전달"); switchToAI(roomId, socket); } },15000) // 타임아웃 ID 저장 gameState.matchingTimeouts.set(socket.id, timeoutId); } } };