diff --git a/backend/src/main/java/io/f1/backend/domain/game/app/ChatService.java b/backend/src/main/java/io/f1/backend/domain/game/app/ChatService.java new file mode 100644 index 00000000..00df3de1 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/app/ChatService.java @@ -0,0 +1,48 @@ +package io.f1.backend.domain.game.app; + +import static io.f1.backend.domain.game.websocket.WebSocketUtils.getDestination; + +import io.f1.backend.domain.game.dto.ChatMessage; +import io.f1.backend.domain.game.dto.MessageType; +import io.f1.backend.domain.game.event.GameCorrectAnswerEvent; +import io.f1.backend.domain.game.model.Room; +import io.f1.backend.domain.game.websocket.MessageSender; +import io.f1.backend.domain.question.entity.Question; + +import lombok.RequiredArgsConstructor; + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ChatService { + + private final RoomService roomService; + private final TimerService timerService; + private final MessageSender messageSender; + private final ApplicationEventPublisher eventPublisher; + + // todo 동시성적용 + public void chat(Long roomId, String sessionId, ChatMessage chatMessage) { + + Room room = roomService.findRoom(roomId); + + String destination = getDestination(roomId); + + messageSender.send(destination, MessageType.CHAT, chatMessage); + + if (!room.isPlaying()) { + return; + } + + Question currentQuestion = room.getCurrentQuestion(); + + String answer = currentQuestion.getAnswer(); + + if (answer.equals(chatMessage.message())) { + eventPublisher.publishEvent( + new GameCorrectAnswerEvent(room, sessionId, chatMessage, answer)); + } + } +} diff --git a/backend/src/main/java/io/f1/backend/domain/game/app/GameService.java b/backend/src/main/java/io/f1/backend/domain/game/app/GameService.java index 2ecae340..f378b24b 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/app/GameService.java +++ b/backend/src/main/java/io/f1/backend/domain/game/app/GameService.java @@ -1,13 +1,16 @@ package io.f1.backend.domain.game.app; -import static io.f1.backend.domain.game.mapper.RoomMapper.toGameSettingResponse; -import static io.f1.backend.domain.game.mapper.RoomMapper.toPlayerListResponse; -import static io.f1.backend.domain.game.mapper.RoomMapper.toQuestionStartResponse; +import static io.f1.backend.domain.game.mapper.RoomMapper.*; +import static io.f1.backend.domain.game.websocket.WebSocketUtils.getDestination; import static io.f1.backend.domain.quiz.mapper.QuizMapper.toGameStartResponse; +import io.f1.backend.domain.game.dto.ChatMessage; import io.f1.backend.domain.game.dto.MessageType; +import io.f1.backend.domain.game.dto.RoomEventType; import io.f1.backend.domain.game.dto.request.GameSettingChanger; import io.f1.backend.domain.game.dto.response.PlayerListResponse; +import io.f1.backend.domain.game.event.GameCorrectAnswerEvent; +import io.f1.backend.domain.game.event.GameTimeoutEvent; import io.f1.backend.domain.game.event.RoomUpdatedEvent; import io.f1.backend.domain.game.model.Player; import io.f1.backend.domain.game.model.Room; @@ -26,9 +29,11 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import java.util.List; +import java.util.Map; import java.util.Objects; @Slf4j @@ -36,11 +41,14 @@ @RequiredArgsConstructor public class GameService { - public static final int START_DELAY = 5; + private static final int START_DELAY = 5; + private static final int CONTINUE_DELAY = 3; + private static final String NONE_CORRECT_USER = ""; - private final MessageSender messageSender; - private final TimerService timerService; private final QuizService quizService; + private final RoomService roomService; + private final TimerService timerService; + private final MessageSender messageSender; private final RoomRepository roomRepository; private final ApplicationEventPublisher eventPublisher; @@ -68,12 +76,109 @@ public void gameStart(Long roomId, UserPrincipal principal) { timerService.startTimer(room, START_DELAY); messageSender.send(destination, MessageType.GAME_START, toGameStartResponse(questions)); + messageSender.send(destination, MessageType.RANK_UPDATE, toRankUpdateResponse(room)); messageSender.send( destination, MessageType.QUESTION_START, toQuestionStartResponse(room, START_DELAY)); } + @EventListener + public void onCorrectAnswer(GameCorrectAnswerEvent event) { + + Room room = event.room(); + String sessionId = event.sessionId(); + ChatMessage chatMessage = event.chatMessage(); + String answer = event.answer(); + + String destination = getDestination(room.getId()); + + room.increasePlayerCorrectCount(sessionId); + + messageSender.send( + destination, + MessageType.QUESTION_RESULT, + toQuestionResultResponse(chatMessage.nickname(), answer)); + messageSender.send(destination, MessageType.RANK_UPDATE, toRankUpdateResponse(room)); + messageSender.send( + destination, + MessageType.SYSTEM_NOTICE, + ofPlayerEvent(chatMessage.nickname(), RoomEventType.CORRECT_ANSWER)); + + timerService.cancelTimer(room); + + if (!timerService.validateCurrentRound(room)) { + gameEnd(room); + return; + } + + room.increaseCurrentRound(); + + // 타이머 추가하기 + timerService.startTimer(room, CONTINUE_DELAY); + messageSender.send( + destination, + MessageType.QUESTION_START, + toQuestionStartResponse(room, CONTINUE_DELAY)); + } + + @EventListener + public void onTimeout(GameTimeoutEvent event) { + Room room = event.room(); + String destination = getDestination(room.getId()); + + messageSender.send( + destination, + MessageType.QUESTION_RESULT, + toQuestionResultResponse(NONE_CORRECT_USER, room.getCurrentQuestion().getAnswer())); + messageSender.send( + destination, + MessageType.SYSTEM_NOTICE, + ofPlayerEvent(NONE_CORRECT_USER, RoomEventType.TIMEOUT)); + + if (!timerService.validateCurrentRound(room)) { + gameEnd(room); + return; + } + + room.increaseCurrentRound(); + + timerService.startTimer(room, CONTINUE_DELAY); + messageSender.send( + destination, + MessageType.QUESTION_START, + toQuestionStartResponse(room, CONTINUE_DELAY)); + } + + public void gameEnd(Room room) { + Long roomId = room.getId(); + String destination = getDestination(roomId); + + Map playerSessionMap = room.getPlayerSessionMap(); + + // TODO : 랭킹 정보 업데이트 + messageSender.send( + destination, + MessageType.GAME_RESULT, + toGameResultListResponse(playerSessionMap, room.getGameSetting().getRound())); + + room.initializeRound(); + room.initializePlayers(); + + List disconnectedPlayers = room.getDisconnectedPlayers(); + roomService.handleDisconnectedPlayers(room, disconnectedPlayers); + + room.updateRoomState(RoomState.WAITING); + + messageSender.send( + destination, + MessageType.GAME_SETTING, + toGameSettingResponse( + room.getGameSetting(), + quizService.getQuizWithQuestionsById(room.getGameSetting().getQuizId()))); + messageSender.send(destination, MessageType.ROOM_SETTING, toRoomSettingResponse(room)); + } + public void handlePlayerReady(Long roomId, String sessionId) { Room room = findRoom(roomId); @@ -136,10 +241,6 @@ private Room findRoom(Long roomId) { .orElseThrow(() -> new CustomException(RoomErrorCode.ROOM_NOT_FOUND)); } - private String getDestination(Long roomId) { - return "/sub/room/" + roomId; - } - private void validateHostAndState(Room room, UserPrincipal principal) { if (!room.isHost(principal.getUserId())) { throw new CustomException(RoomErrorCode.NOT_ROOM_OWNER); diff --git a/backend/src/main/java/io/f1/backend/domain/game/app/RoomService.java b/backend/src/main/java/io/f1/backend/domain/game/app/RoomService.java index 2ca33fa8..481989f8 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/app/RoomService.java +++ b/backend/src/main/java/io/f1/backend/domain/game/app/RoomService.java @@ -1,13 +1,10 @@ package io.f1.backend.domain.game.app; -import static io.f1.backend.domain.game.app.GameService.START_DELAY; import static io.f1.backend.domain.game.mapper.RoomMapper.ofPlayerEvent; import static io.f1.backend.domain.game.mapper.RoomMapper.toGameSetting; import static io.f1.backend.domain.game.mapper.RoomMapper.toGameSettingResponse; import static io.f1.backend.domain.game.mapper.RoomMapper.toPlayerListResponse; -import static io.f1.backend.domain.game.mapper.RoomMapper.toQuestionResultResponse; import static io.f1.backend.domain.game.mapper.RoomMapper.toQuestionStartResponse; -import static io.f1.backend.domain.game.mapper.RoomMapper.toRankUpdateResponse; import static io.f1.backend.domain.game.mapper.RoomMapper.toRoomResponse; import static io.f1.backend.domain.game.mapper.RoomMapper.toRoomSetting; import static io.f1.backend.domain.game.mapper.RoomMapper.toRoomSettingResponse; @@ -16,7 +13,6 @@ import static io.f1.backend.global.util.SecurityUtils.getCurrentUserId; import static io.f1.backend.global.util.SecurityUtils.getCurrentUserNickname; -import io.f1.backend.domain.game.dto.ChatMessage; import io.f1.backend.domain.game.dto.MessageType; import io.f1.backend.domain.game.dto.RoomEventType; import io.f1.backend.domain.game.dto.request.RoomCreateRequest; @@ -37,7 +33,6 @@ import io.f1.backend.domain.game.model.RoomState; import io.f1.backend.domain.game.store.RoomRepository; import io.f1.backend.domain.game.websocket.MessageSender; -import io.f1.backend.domain.question.entity.Question; import io.f1.backend.domain.quiz.app.QuizService; import io.f1.backend.domain.quiz.dto.QuizMinData; import io.f1.backend.domain.quiz.entity.Quiz; @@ -62,7 +57,6 @@ @RequiredArgsConstructor public class RoomService { - private final TimerService timerService; private final QuizService quizService; private final RoomRepository roomRepository; private final AtomicLong roomIdGenerator = new AtomicLong(0); @@ -166,19 +160,7 @@ public void exitRoom(Long roomId, String sessionId, UserPrincipal principal) { String destination = getDestination(roomId); - /* 방 삭제 */ - if (room.isLastPlayer(sessionId)) { - removeRoom(room); - return; - } - - /* 방장 변경 */ - if (room.isHost(removePlayer.getId())) { - changeHost(room, sessionId); - } - - /* 플레이어 삭제 */ - removePlayer(room, sessionId, removePlayer); + cleanRoom(room, sessionId, removePlayer); SystemNoticeResponse systemNoticeResponse = ofPlayerEvent(removePlayer.nickname, RoomEventType.EXIT); @@ -205,55 +187,6 @@ public RoomListResponse getAllRooms() { return new RoomListResponse(roomResponses); } - // todo 동시성적용 - public void chat(Long roomId, String sessionId, ChatMessage chatMessage) { - - Room room = findRoom(roomId); - - String destination = getDestination(roomId); - - messageSender.send(destination, MessageType.CHAT, chatMessage); - - if (!room.isPlaying()) { - return; - } - - Question currentQuestion = room.getCurrentQuestion(); - - String answer = currentQuestion.getAnswer(); - - if (answer.equals(chatMessage.message())) { - room.increasePlayerCorrectCount(sessionId); - - messageSender.send( - destination, - MessageType.QUESTION_RESULT, - toQuestionResultResponse(chatMessage.nickname(), answer)); - messageSender.send(destination, MessageType.RANK_UPDATE, toRankUpdateResponse(room)); - messageSender.send( - destination, - MessageType.SYSTEM_NOTICE, - ofPlayerEvent(chatMessage.nickname(), RoomEventType.CORRECT_ANSWER)); - - timerService.cancelTimer(room); - - // TODO : 게임 종료 로직 추가 - if (!timerService.validateCurrentRound(room)) { - // 게임 종료 로직 - return; - } - - room.increaseCurrentRound(); - - // 타이머 추가하기 - timerService.startTimer(room, CONTINUE_DELAY); - messageSender.send( - destination, - MessageType.QUESTION_START, - toQuestionStartResponse(room, CONTINUE_DELAY)); - } - } - public void reconnectSession( Long roomId, String oldSessionId, String newSessionId, UserPrincipal principal) { Room room = findRoom(roomId); @@ -273,7 +206,7 @@ public void reconnectSession( messageSender.send( destination, MessageType.QUESTION_START, - toQuestionStartResponse(room, START_DELAY)); + toQuestionStartResponse(room, CONTINUE_DELAY)); } else { RoomSettingResponse roomSettingResponse = toRoomSettingResponse(room); @@ -326,7 +259,7 @@ private Player createPlayer() { return new Player(getCurrentUserId(), getCurrentUserNickname()); } - private Room findRoom(Long roomId) { + public Room findRoom(Long roomId) { return roomRepository .findRoom(roomId) .orElseThrow(() -> new CustomException(RoomErrorCode.ROOM_NOT_FOUND)); @@ -362,4 +295,47 @@ private void removePlayer(Room room, String sessionId, Player removePlayer) { room.removeSessionId(sessionId); room.removeValidatedUserId(removePlayer.getId()); } + + public void exitRoomForDisconnectedPlayer(Long roomId, Player player, String sessionId) { + + Object lock = roomLocks.computeIfAbsent(roomId, k -> new Object()); + + synchronized (lock) { + // 연결 끊긴 플레이어 exit 로직 타게 해주기 + Room room = findRoom(roomId); + + cleanRoom(room, sessionId, player); + + String destination = getDestination(roomId); + + SystemNoticeResponse systemNoticeResponse = + ofPlayerEvent(player.nickname, RoomEventType.EXIT); + + messageSender.send(destination, MessageType.PLAYER_LIST, toPlayerListResponse(room)); + messageSender.send(destination, MessageType.SYSTEM_NOTICE, systemNoticeResponse); + } + } + + private void cleanRoom(Room room, String sessionId, Player player) { + /* 방 삭제 */ + if (room.isLastPlayer(sessionId)) { + removeRoom(room); + return; + } + + /* 방장 변경 */ + if (room.isHost(player.getId())) { + changeHost(room, sessionId); + } + + /* 플레이어 삭제 */ + removePlayer(room, sessionId, player); + } + + public void handleDisconnectedPlayers(Room room, List disconnectedPlayers) { + for (Player player : disconnectedPlayers) { + String sessionId = room.getSessionIdByUserId(player.getId()); + exitRoomForDisconnectedPlayer(room.getId(), player, sessionId); + } + } } diff --git a/backend/src/main/java/io/f1/backend/domain/game/app/TimerService.java b/backend/src/main/java/io/f1/backend/domain/game/app/TimerService.java index 407892e0..93a06df8 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/app/TimerService.java +++ b/backend/src/main/java/io/f1/backend/domain/game/app/TimerService.java @@ -1,17 +1,11 @@ package io.f1.backend.domain.game.app; -import static io.f1.backend.domain.game.mapper.RoomMapper.ofPlayerEvent; -import static io.f1.backend.domain.game.mapper.RoomMapper.toQuestionResultResponse; -import static io.f1.backend.domain.game.mapper.RoomMapper.toQuestionStartResponse; -import static io.f1.backend.domain.game.websocket.WebSocketUtils.getDestination; - -import io.f1.backend.domain.game.dto.MessageType; -import io.f1.backend.domain.game.dto.RoomEventType; +import io.f1.backend.domain.game.event.GameTimeoutEvent; import io.f1.backend.domain.game.model.Room; -import io.f1.backend.domain.game.websocket.MessageSender; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import java.util.concurrent.ScheduledFuture; @@ -21,10 +15,7 @@ @RequiredArgsConstructor public class TimerService { - private final MessageSender messageSender; - - private static final String NONE_CORRECT_USER = ""; - private static final int CONTINUE_DELAY = 3; + private final ApplicationEventPublisher eventPublisher; public void startTimer(Room room, int delaySec) { cancelTimer(room); @@ -33,7 +24,7 @@ public void startTimer(Room room, int delaySec) { room.getScheduler() .schedule( () -> { - handleTimeout(room); + eventPublisher.publishEvent(new GameTimeoutEvent(room)); }, delaySec + room.getGameSetting().getTimeLimit(), TimeUnit.SECONDS); @@ -41,35 +32,6 @@ public void startTimer(Room room, int delaySec) { room.updateTimer(timer); } - private void handleTimeout(Room room) { - String destination = getDestination(room.getId()); - - messageSender.send( - destination, - MessageType.QUESTION_RESULT, - toQuestionResultResponse(NONE_CORRECT_USER, room.getCurrentQuestion().getAnswer())); - messageSender.send( - destination, - MessageType.SYSTEM_NOTICE, - ofPlayerEvent(NONE_CORRECT_USER, RoomEventType.TIMEOUT)); - - // TODO : 게임 종료 로직 - if (!validateCurrentRound(room)) { - // 게임 종료 로직 - // GAME_SETTING, PLAYER_LIST, GAME_RESULT, ROOM_SETTING - return; - } - - // 다음 문제 출제 - room.increaseCurrentRound(); - - startTimer(room, CONTINUE_DELAY); - messageSender.send( - destination, - MessageType.QUESTION_START, - toQuestionStartResponse(room, CONTINUE_DELAY)); - } - public boolean validateCurrentRound(Room room) { if (room.getGameSetting().getRound() != room.getCurrentRound()) { return true; diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/MessageType.java b/backend/src/main/java/io/f1/backend/domain/game/dto/MessageType.java index b2b79f20..103bd214 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/dto/MessageType.java +++ b/backend/src/main/java/io/f1/backend/domain/game/dto/MessageType.java @@ -9,5 +9,6 @@ public enum MessageType { CHAT, QUESTION_RESULT, RANK_UPDATE, - QUESTION_START + QUESTION_START, + GAME_RESULT } diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/response/GameResultListResponse.java b/backend/src/main/java/io/f1/backend/domain/game/dto/response/GameResultListResponse.java new file mode 100644 index 00000000..c2c38d46 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/dto/response/GameResultListResponse.java @@ -0,0 +1,5 @@ +package io.f1.backend.domain.game.dto.response; + +import java.util.List; + +public record GameResultListResponse(List result) {} diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/response/GameResultResponse.java b/backend/src/main/java/io/f1/backend/domain/game/dto/response/GameResultResponse.java new file mode 100644 index 00000000..8be85b6a --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/dto/response/GameResultResponse.java @@ -0,0 +1,3 @@ +package io.f1.backend.domain.game.dto.response; + +public record GameResultResponse(String nickname, int score, int totalCorrectCount, int rank) {} diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/response/QuestionStartResponse.java b/backend/src/main/java/io/f1/backend/domain/game/dto/response/QuestionStartResponse.java index c6788e0f..9c502931 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/dto/response/QuestionStartResponse.java +++ b/backend/src/main/java/io/f1/backend/domain/game/dto/response/QuestionStartResponse.java @@ -2,4 +2,10 @@ import java.time.Instant; -public record QuestionStartResponse(Long questionId, int round, Instant timestamp) {} +public record QuestionStartResponse( + Long questionId, + int round, + Instant timestamp, + int timeLimit, + Instant serverTime, + int totalRound) {} diff --git a/backend/src/main/java/io/f1/backend/domain/game/event/GameCorrectAnswerEvent.java b/backend/src/main/java/io/f1/backend/domain/game/event/GameCorrectAnswerEvent.java new file mode 100644 index 00000000..76f5bcd1 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/event/GameCorrectAnswerEvent.java @@ -0,0 +1,7 @@ +package io.f1.backend.domain.game.event; + +import io.f1.backend.domain.game.dto.ChatMessage; +import io.f1.backend.domain.game.model.Room; + +public record GameCorrectAnswerEvent( + Room room, String sessionId, ChatMessage chatMessage, String answer) {} diff --git a/backend/src/main/java/io/f1/backend/domain/game/event/GameTimeoutEvent.java b/backend/src/main/java/io/f1/backend/domain/game/event/GameTimeoutEvent.java new file mode 100644 index 00000000..1670cc90 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/event/GameTimeoutEvent.java @@ -0,0 +1,5 @@ +package io.f1.backend.domain.game.event; + +import io.f1.backend.domain.game.model.Room; + +public record GameTimeoutEvent(Room room) {} diff --git a/backend/src/main/java/io/f1/backend/domain/game/mapper/RoomMapper.java b/backend/src/main/java/io/f1/backend/domain/game/mapper/RoomMapper.java index 206282d5..1731385b 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/mapper/RoomMapper.java +++ b/backend/src/main/java/io/f1/backend/domain/game/mapper/RoomMapper.java @@ -3,6 +3,8 @@ import io.f1.backend.domain.game.dto.Rank; import io.f1.backend.domain.game.dto.RoomEventType; import io.f1.backend.domain.game.dto.request.RoomCreateRequest; +import io.f1.backend.domain.game.dto.response.GameResultListResponse; +import io.f1.backend.domain.game.dto.response.GameResultResponse; import io.f1.backend.domain.game.dto.response.GameSettingResponse; import io.f1.backend.domain.game.dto.response.PlayerListResponse; import io.f1.backend.domain.game.dto.response.PlayerResponse; @@ -21,8 +23,10 @@ import io.f1.backend.domain.quiz.entity.Quiz; import java.time.Instant; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Map; public class RoomMapper { @@ -106,6 +110,52 @@ public static QuestionStartResponse toQuestionStartResponse(Room room, int delay return new QuestionStartResponse( room.getCurrentQuestion().getId(), room.getCurrentRound(), - Instant.now().plusSeconds(delay)); + Instant.now().plusSeconds(delay), + room.getGameSetting().getTimeLimit(), + Instant.now(), + room.getGameSetting().getRound()); + } + + public static GameResultResponse toGameResultResponse( + Player player, int round, int rank, int totalPlayers) { + double correctRate = (double) player.getCorrectCount() / round; + int score = (int) (correctRate * 100) + (totalPlayers - rank) * 5; + + return new GameResultResponse(player.nickname, score, player.getCorrectCount(), rank); + } + + public static GameResultListResponse toGameResultListResponse( + Map playerSessionMap, int round) { + + List rankedPlayers = + playerSessionMap.values().stream() + .sorted(Comparator.comparingInt(Player::getCorrectCount).reversed()) + .toList(); + + List gameResults = buildRankedGameResults(rankedPlayers, round); + + return new GameResultListResponse(gameResults); + } + + private static List buildRankedGameResults( + List rankedPlayers, int round) { + int totalPlayers = rankedPlayers.size(); + int prevCorrectCnt = -1; + int rank = 0; + + List results = new ArrayList<>(); + for (int i = 0; i < totalPlayers; i++) { + Player player = rankedPlayers.get(i); + + int correctCnt = player.getCorrectCount(); + + if (prevCorrectCnt != correctCnt) { + rank = i + 1; + } + + results.add(toGameResultResponse(player, round, rank, totalPlayers)); + prevCorrectCnt = correctCnt; + } + return results; } } diff --git a/backend/src/main/java/io/f1/backend/domain/game/model/Player.java b/backend/src/main/java/io/f1/backend/domain/game/model/Player.java index fe92c1a3..9392539d 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/model/Player.java +++ b/backend/src/main/java/io/f1/backend/domain/game/model/Player.java @@ -32,6 +32,10 @@ public void increaseCorrectCount() { correctCount++; } + public void initializeCorrectCount() { + correctCount = 0; + } + public void updateState(ConnectionState newState) { state = newState; } diff --git a/backend/src/main/java/io/f1/backend/domain/game/model/Room.java b/backend/src/main/java/io/f1/backend/domain/game/model/Room.java index 72819dd4..3f761725 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/model/Room.java +++ b/backend/src/main/java/io/f1/backend/domain/game/model/Room.java @@ -117,6 +117,40 @@ public void increaseCurrentRound() { currentRound++; } + public void initializeRound() { + currentRound = 0; + } + + public List getDisconnectedPlayers() { + List disconnectedPlayers = new ArrayList<>(); + + for (Player player : this.playerSessionMap.values()) { + if (player.getState().equals(ConnectionState.DISCONNECTED)) { + disconnectedPlayers.add(player); + } + } + return disconnectedPlayers; + } + + public void initializePlayers() { + this.playerSessionMap + .values() + .forEach( + player -> { + player.initializeCorrectCount(); + player.toggleReady(); + }); + } + + public String getSessionIdByUserId(Long userId) { + for (Map.Entry entry : playerSessionMap.entrySet()) { + if (entry.getValue().getId().equals(userId)) { + return entry.getKey(); + } + } + throw new CustomException(RoomErrorCode.PLAYER_NOT_FOUND); + } + public void reconnectSession(String oldSessionId, String newSessionId) { Player player = playerSessionMap.get(oldSessionId); removeSessionId(oldSessionId); diff --git a/backend/src/main/java/io/f1/backend/domain/game/model/RoomState.java b/backend/src/main/java/io/f1/backend/domain/game/model/RoomState.java index 50d30dca..8d35f963 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/model/RoomState.java +++ b/backend/src/main/java/io/f1/backend/domain/game/model/RoomState.java @@ -2,6 +2,5 @@ public enum RoomState { WAITING, - PLAYING, - FINISHED + PLAYING } diff --git a/backend/src/main/java/io/f1/backend/domain/game/websocket/controller/GameSocketController.java b/backend/src/main/java/io/f1/backend/domain/game/websocket/controller/GameSocketController.java index 06ee39d0..1090af38 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/websocket/controller/GameSocketController.java +++ b/backend/src/main/java/io/f1/backend/domain/game/websocket/controller/GameSocketController.java @@ -3,6 +3,7 @@ import static io.f1.backend.domain.game.websocket.WebSocketUtils.getSessionId; import static io.f1.backend.domain.game.websocket.WebSocketUtils.getSessionUser; +import io.f1.backend.domain.game.app.ChatService; import io.f1.backend.domain.game.app.GameService; import io.f1.backend.domain.game.app.RoomService; import io.f1.backend.domain.game.dto.ChatMessage; @@ -26,6 +27,8 @@ public class GameSocketController { private final RoomService roomService; private final GameService gameService; + private final ChatService chatService; + private final SessionService sessionService; @MessageMapping("/room/initializeRoomSocket/{roomId}") @@ -79,7 +82,7 @@ public void chat( @DestinationVariable Long roomId, Message> message) { - roomService.chat(roomId, getSessionId(message), message.getPayload().getMessage()); + chatService.chat(roomId, getSessionId(message), message.getPayload().getMessage()); } @MessageMapping("/room/ready/{roomId}") diff --git a/backend/src/test/java/io/f1/backend/domain/game/app/RoomServiceTests.java b/backend/src/test/java/io/f1/backend/domain/game/app/RoomServiceTests.java index de45c424..67c26298 100644 --- a/backend/src/test/java/io/f1/backend/domain/game/app/RoomServiceTests.java +++ b/backend/src/test/java/io/f1/backend/domain/game/app/RoomServiceTests.java @@ -47,6 +47,7 @@ class RoomServiceTests { @Mock private RoomRepository roomRepository; @Mock private QuizService quizService; + @Mock private GameService gameService; @Mock private TimerService timerService; @Mock private ApplicationEventPublisher eventPublisher; @Mock private MessageSender messageSender;