Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,22 @@ repositories {
}

dependencies {
/* SPRING BOOT STARTER */
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-actuator'

/* DATABASE */
runtimeOnly 'com.mysql:mysql-connector-j'

/* MONITORING */
implementation 'io.micrometer:micrometer-registry-prometheus'

/* TEST */
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@
import io.f1.backend.domain.admin.dto.AdminPrincipal;
import io.f1.backend.domain.user.dto.UserPrincipal;

public record CurrentUserAndAdminResponse(Long id, String name, String role, String providerId) {
public record CurrentUserAndAdminResponse(Long id, String name, String role) {

public static CurrentUserAndAdminResponse from(UserPrincipal userPrincipal) {
return new CurrentUserAndAdminResponse(
userPrincipal.getUserId(),
userPrincipal.getUserNickname(),
UserPrincipal.ROLE_USER,
userPrincipal.getName());
UserPrincipal.ROLE_USER);
}

public static CurrentUserAndAdminResponse from(AdminPrincipal adminPrincipal) {
return new CurrentUserAndAdminResponse(
adminPrincipal.getAuthenticationAdmin().adminId(),
adminPrincipal.getUsername(),
AdminPrincipal.ROLE_ADMIN,
null);
AdminPrincipal.ROLE_ADMIN);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.f1.backend.domain.game.app;

import static io.f1.backend.domain.game.websocket.WebSocketUtils.getDestination;

import io.f1.backend.domain.game.dto.ChatMessage;
import io.f1.backend.domain.game.dto.MessageType;
import io.f1.backend.domain.game.event.GameCorrectAnswerEvent;
import io.f1.backend.domain.game.model.Room;
import io.f1.backend.domain.game.websocket.MessageSender;
import io.f1.backend.domain.question.entity.Question;

import lombok.RequiredArgsConstructor;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class ChatService {

private final RoomService roomService;
private final TimerService timerService;
private final MessageSender messageSender;
private final ApplicationEventPublisher eventPublisher;

// todo 동시성적용
public void chat(Long roomId, String sessionId, ChatMessage chatMessage) {

Room room = roomService.findRoom(roomId);

String destination = getDestination(roomId);

messageSender.sendBroadcast(destination, MessageType.CHAT, chatMessage);

if (!room.isPlaying()) {
return;
}

Question currentQuestion = room.getCurrentQuestion();

String answer = currentQuestion.getAnswer();

if (answer.equals(chatMessage.message())) {
eventPublisher.publishEvent(
new GameCorrectAnswerEvent(room, sessionId, chatMessage, answer));
}
}
}
134 changes: 123 additions & 11 deletions backend/src/main/java/io/f1/backend/domain/game/app/GameService.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
package io.f1.backend.domain.game.app;

import static io.f1.backend.domain.game.mapper.RoomMapper.ofPlayerEvent;
import static io.f1.backend.domain.game.mapper.RoomMapper.toGameResultListResponse;
import static io.f1.backend.domain.game.mapper.RoomMapper.toGameSettingResponse;
import static io.f1.backend.domain.game.mapper.RoomMapper.toPlayerListResponse;
import static io.f1.backend.domain.game.mapper.RoomMapper.toQuestionResultResponse;
import static io.f1.backend.domain.game.mapper.RoomMapper.toQuestionStartResponse;
import static io.f1.backend.domain.game.mapper.RoomMapper.toRankUpdateResponse;
import static io.f1.backend.domain.game.mapper.RoomMapper.toRoomSettingResponse;
import static io.f1.backend.domain.game.websocket.WebSocketUtils.getDestination;
import static io.f1.backend.domain.quiz.mapper.QuizMapper.toGameStartResponse;

import io.f1.backend.domain.game.dto.ChatMessage;
import io.f1.backend.domain.game.dto.MessageType;
import io.f1.backend.domain.game.dto.RoomEventType;
import io.f1.backend.domain.game.dto.request.GameSettingChanger;
import io.f1.backend.domain.game.dto.response.PlayerListResponse;
import io.f1.backend.domain.game.event.GameCorrectAnswerEvent;
import io.f1.backend.domain.game.event.GameTimeoutEvent;
import io.f1.backend.domain.game.event.RoomUpdatedEvent;
import io.f1.backend.domain.game.model.Player;
import io.f1.backend.domain.game.model.Room;
Expand All @@ -26,21 +36,26 @@
import lombok.extern.slf4j.Slf4j;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Service
@RequiredArgsConstructor
public class GameService {

public static final int START_DELAY = 5;
private static final int START_DELAY = 5;
private static final int CONTINUE_DELAY = 3;
private static final String NONE_CORRECT_USER = "";

private final MessageSender messageSender;
private final TimerService timerService;
private final QuizService quizService;
private final RoomService roomService;
private final TimerService timerService;
private final MessageSender messageSender;
private final RoomRepository roomRepository;
private final ApplicationEventPublisher eventPublisher;

Expand All @@ -67,13 +82,114 @@ public void gameStart(Long roomId, UserPrincipal principal) {

timerService.startTimer(room, START_DELAY);

messageSender.send(destination, MessageType.GAME_START, toGameStartResponse(questions));
messageSender.send(
messageSender.sendBroadcast(
destination, MessageType.GAME_START, toGameStartResponse(questions));
messageSender.sendBroadcast(
destination, MessageType.RANK_UPDATE, toRankUpdateResponse(room));
messageSender.sendBroadcast(
destination,
MessageType.QUESTION_START,
toQuestionStartResponse(room, START_DELAY));
}

@EventListener
public void onCorrectAnswer(GameCorrectAnswerEvent event) {

Room room = event.room();
String sessionId = event.sessionId();
ChatMessage chatMessage = event.chatMessage();
String answer = event.answer();

String destination = getDestination(room.getId());

room.increasePlayerCorrectCount(sessionId);

messageSender.sendBroadcast(
destination,
MessageType.QUESTION_RESULT,
toQuestionResultResponse(chatMessage.nickname(), answer));
messageSender.sendBroadcast(
destination, MessageType.RANK_UPDATE, toRankUpdateResponse(room));
messageSender.sendBroadcast(
destination,
MessageType.SYSTEM_NOTICE,
ofPlayerEvent(chatMessage.nickname(), RoomEventType.CORRECT_ANSWER));

timerService.cancelTimer(room);

if (!timerService.validateCurrentRound(room)) {
gameEnd(room);
return;
}

room.increaseCurrentRound();

// 타이머 추가하기
timerService.startTimer(room, CONTINUE_DELAY);
messageSender.sendBroadcast(
destination,
MessageType.QUESTION_START,
toQuestionStartResponse(room, CONTINUE_DELAY));
}

@EventListener
public void onTimeout(GameTimeoutEvent event) {
Room room = event.room();
String destination = getDestination(room.getId());

messageSender.sendBroadcast(
destination,
MessageType.QUESTION_RESULT,
toQuestionResultResponse(NONE_CORRECT_USER, room.getCurrentQuestion().getAnswer()));
messageSender.sendBroadcast(
destination,
MessageType.SYSTEM_NOTICE,
ofPlayerEvent(NONE_CORRECT_USER, RoomEventType.TIMEOUT));

if (!timerService.validateCurrentRound(room)) {
gameEnd(room);
return;
}

room.increaseCurrentRound();

timerService.startTimer(room, CONTINUE_DELAY);
messageSender.sendBroadcast(
destination,
MessageType.QUESTION_START,
toQuestionStartResponse(room, CONTINUE_DELAY));
}

public void gameEnd(Room room) {
Long roomId = room.getId();
String destination = getDestination(roomId);

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

// TODO : 랭킹 정보 업데이트
messageSender.sendBroadcast(
destination,
MessageType.GAME_RESULT,
toGameResultListResponse(playerSessionMap, room.getGameSetting().getRound()));

room.initializeRound();
room.initializePlayers();

List<Player> disconnectedPlayers = room.getDisconnectedPlayers();
roomService.handleDisconnectedPlayers(room, disconnectedPlayers);

room.updateRoomState(RoomState.WAITING);

messageSender.sendBroadcast(
destination,
MessageType.GAME_SETTING,
toGameSettingResponse(
room.getGameSetting(),
quizService.getQuizWithQuestionsById(room.getGameSetting().getQuizId())));
messageSender.sendBroadcast(
destination, MessageType.ROOM_SETTING, toRoomSettingResponse(room));
}

public void handlePlayerReady(Long roomId, String sessionId) {

Room room = findRoom(roomId);
Expand All @@ -86,7 +202,7 @@ public void handlePlayerReady(Long roomId, String sessionId) {

PlayerListResponse playerListResponse = toPlayerListResponse(room);
log.info(playerListResponse.toString());
messageSender.send(destination, MessageType.PLAYER_LIST, playerListResponse);
messageSender.sendBroadcast(destination, MessageType.PLAYER_LIST, playerListResponse);
}

public void changeGameSetting(
Expand Down Expand Up @@ -136,10 +252,6 @@ private Room findRoom(Long roomId) {
.orElseThrow(() -> new CustomException(RoomErrorCode.ROOM_NOT_FOUND));
}

private String getDestination(Long roomId) {
return "/sub/room/" + roomId;
}

private void validateHostAndState(Room room, UserPrincipal principal) {
if (!room.isHost(principal.getUserId())) {
throw new CustomException(RoomErrorCode.NOT_ROOM_OWNER);
Expand All @@ -161,7 +273,7 @@ private void toggleReadyIfPossible(Room room, Player player) {
private void broadcastGameSetting(Room room) {
String destination = getDestination(room.getId());
Quiz quiz = quizService.getQuizWithQuestionsById(room.getGameSetting().getQuizId());
messageSender.send(
messageSender.sendBroadcast(
destination,
MessageType.GAME_SETTING,
toGameSettingResponse(room.getGameSetting(), quiz));
Expand Down
Loading