Skip to content

Commit 77cfa7d

Browse files
silver-eunjoojiwon1217dlsrks1021LimKangHyun
authored
✨ feat : 게임 종료 로직 구현 (#109)
* ✨ feat : 게임 종료 로직 구현 * chore: Java 스타일 수정 * ✨ feat: 게임 종료 후 disconnected 참여자 정리 * 🔧 chore : 깃 충돌 해결 * chore: Java 스타일 수정 * ♻️ refactor : 게임 시작 시, 실시간 랭킹도 함께 SEND * ♻️ refactor : 서비스 간의 순환참조 해결 * chore: Java 스타일 수정 * ♻️ refactor : 게임 종료 시 로직 변경 * chore: Java 스타일 수정 * 🔧 chore: 게임 종료 시 모든 메세지 브로드캐스팅하도록 변경 * chore: Java 스타일 수정 * 🐛 fix: index 예외, Lazy 예외 버그 수정 * chore: Java 스타일 수정 * ✨ [feat] 유저 닉네임으로 정보 조회 (#101) * ✨ feat: 특정 닉네임이 포함된 유저 정보 조회 기능 * 🐛 fix: 닉네임 중복 확인 시 대소문자 구분하지 않음 * chore: Java 스타일 수정 --------- Co-authored-by: github-actions <> * ♻️ refactor: 세션 타임아웃 값을 환경변수로 대치 (#105) * 🔧 chore : 깃 충돌 해결 * 🔧 chore : 깃 충돌 해결 * ♻️ refactor: 랭킹 조회 Redis 적용 (#111) * ✨ feat: GameSetting 변경 기능 추가 (#107) * ✨ feat: 게임 설정 변경 인터페이스 및 구현체 추가 - GameSettingChanger 인터페이스 도입 * chore: Java 스타일 수정 * ✨ feat: 게임 설정 변경 기능 추가 * chore: Java 스타일 수정 * 🚚 move: 소켓 요청 DTO request 디렉토리로 이동 * 🚚 move: 소켓 요청 DTO 경로 변경 * chore: Java 스타일 수정 * ♻️ refactor: 게임 세팅 요청 후처리 분리 * chore: Java 스타일 수정 * ♻️ refactor: 코드 리뷰 반영 * chore: Java 스타일 수정 * 🗑️ remove: 불필요 코드 삭제 * chore: Java 스타일 수정 * ♻️ refactor: isHost 원상복구 * ♻️ refactor: QuizChangeRequest, RoundChangeRequest 타입 수정 * 🔧 chore: RoomUpdatedEventListener, RoomDeletedEventListener 빈 등록 * chore: Java 스타일 수정 * chore: Java 스타일 수정 * ♻️ refactor: player 조회 메서드 추가 --------- Co-authored-by: github-actions <> * 🐛 fix: 라운드 변경 시 question개수 조회에 fetch join 적용 (#116) * 🐛 fix: round 변경 시 question 조회에 fetch join 적용 * ♻️ refactor: questionCount만 찾는 쿼리로 변경 및 불필요한 퀴즈 아이디 조회 삭제 * chore: Java 스타일 수정 --------- Co-authored-by: github-actions <> * chore: Java 스타일 수정 * chore: Java 스타일 수정 * 🔧 chore : 테스트 코드 통과 * chore: Java 스타일 수정 --------- Co-authored-by: github-actions <> Co-authored-by: Jiwon Kwak <[email protected]> Co-authored-by: dlsrks1021 <[email protected]> Co-authored-by: kanghyun <[email protected]>
1 parent 53d8d35 commit 77cfa7d

File tree

16 files changed

+333
-128
lines changed

16 files changed

+333
-128
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package io.f1.backend.domain.game.app;
2+
3+
import static io.f1.backend.domain.game.websocket.WebSocketUtils.getDestination;
4+
5+
import io.f1.backend.domain.game.dto.ChatMessage;
6+
import io.f1.backend.domain.game.dto.MessageType;
7+
import io.f1.backend.domain.game.event.GameCorrectAnswerEvent;
8+
import io.f1.backend.domain.game.model.Room;
9+
import io.f1.backend.domain.game.websocket.MessageSender;
10+
import io.f1.backend.domain.question.entity.Question;
11+
12+
import lombok.RequiredArgsConstructor;
13+
14+
import org.springframework.context.ApplicationEventPublisher;
15+
import org.springframework.stereotype.Service;
16+
17+
@Service
18+
@RequiredArgsConstructor
19+
public class ChatService {
20+
21+
private final RoomService roomService;
22+
private final TimerService timerService;
23+
private final MessageSender messageSender;
24+
private final ApplicationEventPublisher eventPublisher;
25+
26+
// todo 동시성적용
27+
public void chat(Long roomId, String sessionId, ChatMessage chatMessage) {
28+
29+
Room room = roomService.findRoom(roomId);
30+
31+
String destination = getDestination(roomId);
32+
33+
messageSender.send(destination, MessageType.CHAT, chatMessage);
34+
35+
if (!room.isPlaying()) {
36+
return;
37+
}
38+
39+
Question currentQuestion = room.getCurrentQuestion();
40+
41+
String answer = currentQuestion.getAnswer();
42+
43+
if (answer.equals(chatMessage.message())) {
44+
eventPublisher.publishEvent(
45+
new GameCorrectAnswerEvent(room, sessionId, chatMessage, answer));
46+
}
47+
}
48+
}

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

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

3-
import static io.f1.backend.domain.game.mapper.RoomMapper.toGameSettingResponse;
4-
import static io.f1.backend.domain.game.mapper.RoomMapper.toPlayerListResponse;
5-
import static io.f1.backend.domain.game.mapper.RoomMapper.toQuestionStartResponse;
3+
import static io.f1.backend.domain.game.mapper.RoomMapper.*;
4+
import static io.f1.backend.domain.game.websocket.WebSocketUtils.getDestination;
65
import static io.f1.backend.domain.quiz.mapper.QuizMapper.toGameStartResponse;
76

7+
import io.f1.backend.domain.game.dto.ChatMessage;
88
import io.f1.backend.domain.game.dto.MessageType;
9+
import io.f1.backend.domain.game.dto.RoomEventType;
910
import io.f1.backend.domain.game.dto.request.GameSettingChanger;
1011
import io.f1.backend.domain.game.dto.response.PlayerListResponse;
12+
import io.f1.backend.domain.game.event.GameCorrectAnswerEvent;
13+
import io.f1.backend.domain.game.event.GameTimeoutEvent;
1114
import io.f1.backend.domain.game.event.RoomUpdatedEvent;
1215
import io.f1.backend.domain.game.model.Player;
1316
import io.f1.backend.domain.game.model.Room;
@@ -26,21 +29,26 @@
2629
import lombok.extern.slf4j.Slf4j;
2730

2831
import org.springframework.context.ApplicationEventPublisher;
32+
import org.springframework.context.event.EventListener;
2933
import org.springframework.stereotype.Service;
3034

3135
import java.util.List;
36+
import java.util.Map;
3237
import java.util.Objects;
3338

3439
@Slf4j
3540
@Service
3641
@RequiredArgsConstructor
3742
public class GameService {
3843

39-
public static final int START_DELAY = 5;
44+
private static final int START_DELAY = 5;
45+
private static final int CONTINUE_DELAY = 3;
46+
private static final String NONE_CORRECT_USER = "";
4047

41-
private final MessageSender messageSender;
42-
private final TimerService timerService;
4348
private final QuizService quizService;
49+
private final RoomService roomService;
50+
private final TimerService timerService;
51+
private final MessageSender messageSender;
4452
private final RoomRepository roomRepository;
4553
private final ApplicationEventPublisher eventPublisher;
4654

@@ -68,12 +76,109 @@ public void gameStart(Long roomId, UserPrincipal principal) {
6876
timerService.startTimer(room, START_DELAY);
6977

7078
messageSender.send(destination, MessageType.GAME_START, toGameStartResponse(questions));
79+
messageSender.send(destination, MessageType.RANK_UPDATE, toRankUpdateResponse(room));
7180
messageSender.send(
7281
destination,
7382
MessageType.QUESTION_START,
7483
toQuestionStartResponse(room, START_DELAY));
7584
}
7685

86+
@EventListener
87+
public void onCorrectAnswer(GameCorrectAnswerEvent event) {
88+
89+
Room room = event.room();
90+
String sessionId = event.sessionId();
91+
ChatMessage chatMessage = event.chatMessage();
92+
String answer = event.answer();
93+
94+
String destination = getDestination(room.getId());
95+
96+
room.increasePlayerCorrectCount(sessionId);
97+
98+
messageSender.send(
99+
destination,
100+
MessageType.QUESTION_RESULT,
101+
toQuestionResultResponse(chatMessage.nickname(), answer));
102+
messageSender.send(destination, MessageType.RANK_UPDATE, toRankUpdateResponse(room));
103+
messageSender.send(
104+
destination,
105+
MessageType.SYSTEM_NOTICE,
106+
ofPlayerEvent(chatMessage.nickname(), RoomEventType.CORRECT_ANSWER));
107+
108+
timerService.cancelTimer(room);
109+
110+
if (!timerService.validateCurrentRound(room)) {
111+
gameEnd(room);
112+
return;
113+
}
114+
115+
room.increaseCurrentRound();
116+
117+
// 타이머 추가하기
118+
timerService.startTimer(room, CONTINUE_DELAY);
119+
messageSender.send(
120+
destination,
121+
MessageType.QUESTION_START,
122+
toQuestionStartResponse(room, CONTINUE_DELAY));
123+
}
124+
125+
@EventListener
126+
public void onTimeout(GameTimeoutEvent event) {
127+
Room room = event.room();
128+
String destination = getDestination(room.getId());
129+
130+
messageSender.send(
131+
destination,
132+
MessageType.QUESTION_RESULT,
133+
toQuestionResultResponse(NONE_CORRECT_USER, room.getCurrentQuestion().getAnswer()));
134+
messageSender.send(
135+
destination,
136+
MessageType.SYSTEM_NOTICE,
137+
ofPlayerEvent(NONE_CORRECT_USER, RoomEventType.TIMEOUT));
138+
139+
if (!timerService.validateCurrentRound(room)) {
140+
gameEnd(room);
141+
return;
142+
}
143+
144+
room.increaseCurrentRound();
145+
146+
timerService.startTimer(room, CONTINUE_DELAY);
147+
messageSender.send(
148+
destination,
149+
MessageType.QUESTION_START,
150+
toQuestionStartResponse(room, CONTINUE_DELAY));
151+
}
152+
153+
public void gameEnd(Room room) {
154+
Long roomId = room.getId();
155+
String destination = getDestination(roomId);
156+
157+
Map<String, Player> playerSessionMap = room.getPlayerSessionMap();
158+
159+
// TODO : 랭킹 정보 업데이트
160+
messageSender.send(
161+
destination,
162+
MessageType.GAME_RESULT,
163+
toGameResultListResponse(playerSessionMap, room.getGameSetting().getRound()));
164+
165+
room.initializeRound();
166+
room.initializePlayers();
167+
168+
List<Player> disconnectedPlayers = room.getDisconnectedPlayers();
169+
roomService.handleDisconnectedPlayers(room, disconnectedPlayers);
170+
171+
room.updateRoomState(RoomState.WAITING);
172+
173+
messageSender.send(
174+
destination,
175+
MessageType.GAME_SETTING,
176+
toGameSettingResponse(
177+
room.getGameSetting(),
178+
quizService.getQuizWithQuestionsById(room.getGameSetting().getQuizId())));
179+
messageSender.send(destination, MessageType.ROOM_SETTING, toRoomSettingResponse(room));
180+
}
181+
77182
public void handlePlayerReady(Long roomId, String sessionId) {
78183

79184
Room room = findRoom(roomId);
@@ -136,10 +241,6 @@ private Room findRoom(Long roomId) {
136241
.orElseThrow(() -> new CustomException(RoomErrorCode.ROOM_NOT_FOUND));
137242
}
138243

139-
private String getDestination(Long roomId) {
140-
return "/sub/room/" + roomId;
141-
}
142-
143244
private void validateHostAndState(Room room, UserPrincipal principal) {
144245
if (!room.isHost(principal.getUserId())) {
145246
throw new CustomException(RoomErrorCode.NOT_ROOM_OWNER);

0 commit comments

Comments
 (0)