Skip to content

Commit ba9ca4d

Browse files
committed
♻️ refactor : 서비스 간의 순환참조 해결
1 parent b804952 commit ba9ca4d

File tree

11 files changed

+221
-137
lines changed

11 files changed

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

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

Lines changed: 81 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
package io.f1.backend.domain.game.app;
22

3+
import static io.f1.backend.domain.game.mapper.RoomMapper.ofPlayerEvent;
34
import static io.f1.backend.domain.game.mapper.RoomMapper.toGameResultListResponse;
45
import static io.f1.backend.domain.game.mapper.RoomMapper.toGameSettingResponse;
5-
import static io.f1.backend.domain.game.mapper.RoomMapper.toPlayerListResponse;
6+
import static io.f1.backend.domain.game.mapper.RoomMapper.toQuestionResultResponse;
67
import static io.f1.backend.domain.game.mapper.RoomMapper.toQuestionStartResponse;
78
import static io.f1.backend.domain.game.mapper.RoomMapper.toRankUpdateResponse;
89
import static io.f1.backend.domain.game.mapper.RoomMapper.toRoomSettingResponse;
910
import static io.f1.backend.domain.game.websocket.WebSocketUtils.getDestination;
1011
import static io.f1.backend.domain.quiz.mapper.QuizMapper.toGameStartResponse;
1112

13+
import io.f1.backend.domain.game.dto.ChatMessage;
1214
import io.f1.backend.domain.game.dto.MessageType;
15+
import io.f1.backend.domain.game.dto.RoomEventType;
16+
import io.f1.backend.domain.game.event.GameCorrectAnswerEvent;
17+
import io.f1.backend.domain.game.event.GameTimeoutEvent;
1318
import io.f1.backend.domain.game.event.RoomUpdatedEvent;
14-
import io.f1.backend.domain.game.model.ConnectionState;
1519
import io.f1.backend.domain.game.model.Player;
1620
import io.f1.backend.domain.game.model.Room;
1721
import io.f1.backend.domain.game.model.RoomState;
@@ -28,9 +32,9 @@
2832
import lombok.RequiredArgsConstructor;
2933

3034
import org.springframework.context.ApplicationEventPublisher;
35+
import org.springframework.context.event.EventListener;
3136
import org.springframework.stereotype.Service;
3237

33-
import java.util.ArrayList;
3438
import java.util.List;
3539
import java.util.Map;
3640
import java.util.Objects;
@@ -40,6 +44,8 @@
4044
public class GameService {
4145

4246
private static final int START_DELAY = 5;
47+
private static final int CONTINUE_DELAY = 3;
48+
private static final String NONE_CORRECT_USER = "";
4349

4450
private final QuizService quizService;
4551
private final RoomService roomService;
@@ -79,42 +85,97 @@ public void gameStart(Long roomId, UserPrincipal principal) {
7985
toQuestionStartResponse(room, START_DELAY));
8086
}
8187

