-
Notifications
You must be signed in to change notification settings - Fork 0
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
Feat/taggame #43
Feat/taggame #43
Changes from all commits
2e20c91
0e487b2
c1117b3
549df86
7b4b5df
77890ba
e3487f0
a945b47
f5a5528
a8ab6f9
0677ff3
55731e2
abdcbaa
0132db2
4048056
62753a6
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 |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package kutaverse.game.taggame.domain; | ||
|
||
import kutaverse.game.map.domain.Status; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import org.springframework.data.annotation.Id; | ||
import org.springframework.data.relational.core.mapping.Table; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
public class TagGameUser { | ||
|
||
private String userId; | ||
|
||
private Double positionX; | ||
|
||
private Double positionY; | ||
|
||
private Double positionZ; | ||
|
||
private Double rotationPitch; | ||
|
||
private Double rotationYaw; | ||
|
||
private Double rotationRoll; | ||
|
||
private Status status; | ||
|
||
private Double velocityX; | ||
|
||
private Double velocityY; | ||
|
||
private Double velocityZ; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package kutaverse.game.websocket.taggame; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import kutaverse.game.websocket.taggame.handler.CustomHandler; | ||
import kutaverse.game.websocket.taggame.handler.CustomHandlerMapping; | ||
import kutaverse.game.websocket.taggame.util.TagGameRequestUtil; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.reactive.socket.WebSocketHandler; | ||
import org.springframework.web.reactive.socket.WebSocketMessage; | ||
import org.springframework.web.reactive.socket.WebSocketSession; | ||
import reactor.core.publisher.Flux; | ||
import reactor.core.publisher.Mono; | ||
|
||
@Component | ||
public class TagGameWebSocketHandler implements WebSocketHandler { | ||
|
||
private final ObjectMapper objectMapper = new ObjectMapper(); | ||
|
||
/** | ||
* request로 반환후 handler를 실행한다. | ||
* @param session the session to handle | ||
* @return | ||
*/ | ||
@Override | ||
public Mono<Void> handle(WebSocketSession session) { | ||
session.receive() | ||
.map(message -> { | ||
String payloadAsText = message.getPayloadAsText(); | ||
return TagGameRequestUtil.fromWebsocketMessage(payloadAsText); | ||
}) | ||
.doOnNext(tagGameRequest->{ | ||
CustomHandler customHandler = CustomHandlerMapping.getHandler(tagGameRequest.getTagGameStatus()); | ||
customHandler.handler(tagGameRequest,session); | ||
}).subscribe(); | ||
return Mono.never(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package kutaverse.game.websocket.taggame.dto; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.ToString; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
@ToString | ||
public class TagGameMatchRequest { | ||
|
||
private String userId; | ||
|
||
private String text; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package kutaverse.game.websocket.taggame.dto; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import org.springframework.web.reactive.socket.WebSocketSession; | ||
|
||
import java.util.Map; | ||
|
||
@AllArgsConstructor | ||
@Builder | ||
public class TagGameMatchResponse { | ||
|
||
private String roomId; | ||
|
||
private String taggerId; | ||
|
||
public static TagGameMatchResponse toDto(String roomId, Map.Entry<String, WebSocketSession> tagger) { | ||
return TagGameMatchResponse.builder() | ||
.roomId(roomId) | ||
.taggerId(tagger.getKey()) | ||
.build(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "{" + | ||
"roomId='" + roomId + '\'' + | ||
", taggerId='" + taggerId + '\'' + | ||
'}'; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package kutaverse.game.websocket.taggame.dto; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.ToString; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
@ToString | ||
public class TagGameRequest<T> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. public class TagGameRequest<T> {
private TagGameStatus tagGameStatus;
private T request;
} 지금은 TagGame으로 status와 클래스가 지정되어 있는데 이것 또한 유연하게 공통 제네릭을 만들자는 의미죠? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 질문을 제대로 이해했는지 모르겠는데 TagGameStatus 를 GameRequestType으로 변경시켜 로직에 대한 enum 값을 추가할 생각이였습니다, 예를 들어
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제대로 이해하신거 같습니다. 현재 코드에서는 제네릭 이름이나 status가 미니게임으로 특정되어 있어서 물어본거였습니다! |
||
|
||
private TagGameStatus tagGameStatus; | ||
|
||
private T request; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package kutaverse.game.websocket.taggame.dto; | ||
|
||
public enum TagGameStatus { | ||
|
||
//매칭 중 | ||
WAITING_FOR_PLAYERS, | ||
//게임 중 | ||
IN_PROGRESS, | ||
//게임 종료 | ||
FINISHED | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package kutaverse.game.websocket.taggame.handler; | ||
|
||
import org.springframework.web.reactive.socket.WebSocketSession; | ||
|
||
public interface CustomHandler { | ||
|
||
void handler(Object o, WebSocketSession session); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package kutaverse.game.websocket.taggame.handler; | ||
|
||
import jakarta.annotation.PostConstruct; | ||
import kutaverse.game.websocket.map.handler.WebSocketHandler; | ||
import kutaverse.game.websocket.taggame.dto.TagGameStatus; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class CustomHandlerMapping { | ||
|
||
private static final Map<TagGameStatus, CustomHandler> map=new HashMap<>(); | ||
|
||
|
||
static { | ||
map.put(TagGameStatus.WAITING_FOR_PLAYERS,new TagGameMatchingHandler()); | ||
} | ||
|
||
public static CustomHandler getHandler(TagGameStatus tagGameStatus){ | ||
return map.get(tagGameStatus); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package kutaverse.game.websocket.taggame.handler; | ||
|
||
import kutaverse.game.websocket.taggame.dto.TagGameMatchRequest; | ||
import kutaverse.game.websocket.taggame.dto.TagGameRequest; | ||
import kutaverse.game.websocket.taggame.util.TagGameMatchingQueue; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.web.reactive.socket.WebSocketSession; | ||
|
||
@RequiredArgsConstructor | ||
public class TagGameMatchingHandler implements CustomHandler{ | ||
|
||
@Override | ||
public void handler(Object object, WebSocketSession webSocketSession) { | ||
TagGameRequest tagGameRequest =(TagGameRequest) object; | ||
TagGameMatchRequest tagGameMatchRequest = (TagGameMatchRequest) tagGameRequest.getRequest(); | ||
TagGameMatchingQueue.addPlayer(tagGameMatchRequest.getUserId(),webSocketSession); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package kutaverse.game.websocket.taggame.util; | ||
|
||
import kutaverse.game.websocket.minigame.GameRoom; | ||
import kutaverse.game.websocket.minigame.GameRoomManager; | ||
import kutaverse.game.websocket.minigame.MatchingQueue; | ||
import kutaverse.game.websocket.taggame.dto.TagGameMatchResponse; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.scheduling.annotation.Scheduled; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.reactive.socket.WebSocketMessage; | ||
import org.springframework.web.reactive.socket.WebSocketSession; | ||
import reactor.core.publisher.Mono; | ||
|
||
import java.util.*; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.ThreadLocalRandom; | ||
|
||
@Component | ||
@Slf4j | ||
public class TagGameMatchingQueue { | ||
private static final ArrayDeque<Map.Entry<String, WebSocketSession>> queueing = new ArrayDeque<>(); | ||
private static final Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>(); | ||
private static final Random random = new Random(); | ||
public static void addPlayer(String userId, WebSocketSession webSocketSession){ | ||
if(!sessionMap.containsKey(userId)) { | ||
queueing.offer(new AbstractMap.SimpleEntry<>(userId,webSocketSession)); | ||
sessionMap.put(userId,webSocketSession); | ||
webSocketSession.send(Mono.just(webSocketSession.textMessage("매칭 대기 중 입니다."))).subscribe(); | ||
} | ||
|
||
} | ||
|
||
private static Map.Entry<String, WebSocketSession> getPlayer(){ | ||
return queueing.poll(); | ||
} | ||
|
||
public static int getQueuePlayers(){ | ||
return queueing.size(); | ||
} | ||
|
||
private String createGameRoom(List<Map.Entry<String, WebSocketSession>> players) { | ||
StringBuilder stringBuilder = new StringBuilder(); | ||
for (Map.Entry<String, WebSocketSession> player : players) { | ||
stringBuilder.append(player.getKey()); | ||
stringBuilder.append(" "); | ||
} | ||
String roomId = stringBuilder.toString(); | ||
TagGameRoom tagGameRoom = new TagGameRoom(roomId,players); | ||
TagGameRoomManager.addGameRoom(tagGameRoom); | ||
return roomId; | ||
} | ||
|
||
@Scheduled(fixedRate = 1000) | ||
public void matchPlayers(){ | ||
while(queueing.size() >= 4){ | ||
Map.Entry<String, WebSocketSession> player1 = TagGameMatchingQueue.getPlayer(); | ||
Map.Entry<String, WebSocketSession> player2 = TagGameMatchingQueue.getPlayer(); | ||
Map.Entry<String, WebSocketSession> player3 = TagGameMatchingQueue.getPlayer(); | ||
Map.Entry<String, WebSocketSession> player4 = TagGameMatchingQueue.getPlayer(); | ||
|
||
// 세션 값 확인 | ||
if(player1.getValue().isOpen() && player2.getValue().isOpen() | ||
&& player3.getValue().isOpen() && player4.getValue().isOpen()){ | ||
List<Map.Entry<String, WebSocketSession>> players = List.of(player1, player2, player3, player4); | ||
String gameRoom = createGameRoom(players); | ||
Map.Entry<String, WebSocketSession> tagger = selectTagger(players); | ||
|
||
//플레이어들에게 gamerood 공지 | ||
for (Map.Entry<String, WebSocketSession> player : players) { | ||
WebSocketSession webSocketSession = player.getValue(); | ||
TagGameMatchResponse tagGameMatchResponse = TagGameMatchResponse.toDto(gameRoom, tagger); | ||
|
||
WebSocketMessage webSocketMessage = webSocketSession.textMessage(tagGameMatchResponse.toString()); | ||
webSocketSession.send(Mono.just(webSocketMessage)).subscribe(); | ||
} | ||
|
||
|
||
|
||
} | ||
else{ | ||
if(player1.getValue().isOpen()){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 뭔가 더 간단한 처리 방법이 있을거 같은데 이건 한 번 고민해보겠습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 원래 자바에서 queue를 LinkedList 구현체로 사용하는 것으로 알고 있는데, Deque를 구현하셨더라고요. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋습니다. 그냥 코드 자체를 조금 더 쉽게 작성할 수 있을거 같은데 안떠올라서 꺼낸 이야기였습니다.✌ |
||
MatchingQueue.requeue(player1); | ||
} | ||
if(player2.getValue().isOpen()){ | ||
MatchingQueue.requeue(player2); | ||
} | ||
if(player3.getValue().isOpen()){ | ||
MatchingQueue.requeue(player3); | ||
} | ||
if(player4.getValue().isOpen()){ | ||
MatchingQueue.requeue(player4); | ||
} | ||
} | ||
|
||
} | ||
} | ||
|
||
/** | ||
* 술래를 정한다 | ||
* @param players | ||
* @return | ||
*/ | ||
private Map.Entry<String, WebSocketSession> selectTagger(List<Map.Entry<String, WebSocketSession>> players) { | ||
|
||
int randomIndex = random.nextInt(players.size()); // 4는 상한값으로, 0~3 사이의 값이 생성됩니다. | ||
|
||
return players.get(randomIndex); | ||
} | ||
} |
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.
데이터 형식은 map과 동일한가요?
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.
이동을 위한 클래스라 동일하게 가져갈 생각입니다. 술래 매칭에 대한 처리가 아직 들어가지 않았기 때문에 필드가 추가될 수 있습니다
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.
👌