3434import java .util .List ;
3535import java .util .Map ;
3636import java .util .Optional ;
37+ import java .util .concurrent .ConcurrentHashMap ;
3738import java .util .concurrent .atomic .AtomicLong ;
3839import lombok .RequiredArgsConstructor ;
40+ import lombok .extern .slf4j .Slf4j ;
41+ import org .hibernate .boot .model .naming .IllegalIdentifierException ;
3942import org .springframework .context .ApplicationEventPublisher ;
4043import org .springframework .stereotype .Service ;
4144
45+ @ Slf4j
4246@ Service
4347@ RequiredArgsConstructor
4448public class RoomService {
@@ -47,6 +51,8 @@ public class RoomService {
4751 private final RoomRepository roomRepository ;
4852 private final AtomicLong roomIdGenerator = new AtomicLong (0 );
4953 private final ApplicationEventPublisher eventPublisher ;
54+ private final Map <Long , Object > roomLocks = new ConcurrentHashMap <>();
55+ private static final String PENDING_SESSION_ID = "PENDING_SESSION_ID" ;
5056
5157 public RoomCreateResponse saveRoom (RoomCreateRequest request ) {
5258
@@ -63,6 +69,8 @@ public RoomCreateResponse saveRoom(RoomCreateRequest request) {
6369
6470 Room room = new Room (newId , roomSetting , gameSetting , host );
6571
72+ room .getUserIdSessionMap ().put (host .id ,PENDING_SESSION_ID );
73+
6674 roomRepository .saveRoom (room );
6775
6876 eventPublisher .publishEvent (new RoomCreatedEvent (room , quiz ));
@@ -72,24 +80,31 @@ public RoomCreateResponse saveRoom(RoomCreateRequest request) {
7280
7381 public void enterRoom (RoomValidationRequest request ) {
7482
75- Room room = findRoom ( request .roomId () );
83+ Long roomId = request .roomId ();
7684
77- if (room .getState ().equals (RoomState .PLAYING )) {
78- throw new IllegalArgumentException ("403 게임이 진행중입니다." );
79- }
85+ Object lock = roomLocks .computeIfAbsent (roomId , k -> new Object ());
8086
81- int maxUserCnt = room .getRoomSetting ().maxUserCount ();
82- int currentCnt = room .getUserIdSessionMap ().size ();
83- if (maxUserCnt == currentCnt ) {
84- throw new IllegalArgumentException ("403 정원이 모두 찼습니다." );
85- }
87+ synchronized (lock ) {
8688
87- if (room .getRoomSetting ().locked ()
88- && !room .getRoomSetting ().password ().equals (request .password ())) {
89- throw new IllegalArgumentException ("401 비밀번호가 일치하지 않습니다." );
90- }
89+ Room room = findRoom (request .roomId ());
90+
91+ if (room .getState ().equals (RoomState .PLAYING )) {
92+ throw new IllegalArgumentException ("403 게임이 진행중입니다." );
93+ }
9194
92- room .getUserIdSessionMap ().put (getCurrentUserId (), "" );
95+ int maxUserCnt = room .getRoomSetting ().maxUserCount ();
96+ int currentCnt = room .getUserIdSessionMap ().size ();
97+ if (maxUserCnt == currentCnt ) {
98+ throw new IllegalArgumentException ("403 정원이 모두 찼습니다." );
99+ }
100+
101+ if (room .getRoomSetting ().locked ()
102+ && !room .getRoomSetting ().password ().equals (request .password ())) {
103+ throw new IllegalArgumentException ("401 비밀번호가 일치하지 않습니다." );
104+ }
105+
106+ room .getUserIdSessionMap ().put (getCurrentUserId (), PENDING_SESSION_ID );
107+ }
93108 }
94109
95110 public RoomInitialData initializeRoomSocket (Long roomId , String sessionId ) {
@@ -102,7 +117,11 @@ public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) {
102117 Map <Long , String > userIdSessionMap = room .getUserIdSessionMap ();
103118
104119 playerSessionMap .put (sessionId , player );
105- userIdSessionMap .put (roomId , sessionId );
120+ String existingSession = userIdSessionMap .get (player .getId ());
121+ /* 정상 흐름 or 재연결 */
122+ if (existingSession .equals (PENDING_SESSION_ID ) || !existingSession .equals (sessionId )) {
123+ userIdSessionMap .put (player .getId (), sessionId );
124+ }
106125
107126 RoomSettingResponse roomSettingResponse = toRoomSettingResponse (room );
108127
@@ -125,50 +144,59 @@ public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) {
125144 }
126145
127146 public RoomExitData exitRoom (Long roomId , String sessionId ) {
128- Room room = findRoom (roomId );
129147
130- Map <String , Player > playerSessionMap = room .getPlayerSessionMap ();
148+ Object lock = roomLocks .computeIfAbsent (roomId , k -> new Object ());
149+
150+ synchronized (lock ) {
151+
152+ Room room = findRoom (roomId );
131153
132- String destination = getDestination ( roomId );
154+ Map < String , Player > playerSessionMap = room . getPlayerSessionMap ( );
133155
134- Player removePlayer = playerSessionMap . get ( sessionId );
156+ String destination = getDestination ( roomId );
135157
136- // if (removePlayer == null) {
137- // room.getUserIdSessionMap().remove(getCurrentUserId());
138- // throw new IllegalIdentifierException("404 세션 없음 비정상적인 퇴장 요청");
139- // }
158+ Player removePlayer = playerSessionMap .get (sessionId );
140159
141- /* 방 삭제 */
142- if (playerSessionMap .size () == 1 && playerSessionMap .containsKey (sessionId )) {
143- roomRepository .removeRoom (roomId );
144- return RoomExitData .builder ().destination (destination ).removedRoom (true ).build ();
160+ if (removePlayer == null ) {
161+ room .getUserIdSessionMap ().remove (getCurrentUserId ());
162+ throw new IllegalIdentifierException ("404 세션 없음 비정상적인 퇴장 요청" );
145163 }
146164
147- /* 방장 변경 */
148- if (room .getHost ().getId ().equals (removePlayer .getId ())) {
165+ /* 방 삭제 */
166+ if (playerSessionMap .size () == 1 && playerSessionMap .containsKey (sessionId )) {
167+ roomRepository .removeRoom (roomId );
168+ roomLocks .remove (roomId );
169+ log .info ("{}번 방 삭제" , roomId );
170+ return RoomExitData .builder ().destination (destination ).removedRoom (true ).build ();
171+ }
149172
150- Optional < String > nextHostSessionId = playerSessionMap . keySet (). stream ()
151- . filter ( key -> ! key . equals ( sessionId )). findFirst ();
173+ /* 방장 변경 */
174+ if ( room . getHost (). getId (). equals ( removePlayer . getId ())) {
152175
153- Player nextHost =
154- playerSessionMap .get (
155- nextHostSessionId .orElseThrow (
156- () ->
157- new IllegalArgumentException (
158- "방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다." )));
176+ Optional <String > nextHostSessionId = playerSessionMap .keySet ().stream ()
177+ .filter (key -> !key .equals (sessionId )).findFirst ();
159178
160- room .updateHost (nextHost );
161- }
179+ Player nextHost =
180+ playerSessionMap .get (
181+ nextHostSessionId .orElseThrow (
182+ () ->
183+ new IllegalArgumentException (
184+ "방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다." )));
162185
163- room .getUserIdSessionMap ().remove (removePlayer .getId ());
164- playerSessionMap .remove (sessionId );
186+ room .updateHost (nextHost );
187+ log .info ("user_id:{} 방장 변경 완료 " , nextHost .getId ());
188+ }
165189
166- SystemNoticeResponse systemNoticeResponse =
167- ofPlayerEvent ( removePlayer , RoomEventType . EXIT );
190+ room . getUserIdSessionMap (). remove ( removePlayer . getId ());
191+ playerSessionMap . remove ( sessionId );
168192
169- PlayerListResponse playerListResponse = toPlayerListResponse (room );
193+ SystemNoticeResponse systemNoticeResponse =
194+ ofPlayerEvent (removePlayer , RoomEventType .EXIT );
170195
171- return new RoomExitData (destination , playerListResponse , systemNoticeResponse , false );
196+ PlayerListResponse playerListResponse = toPlayerListResponse (room );
197+
198+ return new RoomExitData (destination , playerListResponse , systemNoticeResponse , false );
199+ }
172200 }
173201
174202 public RoomListResponse getAllRooms () {
0 commit comments