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 701b6b73..78c8c56c 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 @@ -2,10 +2,8 @@ import static io.f1.backend.domain.quiz.mapper.QuizMapper.toGameStartResponse; -import io.f1.backend.domain.game.dto.request.GameStartRequest; import io.f1.backend.domain.game.dto.response.GameStartResponse; import io.f1.backend.domain.game.event.RoomUpdatedEvent; -import io.f1.backend.domain.game.model.GameSetting; import io.f1.backend.domain.game.model.Player; import io.f1.backend.domain.game.model.Room; import io.f1.backend.domain.game.model.RoomState; @@ -13,6 +11,7 @@ import io.f1.backend.domain.question.entity.Question; import io.f1.backend.domain.quiz.app.QuizService; import io.f1.backend.domain.quiz.entity.Quiz; +import io.f1.backend.domain.user.dto.UserPrincipal; import io.f1.backend.global.exception.CustomException; import io.f1.backend.global.exception.errorcode.GameErrorCode; import io.f1.backend.global.exception.errorcode.RoomErrorCode; @@ -24,6 +23,7 @@ import java.util.List; import java.util.Map; +import java.util.Objects; @Service @RequiredArgsConstructor @@ -33,53 +33,54 @@ public class GameService { private final RoomRepository roomRepository; private final ApplicationEventPublisher eventPublisher; - public GameStartResponse gameStart(Long roomId, GameStartRequest gameStartRequest) { - - Long quizId = gameStartRequest.quizId(); + public GameStartResponse gameStart(Long roomId, UserPrincipal principal) { Room room = roomRepository .findRoom(roomId) .orElseThrow(() -> new CustomException(RoomErrorCode.ROOM_NOT_FOUND)); - if (!validateReadyStatus(room)) { - throw new CustomException(RoomErrorCode.PLAYER_NOT_READY); - } - - // 방의 gameSetting에 설정된 퀴즈랑 요청 퀴즈랑 같은지 체크 후 GameSetting에서 라운드 가져오기 - Integer round = checkGameSetting(room, quizId); + validateRoomStart(room, principal); + Long quizId = room.getGameSetting().getQuizId(); Quiz quiz = quizService.getQuizWithQuestionsById(quizId); + List questions = prepareQuestions(room, quiz); - // 라운드 수만큼 랜덤 Question 추출 - List questions = quizService.getRandomQuestionsWithoutAnswer(quizId, round); room.updateQuestions(questions); - GameStartResponse gameStartResponse = toGameStartResponse(questions); - // 방 정보 게임 중으로 변경 room.updateRoomState(RoomState.PLAYING); eventPublisher.publishEvent(new RoomUpdatedEvent(room, quiz)); - return gameStartResponse; + return toGameStartResponse(questions); } - private Integer checkGameSetting(Room room, Long quizId) { - - GameSetting gameSetting = room.getGameSetting(); + private boolean validateReadyStatus(Room room) { - if (!gameSetting.validateQuizId(quizId)) { - throw new CustomException(GameErrorCode.GAME_SETTING_CONFLICT); - } + Map playerSessionMap = room.getPlayerSessionMap(); - return gameSetting.getRound(); + return playerSessionMap.values().stream().allMatch(Player::isReady); } - private boolean validateReadyStatus(Room room) { + private void validateRoomStart(Room room, UserPrincipal principal) { + if (!Objects.equals(principal.getUserId(), room.getHost().getId())) { + throw new CustomException(RoomErrorCode.NOT_ROOM_OWNER); + } - Map playerSessionMap = room.getPlayerSessionMap(); + if (!validateReadyStatus(room)) { + throw new CustomException(GameErrorCode.PLAYER_NOT_READY); + } - return playerSessionMap.values().stream().allMatch(Player::isReady); + if (room.getState() == RoomState.PLAYING) { + throw new CustomException(RoomErrorCode.GAME_ALREADY_PLAYING); + } + } + + // 라운드 수만큼 랜덤 Question 추출 + private List prepareQuestions(Room room, Quiz quiz) { + Long quizId = quiz.getId(); + Integer round = room.getGameSetting().getRound(); + return quizService.getRandomQuestionsWithoutAnswer(quizId, round); } } diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/request/GameStartRequest.java b/backend/src/main/java/io/f1/backend/domain/game/dto/request/GameStartRequest.java deleted file mode 100644 index 61f792ea..00000000 --- a/backend/src/main/java/io/f1/backend/domain/game/dto/request/GameStartRequest.java +++ /dev/null @@ -1,3 +0,0 @@ -package io.f1.backend.domain.game.dto.request; - -public record GameStartRequest(Long quizId) {} diff --git a/backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java b/backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java index d51c18f2..976c1c98 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java +++ b/backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java @@ -1,5 +1,8 @@ package io.f1.backend.domain.game.websocket; +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.GameService; import io.f1.backend.domain.game.app.RoomService; import io.f1.backend.domain.game.dto.ChatMessage; @@ -8,7 +11,6 @@ import io.f1.backend.domain.game.dto.RoomInitialData; import io.f1.backend.domain.game.dto.RoundResult; import io.f1.backend.domain.game.dto.request.DefaultWebSocketRequest; -import io.f1.backend.domain.game.dto.request.GameStartRequest; import io.f1.backend.domain.game.dto.response.GameStartResponse; import io.f1.backend.domain.game.dto.response.PlayerListResponse; import io.f1.backend.domain.user.dto.UserPrincipal; @@ -18,8 +20,6 @@ import org.springframework.messaging.Message; import org.springframework.messaging.handler.annotation.DestinationVariable; import org.springframework.messaging.handler.annotation.MessageMapping; -import org.springframework.messaging.simp.stomp.StompHeaderAccessor; -import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; @Controller @@ -71,12 +71,11 @@ public void exitRoom(@DestinationVariable Long roomId, Message message) { } @MessageMapping("/room/start/{roomId}") - public void gameStart( - @DestinationVariable Long roomId, - Message> message) { + public void gameStart(@DestinationVariable Long roomId, Message message) { + + UserPrincipal principal = getSessionUser(message); - GameStartResponse gameStartResponse = - gameService.gameStart(roomId, message.getPayload().getMessage()); + GameStartResponse gameStartResponse = gameService.gameStart(roomId, principal); String destination = getDestination(roomId); @@ -112,17 +111,6 @@ public void playerReady(@DestinationVariable Long roomId, Message message) { messageSender.send(getDestination(roomId), MessageType.PLAYER_LIST, playerListResponse); } - private static String getSessionId(Message message) { - StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); - return accessor.getSessionId(); - } - - private static UserPrincipal getSessionUser(Message message) { - StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); - Authentication auth = (Authentication) accessor.getUser(); - return (UserPrincipal) auth.getPrincipal(); - } - private String getDestination(Long roomId) { return "/sub/room/" + roomId; } diff --git a/backend/src/main/java/io/f1/backend/domain/game/websocket/WebSocketUtils.java b/backend/src/main/java/io/f1/backend/domain/game/websocket/WebSocketUtils.java new file mode 100644 index 00000000..d148e35b --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/websocket/WebSocketUtils.java @@ -0,0 +1,21 @@ +package io.f1.backend.domain.game.websocket; + +import io.f1.backend.domain.user.dto.UserPrincipal; + +import org.springframework.messaging.Message; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.security.core.Authentication; + +public class WebSocketUtils { + + public static String getSessionId(Message message) { + StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); + return accessor.getSessionId(); + } + + public static UserPrincipal getSessionUser(Message message) { + StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); + Authentication auth = (Authentication) accessor.getUser(); + return (UserPrincipal) auth.getPrincipal(); + } +} diff --git a/backend/src/main/java/io/f1/backend/global/exception/errorcode/GameErrorCode.java b/backend/src/main/java/io/f1/backend/global/exception/errorcode/GameErrorCode.java index 73a12122..923252d3 100644 --- a/backend/src/main/java/io/f1/backend/global/exception/errorcode/GameErrorCode.java +++ b/backend/src/main/java/io/f1/backend/global/exception/errorcode/GameErrorCode.java @@ -8,7 +8,8 @@ @Getter @RequiredArgsConstructor public enum GameErrorCode implements ErrorCode { - GAME_SETTING_CONFLICT("E409002", HttpStatus.CONFLICT, "게임 설정이 맞지 않습니다."); + GAME_SETTING_CONFLICT("E409002", HttpStatus.CONFLICT, "게임 설정이 맞지 않습니다."), + PLAYER_NOT_READY("E403004", HttpStatus.FORBIDDEN, "게임 시작을 위한 준비 상태가 아닙니다."); private final String code; diff --git a/backend/src/main/java/io/f1/backend/global/exception/errorcode/RoomErrorCode.java b/backend/src/main/java/io/f1/backend/global/exception/errorcode/RoomErrorCode.java index 2925373e..4a9b31ca 100644 --- a/backend/src/main/java/io/f1/backend/global/exception/errorcode/RoomErrorCode.java +++ b/backend/src/main/java/io/f1/backend/global/exception/errorcode/RoomErrorCode.java @@ -10,11 +10,12 @@ public enum RoomErrorCode implements ErrorCode { ROOM_USER_LIMIT_REACHED("E403002", HttpStatus.FORBIDDEN, "정원이 모두 찼습니다."), ROOM_GAME_IN_PROGRESS("E403003", HttpStatus.FORBIDDEN, "게임이 진행 중 입니다."), - PLAYER_NOT_READY("E403004", HttpStatus.FORBIDDEN, "게임 시작을 위한 준비 상태가 아닙니다."), ROOM_NOT_FOUND("E404005", HttpStatus.NOT_FOUND, "존재하지 않는 방입니다."), WRONG_PASSWORD("E401006", HttpStatus.UNAUTHORIZED, "비밀번호가 일치하지않습니다."), PLAYER_NOT_FOUND("E404007", HttpStatus.NOT_FOUND, "존재하지 않는 플레이어입니다."), - SOCKET_SESSION_NOT_FOUND("E404006", HttpStatus.NOT_FOUND, "존재하지 않는 소켓 세션입니다."); + SOCKET_SESSION_NOT_FOUND("E404006", HttpStatus.NOT_FOUND, "존재하지 않는 소켓 세션입니다."), + GAME_ALREADY_PLAYING("E400015", HttpStatus.BAD_REQUEST, "이미 게임이 진행 중 입니다."), + NOT_ROOM_OWNER("E403005", HttpStatus.FORBIDDEN, "방장만 게임 시작이 가능합니다."); private final String code;