diff --git a/src/main/kotlin/yjh/cstar/engine/application/GameEngineService.kt b/src/main/kotlin/yjh/cstar/engine/application/GameEngineService.kt index e573434..ccedd4d 100644 --- a/src/main/kotlin/yjh/cstar/engine/application/GameEngineService.kt +++ b/src/main/kotlin/yjh/cstar/engine/application/GameEngineService.kt @@ -13,14 +13,11 @@ class GameEngineService( ) { @Async("GameEngineThreadPool") - fun start( - players: Map, - quizzes: List, - roomId: Long, - categoryId: Long, - ) { + fun start(players: Map, quizzes: List, roomId: Long, categoryId: Long) { logger.info { "[INFO] 게임 엔진 스레드 시작 - roomId : $roomId" } + quizGameService.play(players, quizzes, roomId, categoryId) + logger.info { "[INFO] 게임 엔진 스레드 종료 - roomId : $roomId" } } } diff --git a/src/main/kotlin/yjh/cstar/engine/application/QuizGameService.kt b/src/main/kotlin/yjh/cstar/engine/application/QuizGameService.kt index 563c7b4..5d6beb3 100644 --- a/src/main/kotlin/yjh/cstar/engine/application/QuizGameService.kt +++ b/src/main/kotlin/yjh/cstar/engine/application/QuizGameService.kt @@ -1,32 +1,29 @@ package yjh.cstar.engine.application import org.springframework.stereotype.Service +import yjh.cstar.engine.application.port.AnswerProvider +import yjh.cstar.engine.application.port.GameNotifier +import yjh.cstar.engine.application.port.RankingHandler import yjh.cstar.engine.domain.game.GameInfo import yjh.cstar.engine.domain.game.QuizGame -import yjh.cstar.engine.domain.io.InputHandler -import yjh.cstar.engine.domain.io.OutputHandler -import yjh.cstar.engine.domain.io.RankingHandler import yjh.cstar.engine.domain.quiz.QuizDto import yjh.cstar.game.application.GameResultService @Service class QuizGameService( - private val inputHandler: InputHandler, - private val outputHandler: OutputHandler, + private val answerProvider: AnswerProvider, + private val gameNotifier: GameNotifier, private val rankingHandler: RankingHandler, private val gameResultService: GameResultService, ) { - fun play( - players: Map, - quizzes: List, - roomId: Long, - categoryId: Long, - ) { + fun play(players: Map, quizzes: List, roomId: Long, categoryId: Long) { val gameInfo = GameInfo.of(players, quizzes, roomId, categoryId) - val quizGame = QuizGame(gameInfo, inputHandler, outputHandler, rankingHandler, gameResultService) + + val quizGame = QuizGame(gameInfo, answerProvider, gameNotifier, rankingHandler, gameResultService) quizGame.initialize() + quizGame.run() } } diff --git a/src/main/kotlin/yjh/cstar/engine/application/port/AnswerProvider.kt b/src/main/kotlin/yjh/cstar/engine/application/port/AnswerProvider.kt new file mode 100644 index 0000000..6b0389b --- /dev/null +++ b/src/main/kotlin/yjh/cstar/engine/application/port/AnswerProvider.kt @@ -0,0 +1,10 @@ +package yjh.cstar.engine.application.port + +import yjh.cstar.engine.domain.quiz.PlayerAnswer + +interface AnswerProvider { + + fun receivePlayerAnswer(roomId: Long, quizId: Long): PlayerAnswer? + + fun initializePlayerAnswerToReceive(roomId: Long, quizId: Long) +} diff --git a/src/main/kotlin/yjh/cstar/engine/application/port/GameAnswerPollRepository.kt b/src/main/kotlin/yjh/cstar/engine/application/port/GameAnswerPollRepository.kt deleted file mode 100644 index cca534f..0000000 --- a/src/main/kotlin/yjh/cstar/engine/application/port/GameAnswerPollRepository.kt +++ /dev/null @@ -1,7 +0,0 @@ -package yjh.cstar.engine.application.port - -import yjh.cstar.game.domain.AnswerResult - -interface GameAnswerPollRepository { - fun poll(roomId: Long, quizId: Long): AnswerResult? -} diff --git a/src/main/kotlin/yjh/cstar/engine/application/port/GameNotifier.kt b/src/main/kotlin/yjh/cstar/engine/application/port/GameNotifier.kt new file mode 100644 index 0000000..dbf42f2 --- /dev/null +++ b/src/main/kotlin/yjh/cstar/engine/application/port/GameNotifier.kt @@ -0,0 +1,22 @@ +package yjh.cstar.engine.application.port + +import yjh.cstar.engine.domain.player.Players +import yjh.cstar.engine.domain.quiz.Quiz +import yjh.cstar.engine.domain.ranking.Ranking + +interface GameNotifier { + + fun notifyGameStartComments(destination: String, roomId: Long) + + fun notifyQuizQuestion(destination: String, quizNo: Int, quiz: Quiz) + + fun notifyRoundResult(destination: String, playerId: Long, nickname: String) + + fun notifyRanking(destination: String, players: Players, ranking: Ranking) + + fun notifyGameResult(destination: String, playerId: Long, nickname: String) + + fun notifyTimeOut(destination: String) + + fun notifyCountdown(destination: String) +} diff --git a/src/main/kotlin/yjh/cstar/engine/domain/io/RankingHandler.kt b/src/main/kotlin/yjh/cstar/engine/application/port/RankingHandler.kt similarity index 56% rename from src/main/kotlin/yjh/cstar/engine/domain/io/RankingHandler.kt rename to src/main/kotlin/yjh/cstar/engine/application/port/RankingHandler.kt index c74aa2b..49dae06 100644 --- a/src/main/kotlin/yjh/cstar/engine/domain/io/RankingHandler.kt +++ b/src/main/kotlin/yjh/cstar/engine/application/port/RankingHandler.kt @@ -1,13 +1,13 @@ -package yjh.cstar.engine.domain.io +package yjh.cstar.engine.application.port import yjh.cstar.engine.domain.player.Players import yjh.cstar.engine.domain.ranking.Ranking interface RankingHandler { - fun init(roomId: Long, players: Players) + fun initRankingBoard(roomId: Long, players: Players) - fun increaseScore(roomId: Long, playerId: Long) + fun assignScoreToPlayer(roomId: Long, playerId: Long) fun getWinner(roomId: Long): Long diff --git a/src/main/kotlin/yjh/cstar/engine/domain/game/GameInfo.kt b/src/main/kotlin/yjh/cstar/engine/domain/game/GameInfo.kt index 9777643..4387b45 100644 --- a/src/main/kotlin/yjh/cstar/engine/domain/game/GameInfo.kt +++ b/src/main/kotlin/yjh/cstar/engine/domain/game/GameInfo.kt @@ -3,10 +3,11 @@ package yjh.cstar.engine.domain.game import yjh.cstar.engine.domain.player.Players import yjh.cstar.engine.domain.quiz.Quiz import yjh.cstar.engine.domain.quiz.QuizDto +import yjh.cstar.engine.domain.quiz.Quizzes data class GameInfo( val players: Players, - val quizzes: List, + val quizzes: Quizzes, val roomId: Long, val categoryId: Long, ) { @@ -14,7 +15,7 @@ data class GameInfo( companion object { fun of(players: Map, quizzes: List, roomId: Long, categoryId: Long): GameInfo { val quizList = quizzes.map { quizDto -> Quiz.of(quizDto.id, quizDto.question, quizDto.answer) } - return GameInfo(Players.of(players), quizList, roomId, categoryId) + return GameInfo(Players.of(players), Quizzes.of(quizzes), roomId, categoryId) } } } diff --git a/src/main/kotlin/yjh/cstar/engine/domain/game/QuizGame.kt b/src/main/kotlin/yjh/cstar/engine/domain/game/QuizGame.kt index 8ce5b21..39325c8 100644 --- a/src/main/kotlin/yjh/cstar/engine/domain/game/QuizGame.kt +++ b/src/main/kotlin/yjh/cstar/engine/domain/game/QuizGame.kt @@ -2,12 +2,11 @@ package yjh.cstar.engine.domain.game import io.github.oshai.kotlinlogging.KotlinLogging import yjh.cstar.common.BaseException -import yjh.cstar.engine.domain.io.InputHandler -import yjh.cstar.engine.domain.io.OutputHandler -import yjh.cstar.engine.domain.io.RankingHandler +import yjh.cstar.engine.application.port.AnswerProvider +import yjh.cstar.engine.application.port.GameNotifier +import yjh.cstar.engine.application.port.RankingHandler import yjh.cstar.engine.domain.player.Players -import yjh.cstar.engine.domain.quiz.PlayerAnswer -import yjh.cstar.engine.domain.quiz.Quiz +import yjh.cstar.engine.domain.quiz.Quizzes import yjh.cstar.engine.domain.ranking.Ranking import yjh.cstar.game.application.GameResultService import yjh.cstar.game.presentation.request.RankingCreateRequest @@ -17,8 +16,8 @@ private val logger = KotlinLogging.logger {} class QuizGame( private val gameInfo: GameInfo, - private val inputHandler: InputHandler, - private val outputHandler: OutputHandler, + private val answerProvider: AnswerProvider, + private val gameNotifier: GameNotifier, private val rankingHandler: RankingHandler, private val gameResultService: GameResultService, ) : GameInitializable, GameRunnable { @@ -34,45 +33,46 @@ class QuizGame( } override fun initialize() { - rankingHandler.init(roomId, players) + rankingHandler.initRankingBoard(roomId, players) } override fun run() { val gameStartedAt = getCurrentAt() - outputHandler.sendGameStartComments(destination, gameInfo.roomId) + gameNotifier.notifyGameStartComments(destination, gameInfo.roomId) try { - for (idx in quizzes.indices) { - val quiz = quizzes[idx] - val quizNo = idx + 1 + for ((index, quiz) in quizzes.getQuizList().withIndex()) { + val quizNo = index + 1 val quizId = quiz.id - outputHandler.resetPlayerAnswer(roomId, quizId) + answerProvider.initializePlayerAnswerToReceive(roomId, quizId) - outputHandler.sendQuizQuestion(destination, quizNo, quiz) + gameNotifier.notifyQuizQuestion(destination, quizNo, quiz) val roundStartTime = getCurrentTime() while (true) { logger.info { "[INFO] 정답 대기중..." } - outputHandler.sendCountdown(destination) + + gameNotifier.notifyCountdown(destination) if (isTimeOut(roundStartTime)) { - outputHandler.sendTimeOut(destination) + gameNotifier.notifyTimeOut(destination) break } - val playerAnswer: PlayerAnswer? = inputHandler.getPlayerAnswer(roomId, quizId) - if (playerAnswer == null) { - continue - } + /** + * 1초 동안 플레이어 응답을 대기합니다.(Blocking) + */ + val playerAnswer = answerProvider.receivePlayerAnswer(roomId, quizId) + ?: continue + + if (playerAnswer.isCorrect(quiz)) { + val roundWinnerId = playerAnswer.playerId - if (quiz.isCorrectAnswer(playerAnswer)) { - rankingHandler.increaseScore(roomId, playerAnswer.playerId) - val ranking = rankingHandler.getRanking(roomId) - outputHandler.sendRanking(destination, players, ranking) + rankingHandler.assignScoreToPlayer(roomId, roundWinnerId) + notifyRanking() - val playerId = playerAnswer.playerId - outputHandler.sendRoundResult(destination, playerId, players.getNickname(playerId)) + notifyRoundResult(roundWinnerId) break } } @@ -80,8 +80,7 @@ class QuizGame( findAndSendWinner(players) - val ranking = rankingHandler.getRanking(roomId) - saveGameResult(ranking, quizzes, gameStartedAt) + recordGameResult(gameStartedAt) } catch (e: BaseException) { logger.error { e } } catch (e: Exception) { @@ -89,32 +88,46 @@ class QuizGame( } } - private fun isTimeOut(startTime: Long) = getDuration(startTime) >= TIME_LIMIT_MILLIS - - private fun getCurrentTime() = System.currentTimeMillis() - - private fun getCurrentAt() = LocalDateTime.now() - - private fun getDuration(pastTime: Long) = getCurrentTime() - pastTime - - private fun findWinner() = rankingHandler.getWinner(roomId) - - private fun findAndSendWinner(players: Players) { - val winnerId = findWinner() - val winnerNickname = players.getNickname(winnerId) - outputHandler.sendGameResult(destination, winnerId, winnerNickname) + private fun recordGameResult(gameStartedAt: LocalDateTime) { + val ranking = rankingHandler.getRanking(roomId) + saveGameResult(ranking, quizzes, gameStartedAt) } - private fun saveGameResult(ranking: Ranking, quizzes: List, gameStartedAt: LocalDateTime) { + private fun saveGameResult(ranking: Ranking, quizzes: Quizzes, gameStartedAt: LocalDateTime) { val winnerId = findWinner() val rankingCreateRequest = RankingCreateRequest( ranking, roomId, winnerId, - quizzes.size, + quizzes.getSize(), categoryId, gameStartedAt ) gameResultService.create(rankingCreateRequest) } + + private fun isTimeOut(startTime: Long) = getDuration(startTime) >= TIME_LIMIT_MILLIS + + private fun findAndSendWinner(players: Players) { + val winnerId = findWinner() + val winnerNickname = players.getNickname(winnerId) + gameNotifier.notifyGameResult(destination, winnerId, winnerNickname) + } + + private fun notifyRoundResult(roundWinnerId: Long) { + gameNotifier.notifyRoundResult(destination, roundWinnerId, players.getNickname(roundWinnerId)) + } + + private fun notifyRanking() { + val ranking = rankingHandler.getRanking(roomId) + gameNotifier.notifyRanking(destination, players, ranking) + } + + private fun findWinner() = rankingHandler.getWinner(roomId) + + private fun getCurrentTime() = System.currentTimeMillis() + + private fun getCurrentAt() = LocalDateTime.now() + + private fun getDuration(pastTime: Long) = getCurrentTime() - pastTime } diff --git a/src/main/kotlin/yjh/cstar/engine/domain/io/ExternalInputHandler.kt b/src/main/kotlin/yjh/cstar/engine/domain/io/ExternalInputHandler.kt deleted file mode 100644 index d759dde..0000000 --- a/src/main/kotlin/yjh/cstar/engine/domain/io/ExternalInputHandler.kt +++ /dev/null @@ -1,16 +0,0 @@ -package yjh.cstar.engine.domain.io - -import org.springframework.stereotype.Service -import yjh.cstar.engine.application.port.GameAnswerPollRepository -import yjh.cstar.engine.domain.quiz.PlayerAnswer -import yjh.cstar.game.domain.toPlayerAnswer - -@Service -class ExternalInputHandler( - val gameAnswerPollRepository: GameAnswerPollRepository, -) : InputHandler { - - override fun getPlayerAnswer(roomId: Long, quizId: Long): PlayerAnswer? { - return gameAnswerPollRepository.poll(roomId, quizId)?.toPlayerAnswer() - } -} diff --git a/src/main/kotlin/yjh/cstar/engine/domain/io/InputHandler.kt b/src/main/kotlin/yjh/cstar/engine/domain/io/InputHandler.kt deleted file mode 100644 index 0f16466..0000000 --- a/src/main/kotlin/yjh/cstar/engine/domain/io/InputHandler.kt +++ /dev/null @@ -1,8 +0,0 @@ -package yjh.cstar.engine.domain.io - -import yjh.cstar.engine.domain.quiz.PlayerAnswer - -interface InputHandler { - - fun getPlayerAnswer(roomId: Long, quizId: Long): PlayerAnswer? -} diff --git a/src/main/kotlin/yjh/cstar/engine/domain/io/OutputHandler.kt b/src/main/kotlin/yjh/cstar/engine/domain/io/OutputHandler.kt deleted file mode 100644 index caa44b7..0000000 --- a/src/main/kotlin/yjh/cstar/engine/domain/io/OutputHandler.kt +++ /dev/null @@ -1,24 +0,0 @@ -package yjh.cstar.engine.domain.io - -import yjh.cstar.engine.domain.player.Players -import yjh.cstar.engine.domain.quiz.Quiz -import yjh.cstar.engine.domain.ranking.Ranking - -interface OutputHandler { - - fun sendGameStartComments(destination: String, roomId: Long) - - fun sendQuizQuestion(destination: String, quizNo: Int, quiz: Quiz) - - fun sendRoundResult(destination: String, playerId: Long, nickname: String) - - fun sendRanking(destination: String, players: Players, ranking: Ranking) - - fun sendGameResult(destination: String, playerId: Long, nickname: String) - - fun sendTimeOut(destination: String) - - fun sendCountdown(destination: String) - - fun resetPlayerAnswer(roomId: Long, quizId: Long) -} diff --git a/src/main/kotlin/yjh/cstar/engine/domain/quiz/PlayerAnswer.kt b/src/main/kotlin/yjh/cstar/engine/domain/quiz/PlayerAnswer.kt index 2ed9997..13146e3 100644 --- a/src/main/kotlin/yjh/cstar/engine/domain/quiz/PlayerAnswer.kt +++ b/src/main/kotlin/yjh/cstar/engine/domain/quiz/PlayerAnswer.kt @@ -3,4 +3,8 @@ package yjh.cstar.engine.domain.quiz class PlayerAnswer(val roomId: Long, val playerId: Long, val quizId: Long, val playerAnswer: String) { fun isMatch(quizAnswer: String) = playerAnswer.equals(quizAnswer) + + fun isCorrect(quiz: Quiz): Boolean { + return quiz.isSameAnswer(playerAnswer) + } } diff --git a/src/main/kotlin/yjh/cstar/engine/domain/quiz/Quiz.kt b/src/main/kotlin/yjh/cstar/engine/domain/quiz/Quiz.kt index 9c24970..26c1db1 100644 --- a/src/main/kotlin/yjh/cstar/engine/domain/quiz/Quiz.kt +++ b/src/main/kotlin/yjh/cstar/engine/domain/quiz/Quiz.kt @@ -1,12 +1,10 @@ package yjh.cstar.engine.domain.quiz -data class QuizDto(val id: Long, val question: String, val answer: String) - class Quiz(val id: Long, val question: String, private val answer: String) { companion object { fun of(id: Long, question: String, answer: String) = Quiz(id, question, answer) } - fun isCorrectAnswer(playerAnswer: PlayerAnswer) = playerAnswer.isMatch(answer) + fun isSameAnswer(answer: String) = this.answer == answer } diff --git a/src/main/kotlin/yjh/cstar/engine/domain/quiz/QuizDto.kt b/src/main/kotlin/yjh/cstar/engine/domain/quiz/QuizDto.kt new file mode 100644 index 0000000..c346111 --- /dev/null +++ b/src/main/kotlin/yjh/cstar/engine/domain/quiz/QuizDto.kt @@ -0,0 +1,9 @@ +package yjh.cstar.engine.domain.quiz + +data class QuizDto(val id: Long, val question: String, val answer: String) + +fun QuizDto.toModel(): Quiz = Quiz( + id = this.id, + question = this.question, + answer = this.answer +) diff --git a/src/main/kotlin/yjh/cstar/engine/domain/quiz/Quizzes.kt b/src/main/kotlin/yjh/cstar/engine/domain/quiz/Quizzes.kt new file mode 100644 index 0000000..aed3b25 --- /dev/null +++ b/src/main/kotlin/yjh/cstar/engine/domain/quiz/Quizzes.kt @@ -0,0 +1,17 @@ +package yjh.cstar.engine.domain.quiz + +class Quizzes(private val quizzes: List) { + companion object { + fun of(quizzes: List): Quizzes { + return Quizzes(quizzes.map { quizDto -> quizDto.toModel() }) + } + } + + fun getQuizList(): List { + return quizzes + } + + fun getSize(): Int { + return quizzes.size + } +} diff --git a/src/main/kotlin/yjh/cstar/engine/infrastructure/GameAnswerPollRepositoryAdapter.kt b/src/main/kotlin/yjh/cstar/engine/infrastructure/GameAnswerPollRepositoryAdapter.kt deleted file mode 100644 index 08a1b55..0000000 --- a/src/main/kotlin/yjh/cstar/engine/infrastructure/GameAnswerPollRepositoryAdapter.kt +++ /dev/null @@ -1,22 +0,0 @@ -package yjh.cstar.engine.infrastructure - -import com.fasterxml.jackson.databind.ObjectMapper -import org.springframework.stereotype.Repository -import yjh.cstar.engine.application.port.GameAnswerPollRepository -import yjh.cstar.engine.infrastructure.redis.AnswerResultEntity -import yjh.cstar.game.domain.AnswerResult -import yjh.cstar.util.RedisUtil - -@Repository -class GameAnswerPollRepositoryAdapter( - private val redisUtil: RedisUtil, - private val objectMapper: ObjectMapper, -) : GameAnswerPollRepository { - - override fun poll(roomId: Long, quizId: Long): AnswerResult? { - val key = "roomId : " + roomId.toString() + ", " + "quizId : " + quizId.toString() - return redisUtil.lpop(key, 1)?.let { - objectMapper.readValue(it, AnswerResultEntity::class.java)?.toModel() - } - } -} diff --git a/src/main/kotlin/yjh/cstar/engine/infrastructure/RedisQueueAnswerProvider.kt b/src/main/kotlin/yjh/cstar/engine/infrastructure/RedisQueueAnswerProvider.kt new file mode 100644 index 0000000..87cf644 --- /dev/null +++ b/src/main/kotlin/yjh/cstar/engine/infrastructure/RedisQueueAnswerProvider.kt @@ -0,0 +1,31 @@ +package yjh.cstar.engine.infrastructure + +import com.fasterxml.jackson.databind.ObjectMapper +import org.springframework.stereotype.Service +import yjh.cstar.engine.application.port.AnswerProvider +import yjh.cstar.engine.domain.quiz.PlayerAnswer +import yjh.cstar.engine.infrastructure.redis.AnswerResultEntity +import yjh.cstar.util.RedisUtil + +@Service +class RedisQueueAnswerProvider( + private val redisUtil: RedisUtil, + private val objectMapper: ObjectMapper, +) : AnswerProvider { + + override fun receivePlayerAnswer(roomId: Long, quizId: Long): PlayerAnswer? { + val key = generateKey(roomId, quizId) + return redisUtil.lpop(key, 1)?.let { + objectMapper.readValue(it, AnswerResultEntity::class.java).toModel2() + } + } + + override fun initializePlayerAnswerToReceive(roomId: Long, quizId: Long) { + val key = generateKey(roomId, quizId) + redisUtil.delete(key) + } + + private fun generateKey(roomId: Long, quizId: Long): String { + return "roomId : $roomId, quizId : $quizId" + } +} diff --git a/src/main/kotlin/yjh/cstar/engine/domain/io/ExternalRankingHandler.kt b/src/main/kotlin/yjh/cstar/engine/infrastructure/RedisRankingHandler.kt similarity index 84% rename from src/main/kotlin/yjh/cstar/engine/domain/io/ExternalRankingHandler.kt rename to src/main/kotlin/yjh/cstar/engine/infrastructure/RedisRankingHandler.kt index 952a6b0..ac47925 100644 --- a/src/main/kotlin/yjh/cstar/engine/domain/io/ExternalRankingHandler.kt +++ b/src/main/kotlin/yjh/cstar/engine/infrastructure/RedisRankingHandler.kt @@ -1,12 +1,13 @@ -package yjh.cstar.engine.domain.io +package yjh.cstar.engine.infrastructure import org.springframework.stereotype.Service +import yjh.cstar.engine.application.port.RankingHandler import yjh.cstar.engine.domain.player.Players import yjh.cstar.engine.domain.ranking.Ranking import yjh.cstar.util.RedisUtil @Service -class ExternalRankingHandler( +class RedisRankingHandler( private val redisUtil: RedisUtil, ) : RankingHandler { @@ -17,7 +18,7 @@ class ExternalRankingHandler( val INCREASE_SCORE = 1 } - override fun init(roomId: Long, players: Players) { + override fun initRankingBoard(roomId: Long, players: Players) { val key = KEY_PREFIX + roomId redisUtil.delete(key) val ids: List = players.getPlayerIds() @@ -26,7 +27,7 @@ class ExternalRankingHandler( } } - override fun increaseScore(roomId: Long, playerId: Long) { + override fun assignScoreToPlayer(roomId: Long, playerId: Long) { val key = KEY_PREFIX + roomId val value = VALUE_PREFIX + playerId redisUtil.zincrby(key, value, INCREASE_SCORE.toDouble()) diff --git a/src/main/kotlin/yjh/cstar/engine/domain/io/ExternalOutputHandler.kt b/src/main/kotlin/yjh/cstar/engine/infrastructure/WebsocketGameNotifier.kt similarity index 70% rename from src/main/kotlin/yjh/cstar/engine/domain/io/ExternalOutputHandler.kt rename to src/main/kotlin/yjh/cstar/engine/infrastructure/WebsocketGameNotifier.kt index 5136753..3cd6e13 100644 --- a/src/main/kotlin/yjh/cstar/engine/domain/io/ExternalOutputHandler.kt +++ b/src/main/kotlin/yjh/cstar/engine/infrastructure/WebsocketGameNotifier.kt @@ -1,21 +1,20 @@ -package yjh.cstar.engine.domain.io +package yjh.cstar.engine.infrastructure import org.springframework.stereotype.Service +import yjh.cstar.engine.application.port.GameNotifier import yjh.cstar.engine.domain.game.QuizGame import yjh.cstar.engine.domain.player.Players import yjh.cstar.engine.domain.quiz.Quiz import yjh.cstar.engine.domain.ranking.Ranking import yjh.cstar.game.presentation.response.QuizInfoResponse -import yjh.cstar.util.RedisUtil import yjh.cstar.websocket.application.BroadCastService @Service -class ExternalOutputHandler( +class WebsocketGameNotifier( private val broadCastService: BroadCastService, - private val redisUtil: RedisUtil, -) : OutputHandler { +) : GameNotifier { - override fun sendGameStartComments(destination: String, roomId: Long) { + override fun notifyGameStartComments(destination: String, roomId: Long) { broadCastService.sendMessage(destination, "start", "게임 시작 합니다. $roomId", null) broadCastService.sendMessage(destination, "guide", "GAME START!", null) broadCastService.sendMessage( @@ -26,7 +25,7 @@ class ExternalOutputHandler( ) } - override fun sendQuizQuestion(destination: String, quizNo: Int, quiz: Quiz) { + override fun notifyQuizQuestion(destination: String, quizNo: Int, quiz: Quiz) { broadCastService.sendMessage( destination, "quiz", @@ -35,11 +34,11 @@ class ExternalOutputHandler( ) } - override fun sendRoundResult(destination: String, playerId: Long, nickname: String) { + override fun notifyRoundResult(destination: String, playerId: Long, nickname: String) { broadCastService.sendMessage(destination, "winner", "[$nickname]님이 맞췄습니다!", playerId) } - override fun sendRanking(destination: String, players: Players, ranking: Ranking) { + override fun notifyRanking(destination: String, players: Players, ranking: Ranking) { val result = StringBuilder() val sortedRanking = ranking.getRanking() // "player:1 - 10" @@ -55,19 +54,15 @@ class ExternalOutputHandler( broadCastService.sendMessage(destination, "rank", "현재 랭킹 정보 입니다.", result) } - override fun sendTimeOut(destination: String) { + override fun notifyTimeOut(destination: String) { broadCastService.sendMessage(destination, "guide", "시간 초과! 다음 문제로 넘어갑니다!", null) } - override fun sendGameResult(destination: String, playerId: Long, nickname: String) { + override fun notifyGameResult(destination: String, playerId: Long, nickname: String) { broadCastService.sendMessage(destination, "champion", "최종 1등은 ?! [$nickname]입니다!", playerId) } - override fun sendCountdown(destination: String) { + override fun notifyCountdown(destination: String) { broadCastService.sendMessage(destination, "countdown", "1초 경과", null) } - - override fun resetPlayerAnswer(roomId: Long, quizId: Long) { - redisUtil.delete("roomId : " + roomId + ", " + "quizId : " + quizId) - } } diff --git a/src/main/kotlin/yjh/cstar/engine/infrastructure/redis/AnswerResultEntity.kt b/src/main/kotlin/yjh/cstar/engine/infrastructure/redis/AnswerResultEntity.kt index ea4b85c..c26c772 100644 --- a/src/main/kotlin/yjh/cstar/engine/infrastructure/redis/AnswerResultEntity.kt +++ b/src/main/kotlin/yjh/cstar/engine/infrastructure/redis/AnswerResultEntity.kt @@ -1,5 +1,6 @@ package yjh.cstar.engine.infrastructure.redis +import yjh.cstar.engine.domain.quiz.PlayerAnswer import yjh.cstar.game.domain.AnswerResult class AnswerResultEntity( @@ -22,13 +23,12 @@ class AnswerResultEntity( } } - fun toModel(): AnswerResult { - return AnswerResult( - answer = this.answer, - quizId = this.quizId, + fun toModel2(): PlayerAnswer { + return PlayerAnswer( roomId = this.roomId, playerId = this.playerId, - nickname = this.nickname + quizId = this.quizId, + playerAnswer = answer ) } } diff --git a/src/test/kotlin/yjh/cstar/redis/RedisConcurrencyTest.kt b/src/test/kotlin/yjh/cstar/redis/RedisConcurrencyTest.kt index b4a27c6..207c609 100644 --- a/src/test/kotlin/yjh/cstar/redis/RedisConcurrencyTest.kt +++ b/src/test/kotlin/yjh/cstar/redis/RedisConcurrencyTest.kt @@ -11,7 +11,8 @@ import org.springframework.test.context.DynamicPropertyRegistry import org.springframework.test.context.DynamicPropertySource import org.testcontainers.containers.GenericContainer import org.testcontainers.utility.DockerImageName -import yjh.cstar.engine.application.port.GameAnswerPollRepository +import yjh.cstar.engine.domain.quiz.PlayerAnswer +import yjh.cstar.engine.infrastructure.RedisQueueAnswerProvider import yjh.cstar.game.domain.AnswerResult import yjh.cstar.util.RedisUtil import yjh.cstar.websocket.application.GameAnswerPushService @@ -31,7 +32,7 @@ class RedisConcurrencyTest { private lateinit var gameAnswerPushService: GameAnswerPushService @Autowired - private lateinit var gameAnswerPollRepository: GameAnswerPollRepository + private lateinit var redisQueueAnswerProvider: RedisQueueAnswerProvider @Autowired private lateinit var redisUtil: RedisUtil @@ -82,7 +83,7 @@ class RedisConcurrencyTest { val startLatch = CountDownLatch(1) val doneLatch = CountDownLatch(numberOfThreads) val executor = Executors.newFixedThreadPool(numberOfThreads) - val receive = Collections.synchronizedList(mutableListOf()) + val receive = Collections.synchronizedList(mutableListOf()) val answerResult = AnswerResult( answer = "정답", @@ -108,7 +109,7 @@ class RedisConcurrencyTest { executor.submit { try { for (idx in 1..100) { - receive.add(gameAnswerPollRepository.poll(ROOM_ID, QUIZ_ID)) + receive.add(redisQueueAnswerProvider.receivePlayerAnswer(ROOM_ID, QUIZ_ID)) } } catch (e: Exception) { println("Error: ${e.message}")