Skip to content

Commit 214bc54

Browse files
committed
✨ feat: 게임 설정 변경 기능 추가
1 parent e1d79fc commit 214bc54

File tree

8 files changed

+129
-32
lines changed

8 files changed

+129
-32
lines changed

backend/src/main/java/io/f1/backend/domain/game/app/GameService.java

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package io.f1.backend.domain.game.app;
22

33
import static io.f1.backend.domain.game.mapper.RoomMapper.toQuestionStartResponse;
4-
import static io.f1.backend.domain.game.websocket.WebSocketUtils.getDestination;
4+
import static io.f1.backend.domain.game.mapper.RoomMapper.toGameSettingResponse;
5+
import static io.f1.backend.domain.game.mapper.RoomMapper.toPlayerListResponse;
56
import static io.f1.backend.domain.quiz.mapper.QuizMapper.toGameStartResponse;
67

8+
import io.f1.backend.domain.game.dto.GameSettingChanger;
79
import io.f1.backend.domain.game.dto.MessageType;
10+
import io.f1.backend.domain.game.dto.response.PlayerListResponse;
811
import io.f1.backend.domain.game.event.RoomUpdatedEvent;
912
import io.f1.backend.domain.game.model.Player;
1013
import io.f1.backend.domain.game.model.Room;
@@ -25,7 +28,6 @@
2528
import org.springframework.stereotype.Service;
2629

2730
import java.util.List;
28-
import java.util.Map;
2931
import java.util.Objects;
3032

3133
@Service
@@ -45,9 +47,9 @@ public void gameStart(Long roomId, UserPrincipal principal) {
4547
String destination = getDestination(roomId);
4648

4749
Room room =
48-
roomRepository
49-
.findRoom(roomId)
50-
.orElseThrow(() -> new CustomException(RoomErrorCode.ROOM_NOT_FOUND));
50+
roomRepository
51+
.findRoom(roomId)
52+
.orElseThrow(() -> new CustomException(RoomErrorCode.ROOM_NOT_FOUND));
5153

5254
validateRoomStart(room, principal);
5355

@@ -70,19 +72,48 @@ public void gameStart(Long roomId, UserPrincipal principal) {
7072
toQuestionStartResponse(room, START_DELAY));
7173
}
7274