82-
public void gameEnd(Room room) {
83-
room.updateRoomState(RoomState.FINISHED);
88+
@EventListener
89+
public void onCorrectAnswer(GameCorrectAnswerEvent event) {
90+
91+
Room room = event.room();
92+
String sessionId = event.sessionId();
93+
ChatMessage chatMessage = event.chatMessage();
94+
String answer = event.answer();
95+
96+
String destination = getDestination(room.getId());
97+
98+
room.increasePlayerCorrectCount(sessionId);
99+
100+
messageSender.send(
101+
destination,
102+
MessageType.QUESTION_RESULT,
103+
toQuestionResultResponse(chatMessage.nickname(), answer));
104+
messageSender.send(destination, MessageType.RANK_UPDATE, toRankUpdateResponse(room));
105+
messageSender.send(
106+
destination,
107+
MessageType.SYSTEM_NOTICE,
108+
ofPlayerEvent(chatMessage.nickname(), RoomEventType.CORRECT_ANSWER));
109+
110+
timerService.cancelTimer(room);
111+
112+
if (!timerService.validateCurrentRound(room)) {
113+
return;
114+
}
115+
116+
room.increaseCurrentRound();
117+
118+
// 타이머 추가하기
119+
timerService.startTimer(room, CONTINUE_DELAY);
120+
messageSender.send(
121+
destination,
122+
MessageType.QUESTION_START,
123+
toQuestionStartResponse(room, CONTINUE_DELAY));
124+
}
125+
126+
@EventListener
127+
public void onTimeout(GameTimeoutEvent event) {
128+
Room room = event.room();
129+
String destination = getDestination(room.getId());
84130

131+
messageSender.send(
132+
destination,
133+
MessageType.QUESTION_RESULT,
134+
toQuestionResultResponse(NONE_CORRECT_USER, room.getCurrentQuestion().getAnswer()));
135+
messageSender.send(
136+
destination,
137+
MessageType.SYSTEM_NOTICE,
138+
ofPlayerEvent(NONE_CORRECT_USER, RoomEventType.TIMEOUT));
139+
140+
if (!timerService.validateCurrentRound(room)) {
141+
gameEnd(room);
142+
return;
143+
}
144+
145+
room.increaseCurrentRound();
146+
147+
timerService.startTimer(room, CONTINUE_DELAY);
148+
messageSender.send(
149+
destination,
150+
MessageType.QUESTION_START,
151+
toQuestionStartResponse(room, CONTINUE_DELAY));
152+
}
153+
154+
public void gameEnd(Room room) {
85155
Long roomId = room.getId();
86156
String destination = getDestination(roomId);
87157

88158
Map<String, Player> playerSessionMap = room.getPlayerSessionMap();
89159

160+
// TODO : 랭킹 정보 업데이트
90161
messageSender.send(
91162
destination,
92163
MessageType.GAME_RESULT,
93164
toGameResultListResponse(playerSessionMap, room.getGameSetting().getRound()));
94165

95-
List<Player> disconnectedPlayers = new ArrayList<>();
96-
97166
room.initializeRound();
98-
for (Player player : playerSessionMap.values()) {
99-
if (player.getState().equals(ConnectionState.DISCONNECTED)) {
100-
disconnectedPlayers.add(player);
101-
}
102-
player.initializeCorrectCount();
103-
player.toggleReady();
104-
}
105-
106-
for (Player player : disconnectedPlayers) {
107-
String sessionId = room.getUserIdSessionMap().get(player.id);
108-
roomService.exitRoomForDisconnectedPlayer(roomId, player, sessionId);
109-
}
167+
room.initializePlayers();
110168

111169
room.updateRoomState(RoomState.WAITING);
112-
messageSender.send(destination, MessageType.PLAYER_LIST, toPlayerListResponse(room));
170+
113171
messageSender.send(
114172
destination,
115173
MessageType.GAME_SETTING,
116174
toGameSettingResponse(room.getGameSetting(), room.getCurrentQuestion().getQuiz()));
117175
messageSender.send(destination, MessageType.ROOM_SETTING, toRoomSettingResponse(room));
176+
177+
List<Player> disconnectedPlayers = room.getDisconnectedPlayers();
178+
roomService.handleDisconnectedPlayers(room, disconnectedPlayers);
118179
}
119180

120181
private boolean validateReadyStatus(Room room) {

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

Lines changed: 30 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,6 @@
5959
@RequiredArgsConstructor
6060
public class RoomService {
6161

62-
private final GameService gameService;
63-
private final TimerService timerService;
6462
private final QuizService quizService;
6563
private final RoomRepository roomRepository;
6664
private final AtomicLong roomIdGenerator = new AtomicLong(0);
@@ -232,54 +230,6 @@ public RoomListResponse getAllRooms() {
232230
return new RoomListResponse(roomResponses);
233231
}
234232

235-
// todo 동시성적용
236-
public void chat(Long roomId, String sessionId, ChatMessage chatMessage) {
237-
238-
Room room = findRoom(roomId);
239-
240-
String destination = getDestination(roomId);
241-
242-
messageSender.send(destination, MessageType.CHAT, chatMessage);
243-
244-
if (!room.isPlaying()) {
245-
return;
246-
}
247-
248-
Question currentQuestion = room.getCurrentQuestion();
249-
250-
String answer = currentQuestion.getAnswer();
251-
252-
if (answer.equals(chatMessage.message())) {
253-
room.increasePlayerCorrectCount(sessionId);
254-
255-
messageSender.send(
256-
destination,
257-
MessageType.QUESTION_RESULT,
258-
toQuestionResultResponse(chatMessage.nickname(), answer));
259-
messageSender.send(destination, MessageType.RANK_UPDATE, toRankUpdateResponse(room));
260-
messageSender.send(
261-
destination,
262-
MessageType.SYSTEM_NOTICE,
263-
ofPlayerEvent(chatMessage.nickname(), RoomEventType.CORRECT_ANSWER));
264-
265-
timerService.cancelTimer(room);
266-
267-
if (!timerService.validateCurrentRound(room)) {
268-
gameService.gameEnd(room);
269-
return;
270-
}
271-
272-
room.increaseCurrentRound();
273-
274-
// 타이머 추가하기
275-
timerService.startTimer(room, CONTINUE_DELAY);
276-
messageSender.send(
277-
destination,
278-
MessageType.QUESTION_START,
279-
toQuestionStartResponse(room, CONTINUE_DELAY));
280-
}
281-
}
282-
283233
private Player getRemovePlayer(Room room, String sessionId, UserPrincipal principal) {
284234
Player removePlayer = room.getPlayerSessionMap().get(sessionId);
285235
if (removePlayer == null) {
@@ -297,7 +247,7 @@ private Player createPlayer() {
297247
return new Player(getCurrentUserId(), getCurrentUserNickname());
298248
}
299249

300-
private Room findRoom(Long roomId) {
250+
public Room findRoom(Long roomId) {
301251
return roomRepository
302252
.findRoom(roomId)
303253
.orElseThrow(() -> new CustomException(RoomErrorCode.ROOM_NOT_FOUND));
@@ -339,28 +289,41 @@ private void removePlayer(Room room, String sessionId, Player removePlayer) {
339289

340290
public void exitRoomForDisconnectedPlayer(Long roomId, Player player, String sessionId) {
341291

342-
// 연결 끊긴 플레이어 exit 로직 타게 해주기
343-
Room room = findRoom(roomId);
292+
Object lock = roomLocks.computeIfAbsent(roomId, k -> new Object());
344293

345-
/* 방 삭제 */
346-
if (isLastPlayer(room, sessionId)) {
347-
removeRoom(room);
348-
return;
349-
}
294+
synchronized (lock) {
295+
// 연결 끊긴 플레이어 exit 로직 타게 해주기
296+
Room room = findRoom(roomId);
350297

351-
/* 방장 변경 */
352-
if (room.isHost(player.getId())) {
353-
changeHost(room, sessionId);
354-
}
298+
/* 방 삭제 */
299+
if (isLastPlayer(room, sessionId)) {
300+
removeRoom(room);
301+
return;
302+
}
355303

356-
/* 플레이어 삭제 */
357-
removePlayer(room, sessionId, player);
304+
/* 방장 변경 */
305+
if (room.isHost(player.getId())) {
306+
changeHost(room, sessionId);
307+
}
358308

359-
SystemNoticeResponse systemNoticeResponse =
309+
/* 플레이어 삭제 */
310+
removePlayer(room, sessionId, player);
311+
312+
String destination = getDestination(roomId);
313+
314+
SystemNoticeResponse systemNoticeResponse =
360315
ofPlayerEvent(player.nickname, RoomEventType.EXIT);
361316

362-
String destination = getDestination(roomId);
317+
messageSender.send(destination, MessageType.PLAYER_LIST, toPlayerListResponse(room));
318+
messageSender.send(destination, MessageType.SYSTEM_NOTICE, systemNoticeResponse);
319+
}
363320

364-
messageSender.send(destination, MessageType.SYSTEM_NOTICE, systemNoticeResponse);
321+
}
322+
323+
public void handleDisconnectedPlayers(Room room, List<Player> disconnectedPlayers) {
324+
for (Player player : disconnectedPlayers) {
325+
String sessionId = room.getSessionIdByUserId(player.getId());
326+
exitRoomForDisconnectedPlayer(room.getId(), player, sessionId);
327+
}
365328
}
366329
}

0 commit comments

Comments
 (0)