Compare commits

...

4 Commits

Author SHA1 Message Date
TeamCity
8812b0fad4 ci: bump version to v4.10.0 2025-12-10 10:40:36 +00:00
dd5e8e65e5 feat: BAC-27 Implemented endpoint which returns information about the current state (#103)
Reviewed-on: #103
Reviewed-by: lq64 <lq@blackhole.local>
Co-authored-by: Janis <janis.e.20@gmx.de>
Co-committed-by: Janis <janis.e.20@gmx.de>
2025-12-10 11:37:35 +01:00
TeamCity
bf6ffeadb0 ci: bump version to v4.9.1 2025-12-10 08:46:31 +00:00
fa3d21e303 fix: FRO-29 Websocket Communication (#104)
Reviewed-on: #104
Co-authored-by: Janis <janis.e.20@gmx.de>
Co-committed-by: Janis <janis.e.20@gmx.de>
2025-12-10 09:42:50 +01:00
13 changed files with 136 additions and 14 deletions

View File

@@ -219,3 +219,13 @@
### Features
* BAC-30 Implement Jackson Mapping via DTOs ([#102](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/issues/102)) ([8d697fd](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/commit/8d697fd311478cf792b4631377de4522ecbda9f7))
## (2025-12-10)
### Bug Fixes
* FRO-29 Websocket Communication ([#104](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/issues/104)) ([fa3d21e](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/commit/fa3d21e3038eb07369764850a9ad9badd269ac57))
## (2025-12-10)
### Features
* BAC-27 Implemented endpoint which returns information about the current state ([#103](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/issues/103)) ([dd5e8e6](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/commit/dd5e8e65e55f02a7618b3c60e8fc7087774e5106))

View File

@@ -0,0 +1,94 @@
package controllers
import auth.AuthAction
import logic.PodManager
import logic.game.GameLobby
import logic.user.SessionManager
import model.users.User
import play.api.libs.json.{JsValue, Json}
import play.api.mvc.*
import util.WebsocketEventMapper
import javax.inject.Inject
class StatusController @Inject()(
val controllerComponents: ControllerComponents,
val sessionManager: SessionManager,
val authAction: AuthAction
) extends BaseController {
def requestStatus(): Action[AnyContent] = {
Action { implicit request =>
val userOpt = getUserFromSession(request)
if (userOpt.isEmpty) {
Ok(
Json.obj(
"status" -> "unauthenticated"
)
)
} else {
val user = userOpt.get
val gameOpt = PodManager.identifyGameOfUser(user)
if (gameOpt.isEmpty) {
Ok(
Json.obj(
"status" -> "authenticated",
"username" -> user.name,
"inGame" -> "false"
)
)
} else {
val game = gameOpt.get
Ok(
Json.obj(
"status" -> "authenticated",
"username" -> user.name,
"inGame" -> "true",
"gameId" -> game.id
)
)
}
}
}
}
def game(gameId: String): Action[AnyContent] = {
Action { implicit request =>
val userOpt = getUserFromSession(request)
if (userOpt.isEmpty) {
Unauthorized("User not authenticated")
} else {
val user = userOpt.get
val gameOpt = PodManager.getGame(gameId)
if (gameOpt.isEmpty) {
NotFound("Game not found")
} else {
val game = gameOpt.get
if (!game.getPlayers.contains(user.id)) {
Forbidden("User not part of this game")
} else {
Ok(
Json.obj(
"gameId" -> game.id,
"state" -> game.logic.getCurrentState.toString,
"data" -> mapGameState(game, user)
)
)
}
}
}
}}
private def getUserFromSession(request: RequestHeader): Option[User] = {
val session = request.cookies.get("sessionId")
if (session.isDefined)
return sessionManager.getUserBySession(session.get.value)
None
}
private def mapGameState(gameLobby: GameLobby, user: User): JsValue = {
val userSession = gameLobby.getUserSession(user.id)
WebsocketEventMapper.stateToJson(userSession)
}
}

View File

@@ -4,7 +4,7 @@ import dto.subDTO.UserDTO
import logic.game.GameLobby
import model.users.User
case class LobbyInfoDTO(users: List[UserDTO], self: UserDTO, maxPlayers: Int)
case class LobbyInfoDTO(gameId: String, users: List[UserDTO], self: UserDTO, maxPlayers: Int)
object LobbyInfoDTO {
@@ -12,6 +12,7 @@ object LobbyInfoDTO {
val session = lobby.getUserSession(user.id)
LobbyInfoDTO(
gameId = lobby.id,
users = lobby.getPlayers.values.map(user => UserDTO(user)).toList,
self = UserDTO(session),
maxPlayers = lobby.maxPlayers,

View File

@@ -6,7 +6,7 @@ import model.users.User
import scala.util.Try
case class TieInfoDTO(currentPlayer: Option[PlayerDTO], self: Option[PlayerDTO], tiedPlayers: Seq[PlayerDTO], highestAmount: Int)
case class TieInfoDTO(gameId: String, currentPlayer: Option[PlayerDTO], self: Option[PlayerDTO], tiedPlayers: Seq[PlayerDTO], highestAmount: Int)
object TieInfoDTO {
@@ -16,6 +16,7 @@ object TieInfoDTO {
}.getOrElse(None)
TieInfoDTO(
gameId = lobby.id,
currentPlayer = lobby.logic.playerTieLogic.currentTiePlayer().map(PlayerDTO.apply),
self = selfPlayer.map(PlayerDTO.apply),
tiedPlayers = lobby.logic.playerTieLogic.getTiedPlayers.map(PlayerDTO.apply),

View File

@@ -7,6 +7,7 @@ import model.users.User
import scala.util.Try
case class TrumpInfoDTO(
gameId: String,
chooser: Option[PlayerDTO],
self: Option[PlayerDTO],
selfHand: Option[HandDTO],
@@ -20,6 +21,7 @@ object TrumpInfoDTO {
}.getOrElse(None)
TrumpInfoDTO(
gameId = lobby.id,
chooser = lobby.logic.getTrumpPlayer.map(PlayerDTO(_)),
self = selfPlayer.map(PlayerDTO(_)),
selfHand = selfPlayer.flatMap(_.currentHand()).map(HandDTO(_))

View File

@@ -5,6 +5,7 @@ import logic.game.GameLobby
import model.users.User
case class WonInfoDTO(
gameId: String,
winner: Option[PodiumPlayerDTO],
allPlayers: Seq[PodiumPlayerDTO]
)
@@ -24,6 +25,7 @@ object WonInfoDTO {
val winnerDTO = lobby.logic.getWinner
WonInfoDTO(
gameId = lobby.id,
winner = winnerDTO.map(player => PodiumPlayerDTO(lobby.logic, player)),
allPlayers = allPlayersDTO
)

View File

@@ -3,7 +3,7 @@ package dto.subDTO
import de.knockoutwhist.cards.Card
import util.WebUIUtils
case class CardDTO(identifier: String, path: String, idx: Int) {
case class CardDTO(identifier: String, path: String, idx: Option[Int]) {
def toCard: Card = {
WebUIUtils.stringToCard(identifier)
@@ -13,11 +13,19 @@ case class CardDTO(identifier: String, path: String, idx: Int) {
object CardDTO {
def apply(card: Card, index: Int = 0): CardDTO = {
def apply(card: Card, index: Int): CardDTO = {
CardDTO(
identifier = WebUIUtils.cardtoString(card),
path = WebUIUtils.cardToPath(card),
idx = index
idx = Some(index)
)
}
def apply(card: Card): CardDTO = {
CardDTO(
identifier = WebUIUtils.cardtoString(card),
path = WebUIUtils.cardToPath(card),
idx = None
)
}
}

View File

@@ -3,7 +3,7 @@ package dto.subDTO
import de.knockoutwhist.cards.Card
import de.knockoutwhist.cards.CardValue.Ace
case class RoundDTO(trumpSuit: CardDTO, firstRound: Boolean, tricklist: List[TrickDTO])
case class RoundDTO(trumpSuit: CardDTO, firstRound: Boolean, trickList: List[TrickDTO])
object RoundDTO {
@@ -11,7 +11,7 @@ object RoundDTO {
RoundDTO(
trumpSuit = CardDTO(Card(Ace, round.trumpSuit)),
firstRound = round.firstRound,
tricklist = round.tricklist.map(trick => TrickDTO(trick))
trickList = round.tricklist.map(trick => TrickDTO(trick))
)
}

View File

@@ -2,13 +2,13 @@ package dto.subDTO
import de.knockoutwhist.rounds.Trick
case class TrickDTO(cards: Map[PlayerDTO, CardDTO], firstCard: Option[CardDTO], winner: Option[PlayerDTO])
case class TrickDTO(cards: Map[String, CardDTO], firstCard: Option[CardDTO], winner: Option[PlayerDTO])
object TrickDTO {
def apply(trick: Trick): TrickDTO = {
TrickDTO(
cards = trick.cards.map { case (card, player) => PlayerDTO(player) -> CardDTO(card) },
cards = trick.cards.map { case (card, player) => player.id.toString -> CardDTO(card) },
firstCard = trick.firstCard.map(card => CardDTO(card)),
winner = trick.winner.map(player => PlayerDTO(player))
)

View File

@@ -57,12 +57,12 @@ object WebsocketEventMapper {
Json.obj(
"id" -> ("request-" + java.util.UUID.randomUUID().toString),
"event" -> obj.id,
"state" -> toJson(session),
"state" -> stateToJson(session),
"data" -> data
)
}
def toJson(session: UserSession): JsValue = {
def stateToJson(session: UserSession): JsValue = {
session.gameLobby.getLogic.getCurrentState match {
case Lobby => Json.toJson(LobbyInfoDTO(session.gameLobby, session.user))
case InGame => Json.toJson(GameInfoDTO(session.gameLobby, session.user))

View File

@@ -27,4 +27,8 @@ GET /logout controllers.UserController.logout()
GET /game/:id controllers.IngameController.game(id: String)
# Websocket
GET /websocket controllers.WebsocketController.socket()
GET /websocket controllers.WebsocketController.socket()
# Status
GET /status controllers.StatusController.requestStatus()
GET /status/:gameId controllers.StatusController.game(gameId: String)

View File

@@ -1,3 +1,3 @@
MAJOR=4
MINOR=9
MINOR=10
PATCH=0