Skip to content

Commit d8e82fe

Browse files
committed
✨ 방 퇴장 구현
1 parent 5d6071c commit d8e82fe

File tree

8 files changed

+132
-36
lines changed

8 files changed

+132
-36
lines changed
Lines changed: 77 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
package io.f1.backend.domain.game.app;
22

3-
import static io.f1.backend.domain.game.mapper.RoomMapper.*;
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.toRoomResponse;
6+
import static io.f1.backend.domain.game.mapper.RoomMapper.toRoomSetting;
7+
import static io.f1.backend.domain.game.mapper.RoomMapper.toRoomSettingResponse;
48

9+
import io.f1.backend.domain.game.dto.RoomExitData;
510
import io.f1.backend.domain.game.dto.RoomInitialData;
611
import io.f1.backend.domain.game.dto.request.RoomCreateRequest;
712
import io.f1.backend.domain.game.dto.request.RoomValidationRequest;
@@ -12,6 +17,7 @@
1217
import io.f1.backend.domain.game.dto.response.RoomListResponse;
1318
import io.f1.backend.domain.game.dto.response.RoomResponse;
1419
import io.f1.backend.domain.game.dto.response.RoomSettingResponse;
20+
import io.f1.backend.domain.game.dto.response.SystemNoticeResponse;
1521
import io.f1.backend.domain.game.model.GameSetting;
1622
import io.f1.backend.domain.game.model.Player;
1723
import io.f1.backend.domain.game.model.Room;
@@ -20,14 +26,13 @@
2026
import io.f1.backend.domain.game.store.RoomRepository;
2127
import io.f1.backend.domain.quiz.entity.Quiz;
2228
import io.f1.backend.domain.user.entity.User;
23-
24-
import lombok.RequiredArgsConstructor;
25-
26-
import org.springframework.stereotype.Service;
27-
29+
import java.time.LocalDateTime;
2830
import java.util.List;
2931
import java.util.Map;
32+
import java.util.Optional;
3033
import java.util.concurrent.atomic.AtomicLong;
34+
import lombok.RequiredArgsConstructor;
35+
import org.springframework.stereotype.Service;
3136

