Compare commits

..

2 Commits

Author SHA1 Message Date
LQ63
ead79cf355 fix(ui): Endscreen
Removed duplicated line
2025-12-02 13:34:48 +01:00
LQ63
a0df05e417 feat(ui): Endscreen
Added a nice look to the endscreen. Implemented a ranking method inside GameLobby to get an order.
2025-12-02 13:19:49 +01:00
6 changed files with 45 additions and 86 deletions

5
.gitmodules vendored
View File

@@ -2,8 +2,3 @@
path = knockoutwhist
branch = main
url = https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist.git
[submodule "knockoutwhistfrontend"]
path = knockoutwhistfrontend
branch = main
url = https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Frontend.git

View File

@@ -192,15 +192,3 @@
### Bug Fixes
* FRO-6 Websocket Close Handle ([#96](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/issues/96)) ([3585566](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/commit/358556612ec74601c8b31125e4e65f750abf8c4c))
## (2025-12-03)
### Features
* **ui:** FRO-7 Endscreen ([#97](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/issues/97)) ([d57e6ef](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/commit/d57e6efa985ca07c32f9f54595fe7393dbdf4d8a))
## (2025-12-03)
## (2025-12-03)
## (2025-12-04)
### Bug Fixes
* BAC-25 Race Condition: Websocket Promises ([#99](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/issues/99)) ([f847424](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/commit/f847424b9cea423ace5661d1efb6e4f01483c655))

View File

@@ -26,7 +26,6 @@ class UserSession(val user: User, val host: Boolean, val gameLobby: GameLobby) e
else canInteract = Some(InteractionType.Card)
case _ =>
}
websocketActor.foreach(_.solveRequests())
websocketActor.foreach(_.transmitEventToClient(event))
}
@@ -39,41 +38,49 @@ class UserSession(val user: User, val host: Boolean, val gameLobby: GameLobby) e
}
def handleWebResponse(eventType: String, data: JsObject): Unit = {
eventType match {
case "ping" =>
// No action needed for Ping
()
case "StartGame" =>
gameLobby.startGame(user)
case "PlayCard" =>
val maybeCardIndex: Option[String] = (data \ "cardindex").asOpt[String]
maybeCardIndex match {
case Some(index) =>
val session = gameLobby.getUserSession(user.id)
gameLobby.playCard(session, index.toInt)
case None =>
println("Card Index not found or is not a number.")
}
case "PickTrumpsuit" =>
val maybeSuitIndex: Option[Int] = (data \ "suitIndex").asOpt[Int]
maybeSuitIndex match {
case Some(index) =>
val session = gameLobby.getUserSession(user.id)
gameLobby.selectTrump(session, index)
case None =>
println("Card Index not found or is not a number.")
}
case "KickPlayer" =>
val maybePlayerId: Option[String] = (data \ "playerId").asOpt[String]
maybePlayerId match {
case Some(id) =>
val playerUUID = UUID.fromString(id)
gameLobby.leaveGame(playerUUID, true)
case None =>
println("Player ID not found or is not a valid UUID.")
}
case "ReturnToLobby" =>
gameLobby.returnToLobby(this)
lock.lock()
val result = Try {
eventType match {
case "ping" =>
// No action needed for Ping
()
case "StartGame" =>
gameLobby.startGame(user)
case "PlayCard" =>
val maybeCardIndex: Option[String] = (data \ "cardindex").asOpt[String]
maybeCardIndex match {
case Some(index) =>
val session = gameLobby.getUserSession(user.id)
gameLobby.playCard(session, index.toInt)
case None =>
println("Card Index not found or is not a number.")
}
case "PickTrumpsuit" =>
val maybeSuitIndex: Option[Int] = (data \ "suitIndex").asOpt[Int]
maybeSuitIndex match {
case Some(index) =>
val session = gameLobby.getUserSession(user.id)
gameLobby.selectTrump(session, index)
case None =>
println("Card Index not found or is not a number.")
}
case "KickPlayer" =>
val maybePlayerId: Option[String] = (data \ "playerId").asOpt[String]
maybePlayerId match {
case Some(id) =>
val playerUUID = UUID.fromString(id)
gameLobby.leaveGame(playerUUID, true)
case None =>
println("Player ID not found or is not a valid UUID.")
}
case "ReturnToLobby" =>
gameLobby.returnToLobby(this)
}
}
lock.unlock()
if (result.isFailure) {
val throwable = result.failed.get
throw throwable
}
}

View File

@@ -5,7 +5,6 @@ import org.apache.pekko.actor.{Actor, ActorRef}
import play.api.libs.json.{JsObject, JsValue, Json}
import util.WebsocketEventMapper
import scala.collection.mutable
import scala.util.{Failure, Success, Try}
class UserWebsocketActor(
@@ -13,8 +12,6 @@ class UserWebsocketActor(
session: UserSession
) extends Actor {
private val requests: mutable.Map[String, String] = mutable.Map()
{
session.lock.lock()
if (session.websocketActor.isDefined) {
@@ -51,14 +48,12 @@ class UserWebsocketActor(
}
private def handle(json: JsValue): Unit = {
session.lock.lock()
val idOpt = (json \ "id").asOpt[String]
if (idOpt.isEmpty) {
transmitJsonToClient(Json.obj(
"status" -> "error",
"error" -> "Missing 'id' field"
))
session.lock.unlock()
return
}
val id = idOpt.get
@@ -70,25 +65,17 @@ class UserWebsocketActor(
"status" -> "error",
"error" -> "Missing 'event' field"
))
session.lock.unlock()
return
}
val statusOpt = (json \ "status").asOpt[String]
if (statusOpt.isDefined) {
session.lock.unlock()
return
}
val event = eventOpt.get
val data = (json \ "data").asOpt[JsObject].getOrElse(Json.obj())
requests += (id -> event)
val result = Try {
session.handleWebResponse(event, data)
}
if (!requests.contains(id)) {
session.lock.unlock()
return
}
requests -= id
if (result.isSuccess) {
transmitJsonToClient(Json.obj(
"id" -> id,
@@ -103,7 +90,6 @@ class UserWebsocketActor(
"error" -> result.failed.get.getMessage
))
}
session.lock.unlock()
}
def transmitJsonToClient(jsonObj: JsValue): Unit = {
@@ -114,20 +100,4 @@ class UserWebsocketActor(
transmitJsonToClient(WebsocketEventMapper.toJson(event, session))
}
def solveRequests(): Unit = {
if (!session.lock.isHeldByCurrentThread)
return;
if (requests.isEmpty)
return;
val pendingRequests = requests.toMap
requests.clear()
pendingRequests.foreach { case (id, event) =>
transmitJsonToClient(Json.obj(
"id" -> id,
"event" -> event,
"status" -> "success"
))
}
}
}

View File

@@ -1,3 +1,3 @@
MAJOR=4
MINOR=7
PATCH=3
MINOR=6
PATCH=2