-
Notifications
You must be signed in to change notification settings - Fork 3
[refactor] 재연결 로직 리팩토링 #159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
645051f
928b61a
31a3b88
1c4f211
2667919
49ddedf
37ff1ba
29ffb6e
ae4589a
d964393
36f0bdd
223c03b
51f58d2
cfa23c9
ac79715
683be42
e51fbe8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ | |
| import static io.f1.backend.domain.quiz.mapper.QuizMapper.toGameStartResponse; | ||
| import static io.f1.backend.global.util.SecurityUtils.getCurrentUserId; | ||
| import static io.f1.backend.global.util.SecurityUtils.getCurrentUserNickname; | ||
| import static io.f1.backend.global.util.SecurityUtils.getCurrentUserPrincipal; | ||
|
|
||
| import io.f1.backend.domain.game.dto.MessageType; | ||
| import io.f1.backend.domain.game.dto.RoomEventType; | ||
|
|
@@ -34,13 +35,16 @@ | |
| import io.f1.backend.domain.game.model.RoomSetting; | ||
| import io.f1.backend.domain.game.model.RoomState; | ||
| import io.f1.backend.domain.game.store.RoomRepository; | ||
| import io.f1.backend.domain.game.store.UserRoomRepository; | ||
| import io.f1.backend.domain.game.websocket.DisconnectTaskManager; | ||
| import io.f1.backend.domain.game.websocket.MessageSender; | ||
| import io.f1.backend.domain.quiz.app.QuizService; | ||
| import io.f1.backend.domain.quiz.dto.QuizMinData; | ||
| 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.RoomErrorCode; | ||
| import io.f1.backend.global.exception.errorcode.UserErrorCode; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
|
|
@@ -61,9 +65,11 @@ public class RoomService { | |
|
|
||
| private final QuizService quizService; | ||
| private final RoomRepository roomRepository; | ||
| private final UserRoomRepository userRoomRepository; | ||
| private final AtomicLong roomIdGenerator = new AtomicLong(0); | ||
| private final ApplicationEventPublisher eventPublisher; | ||
| private final Map<Long, Object> roomLocks = new ConcurrentHashMap<>(); | ||
| private final DisconnectTaskManager disconnectTasks; | ||
|
|
||
| private final MessageSender messageSender; | ||
|
|
||
|
|
@@ -85,10 +91,13 @@ public RoomCreateResponse saveRoom(RoomCreateRequest request) { | |
|
|
||
| Room room = new Room(newId, roomSetting, gameSetting, host); | ||
|
|
||
| room.addValidatedUserId(getCurrentUserId()); | ||
| room.addPlayer(host); | ||
|
|
||
| roomRepository.saveRoom(room); | ||
|
|
||
| /* 다른 방 접속 시 기존 방은 exit 처리 - 탭 동시 로그인 시 (disconnected 리스너 작동x) */ | ||
| exitIfInAnotherRoom(room, host.getId()); | ||
|
|
||
| eventPublisher.publishEvent(new RoomCreatedEvent(room, quiz)); | ||
|
|
||
| return new RoomCreateResponse(newId); | ||
|
|
@@ -103,6 +112,16 @@ public void enterRoom(RoomValidationRequest request) { | |
| synchronized (lock) { | ||
| Room room = findRoom(request.roomId()); | ||
|
|
||
| Long userId = getCurrentUserId(); | ||
|
|
||
| /* 다른 방 접속 시 기존 방은 exit 처리 - 탭 동시 로그인 시 (disconnected 리스너 작동x) */ | ||
| exitIfInAnotherRoom(room, userId); | ||
|
|
||
| /* reconnect */ | ||
| if (room.hasPlayer(userId)) { | ||
| return; | ||
| } | ||
|
|
||
| if (room.getState().equals(RoomState.PLAYING)) { | ||
| throw new CustomException(RoomErrorCode.ROOM_GAME_IN_PROGRESS); | ||
| } | ||
|
|
@@ -118,17 +137,42 @@ public void enterRoom(RoomValidationRequest request) { | |
| throw new CustomException(RoomErrorCode.WRONG_PASSWORD); | ||
| } | ||
|
|
||
| room.addValidatedUserId(getCurrentUserId()); | ||
| room.addPlayer(createPlayer()); | ||
| } | ||
| } | ||
|
|
||
| private void exitIfInAnotherRoom(Room room, Long userId) { | ||
|
|
||
| Long joinedRoomId = userRoomRepository.getRoomId(userId); | ||
|
|
||
| if (joinedRoomId != null && !room.getId().equals(joinedRoomId)) { | ||
|
||
|
|
||
| if (room.isPlaying()) { | ||
| changeConnectedStatus(userId, ConnectionState.DISCONNECTED); | ||
| } else { | ||
| exitRoom(joinedRoomId, getCurrentUserPrincipal()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public void initializeRoomSocket(Long roomId, String sessionId, UserPrincipal principal) { | ||
| public void initializeRoomSocket(Long roomId, UserPrincipal principal) { | ||
|
|
||
| Room room = findRoom(roomId); | ||
| Long userId = principal.getUserId(); | ||
|
|
||
| Player player = createPlayer(principal); | ||
| if (!room.hasPlayer(userId)) { | ||
| throw new CustomException(RoomErrorCode.ROOM_ENTER_REQUIRED); | ||
| } | ||
|
|
||
| /* 재연결 */ | ||
| if (room.getPlayerState(userId).equals(ConnectionState.DISCONNECTED)) { | ||
|
||
| changeConnectedStatus(userId, ConnectionState.CONNECTED); | ||
| cancelTask(userId); | ||
| reconnectSendResponse(roomId, principal); | ||
| return; | ||
| } | ||
|
|
||
| room.addPlayer(sessionId, player); | ||
| Player player = createPlayer(principal); | ||
|
|
||
| RoomSettingResponse roomSettingResponse = toRoomSettingResponse(room); | ||
|
|
||
|
|
@@ -145,6 +189,8 @@ public void initializeRoomSocket(Long roomId, String sessionId, UserPrincipal pr | |
|
|
||
| String destination = getDestination(roomId); | ||
|
|
||
| userRoomRepository.addUser(player, room); | ||
|
|
||
| messageSender.sendPersonal( | ||
| getUserDestination(), MessageType.GAME_SETTING, gameSettingResponse, principal); | ||
|
|
||
|
|
@@ -153,25 +199,29 @@ public void initializeRoomSocket(Long roomId, String sessionId, UserPrincipal pr | |
| messageSender.sendBroadcast(destination, MessageType.SYSTEM_NOTICE, systemNoticeResponse); | ||
| } | ||
|
|
||
| public void exitRoom(Long roomId, String sessionId, UserPrincipal principal) { | ||
| public void exitRoom(Long roomId, UserPrincipal principal) { | ||
|
|
||
| Object lock = roomLocks.computeIfAbsent(roomId, k -> new Object()); | ||
|
|
||
| synchronized (lock) { | ||
| Room room = findRoom(roomId); | ||
|
|
||
| Player removePlayer = getRemovePlayer(room, sessionId, principal); | ||
| if (!room.hasPlayer(principal.getUserId())) { | ||
| throw new CustomException(UserErrorCode.USER_NOT_FOUND); | ||
| } | ||
|
|
||
| Player removePlayer = createPlayer(principal); | ||
|
|
||
| String destination = getDestination(roomId); | ||
|
|
||
| cleanRoom(room, removePlayer); | ||
|
|
||
| messageSender.sendPersonal( | ||
| getUserDestination(), | ||
| MessageType.EXIT_SUCCESS, | ||
| new ExitSuccessResponse(true), | ||
| principal); | ||
|
|
||
| cleanRoom(room, sessionId, removePlayer); | ||
|
|
||
| SystemNoticeResponse systemNoticeResponse = | ||
| ofPlayerEvent(removePlayer.nickname, RoomEventType.EXIT); | ||
|
|
||
|
|
@@ -198,10 +248,8 @@ public RoomListResponse getAllRooms() { | |
| return new RoomListResponse(roomResponses); | ||
| } | ||
|
|
||
| public void reconnectSession( | ||
| Long roomId, String oldSessionId, String newSessionId, UserPrincipal principal) { | ||
| public void reconnectSendResponse(Long roomId, UserPrincipal principal) { | ||
| Room room = findRoom(roomId); | ||
| room.reconnectSession(oldSessionId, newSessionId); | ||
|
|
||
| String destination = getDestination(roomId); | ||
| String userDestination = getUserDestination(); | ||
|
|
@@ -249,30 +297,26 @@ public void reconnectSession( | |
| } | ||
| } | ||
|
|
||
| public void changeConnectedStatus(Long roomId, String sessionId, ConnectionState newState) { | ||
| public Long changeConnectedStatus(Long userId, ConnectionState newState) { | ||
| Long roomId = userRoomRepository.getRoomId(userId); | ||
| Room room = findRoom(roomId); | ||
| room.updatePlayerConnectionState(sessionId, newState); | ||
| } | ||
|
|
||
| public boolean isExit(String sessionId, Long roomId) { | ||
| Room room = findRoom(roomId); | ||
| return room.isExit(sessionId); | ||
| room.updatePlayerConnectionState(userId, newState); | ||
|
|
||
| return roomId; | ||
| } | ||
|
|
||
| public void exitIfNotPlaying(Long roomId, String sessionId, UserPrincipal principal) { | ||
| Room room = findRoom(roomId); | ||
| if (!room.isPlaying()) { | ||
| exitRoom(roomId, sessionId, principal); | ||
| } | ||
| public void cancelTask(Long userId) { | ||
| disconnectTasks.cancelDisconnectTask(userId); | ||
| } | ||
|
|
||
| private Player getRemovePlayer(Room room, String sessionId, UserPrincipal principal) { | ||
| Player removePlayer = room.getPlayerSessionMap().get(sessionId); | ||
| if (removePlayer == null) { | ||
| room.removeValidatedUserId(principal.getUserId()); | ||
| throw new CustomException(RoomErrorCode.SOCKET_SESSION_NOT_FOUND); | ||
| public void exitIfNotPlaying(Long roomId, UserPrincipal principal) { | ||
| Room room = findRoom(roomId); | ||
| if (room.isPlaying()) { | ||
| removeUserRepository(principal.getUserId(), roomId); | ||
| } else { | ||
| exitRoom(roomId, principal); | ||
| } | ||
| return removePlayer; | ||
| } | ||
|
|
||
| private Player createPlayer(UserPrincipal principal) { | ||
|
|
@@ -296,43 +340,33 @@ private void removeRoom(Room room) { | |
| log.info("{}번 방 삭제", roomId); | ||
| } | ||
|
|
||
| private void changeHost(Room room, String hostSessionId) { | ||
| Map<String, Player> playerSessionMap = room.getPlayerSessionMap(); | ||
| private void changeHost(Room room, Player host) { | ||
| Map<Long, Player> playerMap = room.getPlayerMap(); | ||
|
|
||
| Optional<String> nextHostSessionId = | ||
| playerSessionMap.entrySet().stream() | ||
| .filter(entry -> !entry.getKey().equals(hostSessionId)) | ||
| Optional<Player> nextHost = | ||
| playerMap.entrySet().stream() | ||
| .filter(entry -> !entry.getKey().equals(host.getId())) | ||
| .filter(entry -> entry.getValue().getState() == ConnectionState.CONNECTED) | ||
| .map(Map.Entry::getKey) | ||
| .map(Map.Entry::getValue) | ||
| .findFirst(); | ||
|
|
||
| Player nextHost = | ||
| playerSessionMap.get( | ||
| nextHostSessionId.orElseThrow( | ||
| () -> new CustomException(RoomErrorCode.SOCKET_SESSION_NOT_FOUND))); | ||
|
|
||
| room.updateHost(nextHost); | ||
| log.info("user_id:{} 방장 변경 완료 ", nextHost.getId()); | ||
| } | ||
|
|
||
| private void removePlayer(Room room, String sessionId, Player removePlayer) { | ||
| room.removeSessionId(sessionId); | ||
| room.removeValidatedUserId(removePlayer.getId()); | ||
| room.updateHost( | ||
| nextHost.orElseThrow(() -> new CustomException(RoomErrorCode.PLAYER_NOT_FOUND))); | ||
| } | ||
|
|
||
| private String getUserDestination() { | ||
| return "/queue"; | ||
| } | ||
|
|
||
| public void exitRoomForDisconnectedPlayer(Long roomId, Player player, String sessionId) { | ||
| public void exitRoomForDisconnectedPlayer(Long roomId, Player player) { | ||
|
|
||
| Object lock = roomLocks.computeIfAbsent(roomId, k -> new Object()); | ||
|
|
||
| synchronized (lock) { | ||
| // 연결 끊긴 플레이어 exit 로직 타게 해주기 | ||
| Room room = findRoom(roomId); | ||
|
|
||
| cleanRoom(room, sessionId, player); | ||
| cleanRoom(room, player); | ||
|
|
||
| String destination = getDestination(roomId); | ||
|
|
||
|
|
@@ -346,28 +380,46 @@ public void exitRoomForDisconnectedPlayer(Long roomId, Player player, String ses | |
| } | ||
| } | ||
|
|
||
| private void cleanRoom(Room room, String sessionId, Player player) { | ||
| private void cleanRoom(Room room, Player player) { | ||
|
|
||
| Long roomId = room.getId(); | ||
| Long userId = player.getId(); | ||
|
|
||
| /* user-room mapping 정보 삭제 */ | ||
| removeUserRepository(userId, roomId); | ||
|
|
||
| /* 방 삭제 */ | ||
| if (room.isLastPlayer(sessionId)) { | ||
| if (room.isLastPlayer(player)) { | ||
| removeRoom(room); | ||
| Long roomId = room.getId(); | ||
| eventPublisher.publishEvent(new RoomDeletedEvent(roomId)); | ||
| return; | ||
| } | ||
|
|
||
| /* 방장 변경 */ | ||
| if (room.isHost(player.getId())) { | ||
| changeHost(room, sessionId); | ||
| if (room.isHost(userId)) { | ||
| changeHost(room, player); | ||
| } | ||
|
|
||
| /* 플레이어 삭제 */ | ||
| removePlayer(room, sessionId, player); | ||
| room.removePlayer(player); | ||
| } | ||
|
|
||
| public void handleDisconnectedPlayers(Room room, List<Player> disconnectedPlayers) { | ||
| for (Player player : disconnectedPlayers) { | ||
| String sessionId = room.getSessionIdByUserId(player.getId()); | ||
| exitRoomForDisconnectedPlayer(room.getId(), player, sessionId); | ||
| exitRoomForDisconnectedPlayer(room.getId(), player); | ||
| } | ||
| } | ||
|
|
||
| public ConnectionState getPlayerState(Long userId, Long roomId) { | ||
| Room room = findRoom(roomId); | ||
| return room.getPlayerState(userId); | ||
| } | ||
|
|
||
| public void removeUserRepository(Long userId, Long roomId) { | ||
| userRoomRepository.removeUser(userId, roomId); | ||
| } | ||
|
|
||
| public boolean isUserInAnyRoom(Long userId) { | ||
| return userRoomRepository.isUserInAnyRoom(userId); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
room.isPlaying() 메서드로 대체할 수 있을 것 같습니다 !