3237
@Service
3338
@RequiredArgsConstructor
@@ -54,9 +59,9 @@ public RoomCreateResponse saveRoom(RoomCreateRequest request, Map<String, Object
5459
public void validateRoom(RoomValidationRequest request) {
5560

5661
Room room =
57-
roomRepository
58-
.findRoom(request.roomId())
59-
.orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다."));
62+
roomRepository
63+
.findRoom(request.roomId())
64+
.orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다."));
6065

6166
if (room.getState().equals(RoomState.PLAYING)) {
6267
throw new IllegalArgumentException("403 게임이 진행중입니다.");
@@ -69,17 +74,17 @@ public void validateRoom(RoomValidationRequest request) {
6974
}
7075

7176
if (room.getRoomSetting().locked()
72-
&& !room.getRoomSetting().password().equals(request.password())) {
77+
&& !room.getRoomSetting().password().equals(request.password())) {
7378
throw new IllegalArgumentException("401 비밀번호가 일치하지 않습니다.");
7479
}
7580
}
7681

7782
public RoomInitialData enterRoom(Long roomId, String sessionId) {
7883

7984
Room room =
80-
roomRepository
81-
.findRoom(roomId)
82-
.orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다."));
85+
roomRepository
86+
.findRoom(roomId)
87+
.orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다."));
8388

8489
// todo security
8590
Player player = new Player(1L, "빵야빵야");
@@ -88,41 +93,79 @@ public RoomInitialData enterRoom(Long roomId, String sessionId) {
8893

8994
playerSessionMap.put(sessionId, player);
9095

91-
String destination = "/sub/room/" + roomId;
92-
9396
RoomSettingResponse roomSettingResponse = toRoomSettingResponse(room);
97+
9498
// todo quiz 생성 api 완성 후 수정
9599
QuizResponse quiz =
96-
new QuizResponse(room.getGameSetting().getQuizId(), "title", "설명", "url", 10);
100+
new QuizResponse(room.getGameSetting().getQuizId(), "title", "설명", "url", 10);
97101
GameSettingResponse gameSettingResponse =
98-
toGameSettingResponse(room.getGameSetting(), quiz);
102+
toGameSettingResponse(room.getGameSetting(), quiz);
99103

100104
PlayerListResponse playerListResponse = toPlayerListResponse(room);
101105

102106
return new RoomInitialData(
103-
destination, roomSettingResponse, gameSettingResponse, playerListResponse);
107+
getDestination(roomId), roomSettingResponse, gameSettingResponse, playerListResponse);
108+
}
109+
110+
public RoomExitData exitRoom(Long roomId, String sessionId) {
111+
Room room =
112+
roomRepository
113+
.findRoom(roomId)
114+
.orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다."));
115+
116+
Map<String, Player> playerSessionMap = room.getPlayerSessionMap();
117+
118+
String destination = getDestination(roomId);
119+
120+
if (playerSessionMap.size() == 1 && playerSessionMap.get(sessionId) != null) {
121+
roomRepository.removeRoom(roomId);
122+
return new RoomExitData(destination,null, null, true);
123+
}
124+
125+
Player removedPlayer = playerSessionMap.remove(sessionId);
126+
if (removedPlayer == null) {
127+
throw new IllegalArgumentException("퇴장 처리 불가 - 404 해당 세션 플레이어는 존재하지않습니다.");
128+
}
129+
130+
if (room.getHost().getId().equals(removedPlayer.getId())) {
131+
Optional<String> nextHostSessionId = playerSessionMap.keySet().stream().findFirst();
132+
Player nextHost = playerSessionMap.get(nextHostSessionId.orElseThrow(
133+
() -> new IllegalArgumentException("방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다.")));
134+
room.updateHost(nextHost);
135+
}
136+
137+
SystemNoticeResponse systemNoticeResponse = new SystemNoticeResponse(
138+
removedPlayer.getNickname() + "님이 퇴장하셨습니다.", LocalDateTime.now());
139+
140+
PlayerListResponse playerListResponse = toPlayerListResponse(room);
141+
142+
return new RoomExitData(destination,playerListResponse, systemNoticeResponse, false);
104143
}
105144

106145
// todo quizService에서 퀴즈 조회 메서드로 변경
107146
public RoomListResponse getAllRooms() {
108147
List<Room> rooms = roomRepository.findAll();
109148
List<RoomResponse> roomResponses =
110-
rooms.stream()
111-
.map(
112-
room -> {
113-
User user = new User(); // 임시 유저 객체
114-
user.setNickname("임시 유저 닉네임");
115-
116-
Quiz quiz = new Quiz(); // 임시 퀴즈 객체
117-
quiz.setTitle("임시 퀴즈 제목");
118-
quiz.setDescription("임시 퀴즈 설명");
119-
quiz.setThumbnailUrl("임시 이미지");
120-
quiz.setQuestions(List.of());
121-
quiz.setCreator(user);
122-
123-
return toRoomResponse(room, quiz);
124-
})
125-
.toList();
149+
rooms.stream()
150+
.map(
151+
room -> {
152+
User user = new User(); // 임시 유저 객체
153+
user.setNickname("임시 유저 닉네임");
154+
155+
Quiz quiz = new Quiz(); // 임시 퀴즈 객체
156+
quiz.setTitle("임시 퀴즈 제목");
157+
quiz.setDescription("임시 퀴즈 설명");
158+
quiz.setThumbnailUrl("임시 이미지");
159+
quiz.setQuestions(List.of());
160+
quiz.setCreator(user);
161+
162+
return toRoomResponse(room, quiz);
163+
})
164+
.toList();
126165
return new RoomListResponse(roomResponses);
127166
}
167+
168+
private static String getDestination(Long roomId) {
169+
return "/sub/room/" + roomId;
170+
}
128171
}

backend/src/main/java/io/f1/backend/domain/game/dto/MessageType.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ public enum MessageType {
44
ROOM_SETTING,
55
GAME_SETTING,
66
PLAYER_LIST,
7+
SYSTEM_NOTICE,
78
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.f1.backend.domain.game.dto;
2+
3+
import io.f1.backend.domain.game.dto.response.PlayerListResponse;
4+
import io.f1.backend.domain.game.dto.response.SystemNoticeResponse;
5+
6+
public record RoomExitData(String destination, PlayerListResponse playerListResponses,
7+
SystemNoticeResponse systemNoticeResponse,
8+
boolean removedRoom) {
9+
10+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.f1.backend.domain.game.dto.response;
2+
3+
import java.time.LocalDateTime;
4+
5+
public record SystemNoticeResponse(String noticeMessage, LocalDateTime timestamp) {
6+
7+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,8 @@ public Room(Long id, RoomSetting roomSetting, GameSetting gameSetting, Player ho
3535
this.gameSetting = gameSetting;
3636
this.host = host;
3737
}
38+
39+
public void updateHost(Player nextHost){
40+
this.host = nextHost;
41+
}
3842
}

backend/src/main/java/io/f1/backend/domain/game/store/RoomRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ public interface RoomRepository {
1111
Optional<Room> findRoom(Long roomId);
1212

1313
List<Room> findAll();
14+
15+
void removeRoom(Long roomId);
1416
}

backend/src/main/java/io/f1/backend/domain/game/store/RoomRepositoryImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ public List<Room> findAll() {
3030
return new ArrayList<>(roomMap.values());
3131
}
3232

33+
@Override
34+
public void removeRoom(Long roomId) {
35+
roomMap.remove(roomId);
36+
}
37+
3338
// 테스트 전용 메소드
3439
public Room getRoomForTest(Long roomId) {
3540
return roomMap.get(roomId);

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

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

33
import io.f1.backend.domain.game.app.RoomService;
44
import io.f1.backend.domain.game.dto.MessageType;
5+
import io.f1.backend.domain.game.dto.RoomExitData;
56
import io.f1.backend.domain.game.dto.RoomInitialData;
67

78
import lombok.RequiredArgsConstructor;
@@ -22,8 +23,7 @@ public class GameSocketController {
2223
@MessageMapping("/room/enter/{roomId}")
2324
public void roomEnter(@DestinationVariable Long roomId, Message<?> message) {
2425

25-
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
26-
String websocketSessionId = accessor.getSessionId();
26+
String websocketSessionId = getSessionId(message);
2727

2828
RoomInitialData roomInitialData = roomService.enterRoom(roomId, websocketSessionId);
2929
String destination = roomInitialData.destination();
@@ -35,4 +35,28 @@ public void roomEnter(@DestinationVariable Long roomId, Message<?> message) {
3535
messageSender.send(
3636
destination, MessageType.PLAYER_LIST, roomInitialData.playerListResponse());
3737
}
38+
39+
@MessageMapping("/room/exit/{roomId}")
40+
public void exitRoom(@DestinationVariable Long roomId, Message<?> message) {
41+
42+
String websocketSessionId = getSessionId(message);
43+
44+
RoomExitData roomExitData = roomService.exitRoom(roomId, websocketSessionId);
45+
46+
String destination = roomExitData.destination();
47+
48+
if (!roomExitData.removedRoom()) {
49+
messageSender.send(
50+
destination, MessageType.PLAYER_LIST, roomExitData.playerListResponses()
51+
);
52+
messageSender.send(
53+
destination , MessageType.SYSTEM_NOTICE, roomExitData.systemNoticeResponse()
54+
);
55+
}
56+
}
57+
58+
private static String getSessionId(Message<?> message) {
59+
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
60+
return accessor.getSessionId();
61+
}
3862
}

0 commit comments

Comments
 (0)