Skip to content

Commit

Permalink
Merge branch 'feature/DRAW-157-3' into sandbox
Browse files Browse the repository at this point in the history
  • Loading branch information
comforest committed Jul 26, 2024
2 parents 1d37d81 + 2bc3db5 commit 2751412
Show file tree
Hide file tree
Showing 40 changed files with 570 additions and 196 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.xorker.draw.timer

import com.xorker.draw.mafia.event.JobWithStartTime
import java.time.Duration
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.springframework.context.ApplicationEventPublisher
Expand All @@ -12,10 +14,21 @@ internal class TimerAdapter(
private val eventPublisher: ApplicationEventPublisher,
) : TimerRepository {

override fun <T> startTimer(interval: Duration, event: T) {
GlobalScope.launch {
override fun <T : Any> startTimer(interval: Duration, event: T): JobWithStartTime {
val job = JobWithStartTime()
CoroutineScope(Dispatchers.IO + job).launch {
delay(interval.toMillis())
eventPublisher.publishEvent(event)
}
return job
}

override fun startTimer(interval: Duration, callback: () -> Unit): JobWithStartTime {
val job = JobWithStartTime()
CoroutineScope(Dispatchers.IO + job).launch {
delay(interval.toMillis())
callback.invoke()
}
return job
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ fun XorkerException.getButtons(): List<ExceptionButtonType> {
InvalidMafiaGamePlayingPhaseStatusException,
NotFoundWordException,
InvalidBroadcastException,
is InvalidMafiaPhaseException,
InvalidRequestOnlyMyTurnException,
-> buttonOk
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.xorker.draw.websocket

object EmptyObject
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.xorker.draw.websocket
import com.fasterxml.jackson.databind.ObjectMapper
import com.xorker.draw.exception.InvalidRequestValueException
import com.xorker.draw.mafia.MafiaGameUseCase
import com.xorker.draw.mafia.MafiaStartGameUseCase
import com.xorker.draw.mafia.MafiaPhaseUseCase
import com.xorker.draw.room.RoomId
import com.xorker.draw.websocket.message.request.RequestAction
import com.xorker.draw.websocket.message.request.dto.StartMafiaGameRequest
Expand All @@ -16,7 +16,7 @@ class WebSocketRouter(
private val objectMapper: ObjectMapper,
private val webSocketController: WebSocketController,
private val sessionUseCase: SessionUseCase,
private val mafiaStartGameUseCase: MafiaStartGameUseCase,
private val mafiaPhaseUseCase: MafiaPhaseUseCase,
private val mafiaGameUseCase: MafiaGameUseCase,
) {
fun route(session: WebSocketSession, request: WebSocketRequest) {
Expand All @@ -26,15 +26,17 @@ class WebSocketRouter(
val requestDto = request.extractBody<StartMafiaGameRequest>()
val roomId = RoomId(requestDto.roomId)

mafiaStartGameUseCase.startMafiaGame(roomId)
}
RequestAction.DRAW -> {
val sessionDto = sessionUseCase.getSession(SessionId(session.id)) ?: throw InvalidRequestValueException
mafiaGameUseCase.draw(sessionDto, request.extractBody())
mafiaPhaseUseCase.startGame(roomId)
}

RequestAction.DRAW -> mafiaGameUseCase.draw(session.getDto(), request.extractBody())
RequestAction.END_TURN -> mafiaGameUseCase.nextTurnByUser(session.getDto())
}
}

private fun WebSocketSession.getDto(): Session =
sessionUseCase.getSession(SessionId(this.id)) ?: throw InvalidRequestValueException

private inline fun <reified T : Any> WebSocketRequest.extractBody(): T {
return objectMapper.readValue(this.body, T::class.java)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.xorker.draw.websocket.BroadcastEvent
import com.xorker.draw.websocket.RespectiveBroadcastEvent
import com.xorker.draw.websocket.SessionMessageBroker
import com.xorker.draw.websocket.SessionUseCase
import com.xorker.draw.websocket.UnicastEvent
import com.xorker.draw.websocket.parser.WebSocketResponseParser
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Component
Expand All @@ -18,6 +19,15 @@ class SimpleSessionMessageBroker(
private val parser: WebSocketResponseParser,
) : SessionMessageBroker {

@EventListener
override fun unicast(event: UnicastEvent) {
val userId = event.userId
val session = sessionUseCase.getSession(userId)
val response = parser.parse(event.message)

session?.send(response)
}

@EventListener
override fun broadcast(event: BroadcastEvent) {
val roomId = event.roomId
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,44 @@
package com.xorker.draw.websocket.broker

import com.xorker.draw.room.RoomId
import com.xorker.draw.user.UserId
import com.xorker.draw.websocket.BranchedBroadcastEvent
import com.xorker.draw.websocket.BroadcastEvent
import com.xorker.draw.websocket.RespectiveBroadcastEvent
import com.xorker.draw.websocket.SessionMessage
import com.xorker.draw.websocket.UnicastEvent
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Component

interface WebSocketBroadcaster {
fun unicast(userId: UserId, message: SessionMessage)
fun broadcast(roomId: RoomId, sessionMessage: SessionMessage)
fun publishBroadcastEvent(event: BroadcastEvent)
fun publishBranchedBroadcastEvent(event: BranchedBroadcastEvent)
fun publishRespectiveBroadcastEvent(event: RespectiveBroadcastEvent)
}

@Component
class WebSocketBroadcaster(
internal class WebSocketBroadcasterSingleInstance(
private val publisher: ApplicationEventPublisher,
) {
fun publishBroadcastEvent(event: BroadcastEvent) {
) : WebSocketBroadcaster {
override fun unicast(userId: UserId, message: SessionMessage) {
publisher.publishEvent(UnicastEvent(userId, message))
}

override fun broadcast(roomId: RoomId, sessionMessage: SessionMessage) {
publisher.publishEvent(BroadcastEvent(roomId, sessionMessage))
}

override fun publishBroadcastEvent(event: BroadcastEvent) {
publisher.publishEvent(event)
}

fun publishBranchedBroadcastEvent(event: BranchedBroadcastEvent) {
override fun publishBranchedBroadcastEvent(event: BranchedBroadcastEvent) {
publisher.publishEvent(event)
}

fun publishRespectiveBroadcastEvent(event: RespectiveBroadcastEvent) {
override fun publishRespectiveBroadcastEvent(event: RespectiveBroadcastEvent) {
publisher.publishEvent(event)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ enum class RequestAction(
INIT("세션 초기화"),
START_GAME("마피아 게임 시작"),
DRAW("그림 그리기"),
END_TURN("턴 넘기기"),
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,41 @@ import com.xorker.draw.exception.InvalidMafiaGamePlayingPhaseStatusException
import com.xorker.draw.mafia.MafiaGameInfo
import com.xorker.draw.mafia.MafiaGameMessenger
import com.xorker.draw.mafia.MafiaPhase
import com.xorker.draw.mafia.MafiaPlayer
import com.xorker.draw.room.Room
import com.xorker.draw.mafia.MafiaPhaseWithTurnList
import com.xorker.draw.mafia.assertIs
import com.xorker.draw.room.RoomId
import com.xorker.draw.user.UserId
import com.xorker.draw.websocket.BranchedBroadcastEvent
import com.xorker.draw.websocket.BroadcastEvent
import com.xorker.draw.websocket.RespectiveBroadcastEvent
import com.xorker.draw.websocket.SessionMessage
import com.xorker.draw.websocket.broker.WebSocketBroadcaster
import com.xorker.draw.websocket.message.response.dto.MafiaGameDrawBody
import com.xorker.draw.websocket.message.response.dto.MafiaGameDrawMessage
import com.xorker.draw.websocket.message.response.dto.MafiaGameInfoBody
import com.xorker.draw.websocket.message.response.dto.MafiaGameInfoMessage
import com.xorker.draw.websocket.message.response.dto.MafiaGameReadyBody
import com.xorker.draw.websocket.message.response.dto.MafiaGameReadyMessage
import com.xorker.draw.websocket.message.response.dto.MafiaGameTurnInfoBody
import com.xorker.draw.websocket.message.response.dto.MafiaGameTurnInfoMessage
import com.xorker.draw.websocket.message.response.dto.MafiaPlayerListBody
import com.xorker.draw.websocket.message.response.dto.MafiaPlayerListMessage
import com.xorker.draw.websocket.message.response.dto.MafiaPlayerTurnListBody
import com.xorker.draw.websocket.message.response.dto.MafiaPlayerTurnListMessage
import com.xorker.draw.websocket.message.response.dto.toResponse
import java.time.LocalDateTime
import org.springframework.stereotype.Component

@Component
class MafiaGameMessengerImpl(
private val broadcaster: WebSocketBroadcaster,
) : MafiaGameMessenger {

override fun broadcastPlayerList(room: Room<MafiaPlayer>) {
val roomId = room.id
override fun broadcastPlayerList(gameInfo: MafiaGameInfo) {
val roomId = gameInfo.room.id
val phase = gameInfo.phase

val list =
if (phase is MafiaPhaseWithTurnList) {
phase.turnList
} else {
gameInfo.room.players
}

val message = MafiaPlayerListMessage(
MafiaPlayerListBody(
roomId,
room.players.map { it.toResponse(room.owner) }.toList(),
list.map { it.toResponse(gameInfo.room.owner) }.toList(),
),
)

Expand All @@ -47,72 +47,6 @@ class MafiaGameMessengerImpl(
broadcaster.publishBroadcastEvent(event)
}

override fun broadcastGameInfo(mafiaGameInfo: MafiaGameInfo) {
val roomId = mafiaGameInfo.room.id

val phase = mafiaGameInfo.phase as? MafiaPhase.Playing ?: throw InvalidMafiaGamePlayingPhaseStatusException

val mafia = phase.mafiaPlayer
val keyword = phase.keyword

val gameOption = mafiaGameInfo.gameOption

val message = MafiaGameInfoMessage(
MafiaGameInfoBody(
category = keyword.category,
answer = keyword.answer,
gameOption = gameOption.toResponse(),
),
)

val branchedMessage = MafiaGameInfoMessage(
MafiaGameInfoBody(
isMafia = true,
category = keyword.category,
answer = keyword.answer,
gameOption = gameOption.toResponse(),
),
)

val branched = setOf(mafia.userId)

val event = BranchedBroadcastEvent(
roomId = roomId,
branched = branched,
message = message,
branchedMessage = branchedMessage,
)

broadcaster.publishBranchedBroadcastEvent(event)
}

override fun broadcastGameReady(mafiaGameInfo: MafiaGameInfo) {
val roomId = mafiaGameInfo.room.id

val phase = mafiaGameInfo.phase as? MafiaPhase.Playing ?: throw InvalidMafiaGamePlayingPhaseStatusException

val turnList = phase.turnList

val messages = mutableMapOf<UserId, SessionMessage>()

turnList.forEachIndexed { i, player ->
val message = MafiaGameReadyMessage(
MafiaGameReadyBody(
turn = i + 1,
player = player.toResponse(mafiaGameInfo.room.owner),
),
)
messages[player.userId] = message
}

val event = RespectiveBroadcastEvent(
roomId = roomId,
messages = messages,
)

broadcaster.publishRespectiveBroadcastEvent(event)
}

override fun broadcastPlayerTurnList(mafiaGameInfo: MafiaGameInfo) {
val room = mafiaGameInfo.room
val roomId = room.id
Expand Down Expand Up @@ -155,22 +89,20 @@ class MafiaGameMessengerImpl(
broadcaster.publishBranchedBroadcastEvent(event)
}

override fun broadcastDraw(roomId: RoomId, phase: MafiaPhase.Playing) {
if (phase !is MafiaPhase.Playing) throw InvalidMafiaGamePlayingPhaseStatusException

val event = BroadcastEvent(
roomId,
MafiaGameDrawMessage(
MafiaGameDrawBody(
round = phase.round,
turn = phase.turn,
startTurnTime = LocalDateTime.now(), // TOOD: 턴 시스템 도입 시 수정
draw = phase.drawData.take(phase.drawData.size - 1).map { it.second },
currentDraw = phase.drawData.last().second,
),
),
)
override fun broadcastDraw(roomId: RoomId, data: Map<String, Any>) {
val message = MafiaGameDrawMessage(data)
broadcaster.broadcast(roomId, message)
}

broadcaster.publishBroadcastEvent(event)
override fun broadcastNextTurn(gameInfo: MafiaGameInfo) {
val phase = gameInfo.phase
assertIs<MafiaPhase.Playing>(phase)
val body = MafiaGameTurnInfoBody(
phase.round,
phase.turn,
phase.timerJob.startTime,
phase.turnList[phase.turn].userId,
)
broadcaster.broadcast(gameInfo.room.id, MafiaGameTurnInfoMessage(body))
}
}
Loading

0 comments on commit 2751412

Please sign in to comment.