73-
private boolean validateReadyStatus(Room room) {
75+
public void handlePlayerReady(Long roomId, String sessionId) {
76+
Player player =
77+
roomRepository
78+
.findPlayerInRoomBySessionId(roomId, sessionId)
79+
.orElseThrow(() -> new CustomException(RoomErrorCode.PLAYER_NOT_FOUND));
7480

75-
Map<String, Player> playerSessionMap = room.getPlayerSessionMap();
81+
Room room = findRoom(roomId);
7682

77-
return playerSessionMap.values().stream().allMatch(Player::isReady);
83+
toggleReadyIfPossible(room, player);
84+
85+
String destination = getDestination(roomId);
86+
PlayerListResponse playerListResponse = toPlayerListResponse(room);
87+
88+
messageSender.send(destination, MessageType.PLAYER_LIST, toPlayerListResponse(room));
89+
}
90+
91+
public void changeGameSetting(Long roomId, UserPrincipal principal, GameSettingChanger request) {
92+
Room room = findRoom(roomId);
93+
validateHostAndState(room, principal);
94+
95+
if (!request.change(room, quizService)) {
96+
return;
97+
}
98+
99+
room.resetAllPlayerReadyStates();
100+
101+
String destination = getDestination(roomId);
102+
Quiz quiz = quizService.getQuizWithQuestionsById(room.getGameSetting().getQuizId());
103+
104+
PlayerListResponse playerListResponse = toPlayerListResponse(room);
105+
messageSender.send(destination, MessageType.GAME_SETTING, toGameSettingResponse(room.getGameSetting(), quiz));
106+
messageSender.send(destination, MessageType.PLAYER_LIST, playerListResponse);
107+
108+
eventPublisher.publishEvent(new RoomUpdatedEvent(room, quiz));
78109
}
79110

80111
private void validateRoomStart(Room room, UserPrincipal principal) {
81112
if (!Objects.equals(principal.getUserId(), room.getHost().getId())) {
82113
throw new CustomException(RoomErrorCode.NOT_ROOM_OWNER);
83114
}
84115

85-
if (!validateReadyStatus(room)) {
116+
if (!room.validateReadyStatus()) {
86117
throw new CustomException(GameErrorCode.PLAYER_NOT_READY);
87118
}
88119

@@ -97,4 +128,32 @@ private List<Question> prepareQuestions(Room room, Quiz quiz) {
97128
Integer round = room.getGameSetting().getRound();
98129
return quizService.getRandomQuestionsWithoutAnswer(quizId, round);
99130
}
131+
132+
private Room findRoom(Long roomId) {
133+
return roomRepository
134+
.findRoom(roomId)
135+
.orElseThrow(() -> new CustomException(RoomErrorCode.ROOM_NOT_FOUND));
136+
}
137+
138+
private String getDestination(Long roomId) {
139+
return "/sub/room/" + roomId;
140+
}
141+
142+
private void validateHostAndState(Room room, UserPrincipal principal) {
143+
if (!Objects.equals(principal.getUserId(), room.getHost().getId())) {
144+
throw new CustomException(RoomErrorCode.NOT_ROOM_OWNER);
145+
}
146+
if (room.isPlaying()) {
147+
throw new CustomException(RoomErrorCode.GAME_ALREADY_PLAYING);
148+
}
149+
}
150+
151+
private void toggleReadyIfPossible(Room room, Player player) {
152+
if (room.isPlaying()) {
153+
throw new CustomException(RoomErrorCode.GAME_ALREADY_PLAYING);
154+
}
155+
if (!Objects.equals(player.getId(), room.getHost().getId())) {
156+
player.toggleReady();
157+
}
158+
}
100159
}

backend/src/main/java/io/f1/backend/domain/game/app/RoomService.java

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -201,21 +201,6 @@ public void exitRoom(Long roomId, String sessionId, UserPrincipal principal) {
201201
}
202202
}
203203

204-
public void handlePlayerReady(Long roomId, String sessionId) {
205-
Player player =
206-
roomRepository
207-
.findPlayerInRoomBySessionId(roomId, sessionId)
208-
.orElseThrow(() -> new CustomException(RoomErrorCode.PLAYER_NOT_FOUND));
209-
210-
player.toggleReady();
211-
212-
Room room = findRoom(roomId);
213-
214-
String destination = getDestination(roomId);
215-
216-
messageSender.send(destination, MessageType.PLAYER_LIST, toPlayerListResponse(room));
217-
}
218-
219204
public RoomListResponse getAllRooms() {
220205
List<Room> rooms = roomRepository.findAll();
221206
List<RoomResponse> roomResponses =

backend/src/main/java/io/f1/backend/domain/game/mapper/RoomMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public static GameSettingResponse toGameSettingResponse(GameSetting gameSetting,
5656
public static PlayerListResponse toPlayerListResponse(Room room) {
5757
List<PlayerResponse> playerResponseList =
5858
room.getPlayerSessionMap().values().stream()
59-
.map(player -> new PlayerResponse(player.getNickname(), false))
59+
.map(player -> new PlayerResponse(player.getNickname(), player.isReady()))
6060
.toList();
6161

6262
return new PlayerListResponse(room.getHost().getNickname(), playerResponseList);
Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
11
package io.f1.backend.domain.game.model;
22

3+
import io.f1.backend.domain.game.dto.TimeLimit;
4+
import io.f1.backend.domain.quiz.entity.Quiz;
5+
import io.f1.backend.global.exception.CustomException;
6+
import io.f1.backend.global.exception.errorcode.GameErrorCode;
37
import lombok.AllArgsConstructor;
48
import lombok.Getter;
59

6-
import java.util.Objects;
7-
810
@Getter
911
@AllArgsConstructor
1012
public class GameSetting {
1113

1214
private Long quizId;
13-
private Integer round; // 게임 변경 시 해당 게임의 총 문제 수로 설정
15+
private Integer round;
1416
private int timeLimit;
1517

16-
public boolean validateQuizId(Long quizId) {
17-
return Objects.equals(this.quizId, quizId);
18+
public void changeQuiz(Quiz quiz) {
19+
quizId = quiz.getId();
20+
round = quiz.getQuestions().size(); // 라운드를 바꾼 퀴즈의 문제 수로 동기화
21+
}
22+
23+
public void changeTimeLimit(TimeLimit timeLimit) {
24+
this.timeLimit = timeLimit.getValue();
25+
}
26+
27+
public void changeRound(int round, int questionsCount) {
28+
if (round > questionsCount) {
29+
throw new CustomException(GameErrorCode.ROUND_EXCEEDS_QUESTION_COUNT);
30+
}
31+
this.round = round;
1832
}
1933
}

backend/src/main/java/io/f1/backend/domain/game/model/Player.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ public void toggleReady() {
2424
this.isReady = !this.isReady;
2525
}
2626

27+
public void setReadyFalse() {
28+
this.isReady = false;
29+
}
30+
2731
public void increaseCorrectCount() {
2832
correctCount++;
2933
}

backend/src/main/java/io/f1/backend/domain/game/model/Room.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.f1.backend.domain.question.entity.Question;
44

5+
import java.util.Objects;
56
import lombok.Getter;
67

78
import java.time.LocalDateTime;
@@ -90,4 +91,16 @@ public Boolean isPlaying() {
9091
public void increaseCurrentRound() {
9192
currentRound++;
9293
}
94+
95+
public boolean validateReadyStatus() {
96+
97+
return playerSessionMap.values().stream().allMatch(Player::isReady);
98+
}
99+
100+
public void resetAllPlayerReadyStates() {
101+
for (Player player : playerSessionMap.values()) {
102+
if (Objects.equals(player.getId(), getHost().getId())) continue;
103+
player.setReadyFalse();
104+
}
105+
}
93106
}

backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import io.f1.backend.domain.game.app.GameService;
77
import io.f1.backend.domain.game.app.RoomService;
88
import io.f1.backend.domain.game.dto.ChatMessage;
9+
import io.f1.backend.domain.game.dto.QuizChangeRequest;
10+
import io.f1.backend.domain.game.dto.RoundChangeRequest;
11+
import io.f1.backend.domain.game.dto.TimeLimitChangeRequest;
912
import io.f1.backend.domain.game.dto.request.DefaultWebSocketRequest;
1013
import io.f1.backend.domain.user.dto.UserPrincipal;
1114

@@ -61,6 +64,24 @@ public void chat(
6164
@MessageMapping("/room/ready/{roomId}")
6265
public void playerReady(@DestinationVariable Long roomId, Message<?> message) {
6366

64-
roomService.handlePlayerReady(roomId, getSessionId(message));
67+
gameService.handlePlayerReady(roomId, getSessionId(message));
68+
}
69+
70+
@MessageMapping("/room/quiz/{roomId}")
71+
public void quizChange(@DestinationVariable Long roomId, Message<DefaultWebSocketRequest<QuizChangeRequest>> message) {
72+
UserPrincipal principal = getSessionUser(message);
73+
gameService.changeGameSetting(roomId, principal, message.getPayload().getMessage());
74+
}
75+
76+
@MessageMapping("/room/time-limit/{roomId}")
77+
public void timeLimitChange(@DestinationVariable Long roomId, Message<DefaultWebSocketRequest<TimeLimitChangeRequest>> message) {
78+
UserPrincipal principal = getSessionUser(message);
79+
gameService.changeGameSetting(roomId, principal, message.getPayload().getMessage());
80+
}
81+
82+
@MessageMapping("/room/round/{roomId}")
83+
public void roundChange(@DestinationVariable Long roomId, Message<DefaultWebSocketRequest<RoundChangeRequest>> message) {
84+
UserPrincipal principal = getSessionUser(message);
85+
gameService.changeGameSetting(roomId, principal, message.getPayload().getMessage());
6586
}
6687
}

backend/src/main/java/io/f1/backend/global/exception/errorcode/GameErrorCode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
@RequiredArgsConstructor
1010
public enum GameErrorCode implements ErrorCode {
1111
GAME_SETTING_CONFLICT("E409002", HttpStatus.CONFLICT, "게임 설정이 맞지 않습니다."),
12-
PLAYER_NOT_READY("E403004", HttpStatus.FORBIDDEN, "게임 시작을 위한 준비 상태가 아닙니다.");
12+
PLAYER_NOT_READY("E403004", HttpStatus.FORBIDDEN, "게임 시작을 위한 준비 상태가 아닙니다."),
13+
ROUND_EXCEEDS_QUESTION_COUNT("E400016", HttpStatus.BAD_REQUEST, "라운드 수는 선택한 퀴즈의 문제 수보다 많을 수 없습니다.");
1314

1415
private final String code;
1516

0 commit comments

Comments
 (0)