Skip to content

Commit

Permalink
Merge pull request #16 from f-lab-edu/feat/4-logging-file-output
Browse files Browse the repository at this point in the history
[Feat][#4] logging 기능 구현
  • Loading branch information
Stark-Industries0417 authored Feb 5, 2025
2 parents 6ce1424 + 6bec6a8 commit aa22543
Show file tree
Hide file tree
Showing 22 changed files with 355 additions and 72 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ build/
.kotlin

### Mac OS ###
.DS_Store
.DS_Store
logs
12 changes: 12 additions & 0 deletions game-server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM openjdk:17-alpine

RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app
COPY build/libs/app.jar /app/app.jar

RUN chown -R appuser:appgroup /app

USER appuser

ENTRYPOINT ["java", "-jar", "/app/app.jar"]
2 changes: 2 additions & 0 deletions game-server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ dependencies {
// Serialize kotlin
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.15.2")

implementation("net.logstash.logback:logstash-logback-encoder:7.4")

testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage")
}
Expand Down
7 changes: 6 additions & 1 deletion game-server/src/main/kotlin/game/server/Player.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package game.server

import game.server.dto.Position
import game.server.domain.Position
import game.server.handler.CANVAS_HEIGHT
import game.server.handler.CANVAS_WIDTH
import org.springframework.stereotype.Component


Expand All @@ -11,4 +13,7 @@ class Player(
val speed: Int = 5
) {
var position = Position(400, 300)

fun isMoveAllowed(x: Int, y: Int) =
x in 0 until CANVAS_WIDTH && y in 0 until CANVAS_HEIGHT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package game.server.config

import ch.qos.logback.classic.spi.ILoggingEvent
import com.fasterxml.jackson.core.JsonGenerator
import game.server.dto.request.ApiRequest
import game.server.dto.response.ApiResponse
import game.server.dto.response.ErrorResponse
import net.logstash.logback.composite.AbstractJsonProvider

class CustomJsonProvider : AbstractJsonProvider<ILoggingEvent>() {

override fun writeTo(generator: JsonGenerator, event: ILoggingEvent) {
val message = event.argumentArray?.firstOrNull()

try {
when (message) {
is ApiResponse<*> -> {
generator.writeStringField("messageType", message.messageType)
generator.writeStringField("type", message.type)
generator.writeBooleanField("success", message.success)
generator.writeObjectField("data", message.data)
if (message is ErrorResponse<*>) generator.writeStringField("message", message.message)
}
is ApiRequest<*> -> {
generator.writeStringField("messageType", message.messageType)
generator.writeStringField("type", message.type)
generator.writeObjectField("data", message.data)
}
else -> {
generator.writeStringField("message", event.formattedMessage)
}
}
} catch (e: Exception) {
generator.writeStringField("error", e.message)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAd
open class WebSocketConfig : WebFluxConfigurer {

@Bean
open fun webSocketHandlerMapping(gameWebSocketHandler: GameRequestRouter): SimpleUrlHandlerMapping {
open fun webSocketHandlerMapping(gameRequestRouter: GameRequestRouter): SimpleUrlHandlerMapping {
return SimpleUrlHandlerMapping()
.apply {
urlMap = mapOf("/ws/game" to gameWebSocketHandler)
urlMap = mapOf("/ws/game" to gameRequestRouter)
order = 0
}
}
Expand Down
2 changes: 2 additions & 0 deletions game-server/src/main/kotlin/game/server/dto/EnemyResponse.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package game.server.dto

import game.server.domain.Position


data class EnemyInfoResponse(
val enemyId: String,
Expand Down
3 changes: 0 additions & 3 deletions game-server/src/main/kotlin/game/server/dto/Request.kt

This file was deleted.

12 changes: 12 additions & 0 deletions game-server/src/main/kotlin/game/server/dto/request/ApiRequest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package game.server.dto.request

sealed class ApiRequest<T>(
open val type: String,
val messageType: String = "request",
open val data: T?
)

data class Request<T>(
override val type: String,
override val data: T
) : ApiRequest<T>(type = type, data = data)
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package game.server.dto
package game.server.dto.request

import game.server.domain.Position
import game.server.dto.Direction

data class PlayerMoveRequest(
data class PlayerMoveRequestData(
val currentPosition: Position,
val direction: Direction,
val speed: Int
) : Request
)
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
package game.server.dto.response

sealed class ApiResponse {
abstract val type: String
}
sealed class ApiResponse<T>(
open val type: String,
open val success: Boolean = true,
open val messageType: String = "response",
open val data: T?
)

data class Success<T>(override val type: String, val success: Boolean = true, val data: T) : ApiResponse()
data class Error(override val type: String, val success: Boolean = false, val message: String) : ApiResponse()
data class Response<T>(
override val type: String,
override val success: Boolean = true,
override val data: T
) : ApiResponse<T>(type = type, data = data)

data class ErrorResponse<T>(
override val type: String = "error",
override val success: Boolean = false,
val message: String,
) : ApiResponse<T>(
type = type,
success = success,
data = null
)
2 changes: 1 addition & 1 deletion game-server/src/main/kotlin/game/server/enemy/Enemy.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package game.server.enemy

import game.server.Player
import game.server.domain.Position
import game.server.dto.EnemyInfoResponse
import game.server.dto.Position

data class Enemy(
val id: String,
Expand Down
4 changes: 1 addition & 3 deletions game-server/src/main/kotlin/game/server/enemy/EnemyAI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package game.server.enemy

import game.server.Player
import game.server.bt.*
import game.server.dto.Position
import game.server.domain.Position
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
Expand Down Expand Up @@ -62,8 +62,6 @@ class EnemyAI(private val enemy: Enemy, private val player: Player) {
val newX = (enemy.position.x + cos(angle) * speed).toInt()
val newY = (enemy.position.y + sin(angle) * speed).toInt()

println("Enemy ${enemy.id} moving from (${enemy.position.x}, ${enemy.position.y}) to ($newX, $newY)")

enemy.position = Position(newX, newY)
return true
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package game.server.handler

import com.fasterxml.jackson.core.type.TypeReference
import game.server.Player
import game.server.domain.Position
import game.server.dto.Direction
import game.server.dto.Direction.*
import game.server.dto.PlayerMoveRequest
import game.server.dto.request.PlayerMoveRequestData
import game.server.dto.request.Request
import game.server.dto.response.ApiResponse
import game.server.dto.response.Error
import game.server.dto.response.ErrorResponse
import game.server.dto.response.MoveResponseData
import game.server.dto.response.Success
import game.server.dto.response.Response
import org.springframework.stereotype.Component

const val CANVAS_WIDTH = 800
Expand All @@ -17,21 +19,24 @@ const val CANVAS_HEIGHT = 600
@Component("move")
class PlayerMoveHandler(
private val player: Player
) : RequestHandler<PlayerMoveRequest> {
) : RequestHandler<PlayerMoveRequestData, MoveResponseData> {

override fun handle(request: PlayerMoveRequest): ApiResponse {
val (currentX, currentY) = request.currentPosition
val (newX, newY) = calculateNewPosition(currentX, currentY, request.direction, request.speed)
override val requestTypeReference: TypeReference<Request<PlayerMoveRequestData>> =
object : TypeReference<Request<PlayerMoveRequestData>>() {}

val isAllowed = isMoveAllowed(newX, newY)
override fun handle(request: Request<PlayerMoveRequestData>): ApiResponse<MoveResponseData> {
val (currentX, currentY) = request.data.currentPosition
val (newX, newY) = calculateNewPosition(currentX, currentY, request.data.direction, request.data.speed)

val isAllowed = player.isMoveAllowed(newX, newY)
return if (isAllowed) {
player.position = Position(newX, newY)
Success(
Response(
type = "move",
data = MoveResponseData(Position(newX, newY))
)
} else {
Error(
ErrorResponse(
type = "move",
message = "Move is not allowed"
)
Expand All @@ -46,8 +51,4 @@ class PlayerMoveHandler(
RIGHT -> Position(x + speed, y)
}
}

private fun isMoveAllowed(x: Int, y: Int): Boolean {
return x in 0 until CANVAS_WIDTH && y in 0 until CANVAS_HEIGHT
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package game.server.handler

import game.server.dto.Request
import com.fasterxml.jackson.core.type.TypeReference
import game.server.dto.request.Request
import game.server.dto.response.ApiResponse

interface RequestHandler<T : Request> {
fun handle(request: T): ApiResponse
interface RequestHandler<D, R> {
val requestTypeReference: TypeReference<Request<D>>
fun handle(request: Request<D>): ApiResponse<R>
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package game.server.handler

import game.server.dto.Request
import org.springframework.stereotype.Component

@Component
class RequestHandlerFactory(
private val handlers: Map<String, RequestHandler<*>>
private val handlers: Map<String, RequestHandler<*, *>>
) {

@Suppress("UNCHECKED_CAST")
fun <T : Request> getHandler(type: String): RequestHandler<T> {
return handlers[type] as? RequestHandler<T>
fun <D, R> getHandler(type: String): RequestHandler<D, R> {
return handlers[type] as RequestHandler<D, R>?
?: throw IllegalArgumentException("Unknown request type: $type")
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package game.server.scheduler

import game.server.dto.response.Response
import game.server.enemy.EnemyManager
import game.server.websocket.GameWebSocketHandler
import game.server.websocket.GameRequestRouter
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component


@Component
class EnemyAIScheduler(
private val webSocketHandler: GameWebSocketHandler,
private val router: GameRequestRouter,
private val enemyManager: EnemyManager,
) {

Expand All @@ -21,10 +22,7 @@ class EnemyAIScheduler(
private fun notifyClients() {
val enemyPackets = enemyManager.getAllEnemies().map { it.toPacket() }

val message = mapOf(
"type" to "enemy_positions",
"data" to enemyPackets
)
webSocketHandler.sendToClient(message)
val response = Response(type = "enemy_positions", data = enemyPackets)
router.sendToClient(response)
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package game.server.scheduler

import game.server.domain.Position
import game.server.dto.response.Response
import game.server.handler.CANVAS_HEIGHT
import game.server.handler.CANVAS_WIDTH
import game.server.dto.Position
import game.server.enemy.Enemy
import game.server.enemy.EnemyManager
import game.server.enemy.EnemyStatus
import game.server.websocket.GameWebSocketHandler
import game.server.websocket.GameRequestRouter
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import java.lang.Math.*
Expand All @@ -18,7 +19,7 @@ import kotlin.random.Random

@Component
class EnemySpawnScheduler(
private val webSocketHandler: GameWebSocketHandler,
private val gameRequestRouter: GameRequestRouter,
private val enemyManager: EnemyManager
) {
private val center = Position(400, 300)
Expand All @@ -35,13 +36,9 @@ class EnemySpawnScheduler(
createRandomEnemy(center, radius).toPacket()
}

val message = mapOf(
"type" to "enemy_spawn",
"data" to enemies,
"round" to round
)
val response = Response(type = "enemy_spawn", data = enemies)
round++
webSocketHandler.sendToClient(message)
gameRequestRouter.sendToClient(response)
}

private fun createRandomEnemy(center: Position, radius: Int, minDistance: Int = 270): Enemy {
Expand Down
Loading

0 comments on commit aa22543

Please sign in to comment.