Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49a1bd40ff | ||
| f847424b9c | |||
|
|
d50a576e31 |
@@ -198,3 +198,9 @@
|
|||||||
|
|
||||||
* **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))
|
* **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-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))
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class UserSession(val user: User, val host: Boolean, val gameLobby: GameLobby) e
|
|||||||
else canInteract = Some(InteractionType.Card)
|
else canInteract = Some(InteractionType.Card)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
|
websocketActor.foreach(_.solveRequests())
|
||||||
websocketActor.foreach(_.transmitEventToClient(event))
|
websocketActor.foreach(_.transmitEventToClient(event))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,49 +39,41 @@ class UserSession(val user: User, val host: Boolean, val gameLobby: GameLobby) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
def handleWebResponse(eventType: String, data: JsObject): Unit = {
|
def handleWebResponse(eventType: String, data: JsObject): Unit = {
|
||||||
lock.lock()
|
eventType match {
|
||||||
val result = Try {
|
case "ping" =>
|
||||||
eventType match {
|
// No action needed for Ping
|
||||||
case "ping" =>
|
()
|
||||||
// No action needed for Ping
|
case "StartGame" =>
|
||||||
()
|
gameLobby.startGame(user)
|
||||||
case "StartGame" =>
|
case "PlayCard" =>
|
||||||
gameLobby.startGame(user)
|
val maybeCardIndex: Option[String] = (data \ "cardindex").asOpt[String]
|
||||||
case "PlayCard" =>
|
maybeCardIndex match {
|
||||||
val maybeCardIndex: Option[String] = (data \ "cardindex").asOpt[String]
|
case Some(index) =>
|
||||||
maybeCardIndex match {
|
val session = gameLobby.getUserSession(user.id)
|
||||||
case Some(index) =>
|
gameLobby.playCard(session, index.toInt)
|
||||||
val session = gameLobby.getUserSession(user.id)
|
case None =>
|
||||||
gameLobby.playCard(session, index.toInt)
|
println("Card Index not found or is not a number.")
|
||||||
case None =>
|
}
|
||||||
println("Card Index not found or is not a number.")
|
case "PickTrumpsuit" =>
|
||||||
}
|
val maybeSuitIndex: Option[Int] = (data \ "suitIndex").asOpt[Int]
|
||||||
case "PickTrumpsuit" =>
|
maybeSuitIndex match {
|
||||||
val maybeSuitIndex: Option[Int] = (data \ "suitIndex").asOpt[Int]
|
case Some(index) =>
|
||||||
maybeSuitIndex match {
|
val session = gameLobby.getUserSession(user.id)
|
||||||
case Some(index) =>
|
gameLobby.selectTrump(session, index)
|
||||||
val session = gameLobby.getUserSession(user.id)
|
case None =>
|
||||||
gameLobby.selectTrump(session, index)
|
println("Card Index not found or is not a number.")
|
||||||
case None =>
|
}
|
||||||
println("Card Index not found or is not a number.")
|
case "KickPlayer" =>
|
||||||
}
|
val maybePlayerId: Option[String] = (data \ "playerId").asOpt[String]
|
||||||
case "KickPlayer" =>
|
maybePlayerId match {
|
||||||
val maybePlayerId: Option[String] = (data \ "playerId").asOpt[String]
|
case Some(id) =>
|
||||||
maybePlayerId match {
|
val playerUUID = UUID.fromString(id)
|
||||||
case Some(id) =>
|
gameLobby.leaveGame(playerUUID, true)
|
||||||
val playerUUID = UUID.fromString(id)
|
case None =>
|
||||||
gameLobby.leaveGame(playerUUID, true)
|
println("Player ID not found or is not a valid UUID.")
|
||||||
case None =>
|
}
|
||||||
println("Player ID not found or is not a valid UUID.")
|
case "ReturnToLobby" =>
|
||||||
}
|
gameLobby.returnToLobby(this)
|
||||||
case "ReturnToLobby" =>
|
|
||||||
gameLobby.returnToLobby(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lock.unlock()
|
|
||||||
if (result.isFailure) {
|
|
||||||
val throwable = result.failed.get
|
|
||||||
throw throwable
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import org.apache.pekko.actor.{Actor, ActorRef}
|
|||||||
import play.api.libs.json.{JsObject, JsValue, Json}
|
import play.api.libs.json.{JsObject, JsValue, Json}
|
||||||
import util.WebsocketEventMapper
|
import util.WebsocketEventMapper
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
class UserWebsocketActor(
|
class UserWebsocketActor(
|
||||||
@@ -12,6 +13,8 @@ class UserWebsocketActor(
|
|||||||
session: UserSession
|
session: UserSession
|
||||||
) extends Actor {
|
) extends Actor {
|
||||||
|
|
||||||
|
private val requests: mutable.Map[String, String] = mutable.Map()
|
||||||
|
|
||||||
{
|
{
|
||||||
session.lock.lock()
|
session.lock.lock()
|
||||||
if (session.websocketActor.isDefined) {
|
if (session.websocketActor.isDefined) {
|
||||||
@@ -48,12 +51,14 @@ class UserWebsocketActor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def handle(json: JsValue): Unit = {
|
private def handle(json: JsValue): Unit = {
|
||||||
|
session.lock.lock()
|
||||||
val idOpt = (json \ "id").asOpt[String]
|
val idOpt = (json \ "id").asOpt[String]
|
||||||
if (idOpt.isEmpty) {
|
if (idOpt.isEmpty) {
|
||||||
transmitJsonToClient(Json.obj(
|
transmitJsonToClient(Json.obj(
|
||||||
"status" -> "error",
|
"status" -> "error",
|
||||||
"error" -> "Missing 'id' field"
|
"error" -> "Missing 'id' field"
|
||||||
))
|
))
|
||||||
|
session.lock.unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val id = idOpt.get
|
val id = idOpt.get
|
||||||
@@ -65,17 +70,25 @@ class UserWebsocketActor(
|
|||||||
"status" -> "error",
|
"status" -> "error",
|
||||||
"error" -> "Missing 'event' field"
|
"error" -> "Missing 'event' field"
|
||||||
))
|
))
|
||||||
|
session.lock.unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val statusOpt = (json \ "status").asOpt[String]
|
val statusOpt = (json \ "status").asOpt[String]
|
||||||
if (statusOpt.isDefined) {
|
if (statusOpt.isDefined) {
|
||||||
|
session.lock.unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val event = eventOpt.get
|
val event = eventOpt.get
|
||||||
val data = (json \ "data").asOpt[JsObject].getOrElse(Json.obj())
|
val data = (json \ "data").asOpt[JsObject].getOrElse(Json.obj())
|
||||||
|
requests += (id -> event)
|
||||||
val result = Try {
|
val result = Try {
|
||||||
session.handleWebResponse(event, data)
|
session.handleWebResponse(event, data)
|
||||||
}
|
}
|
||||||
|
if (!requests.contains(id)) {
|
||||||
|
session.lock.unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
requests -= id
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
transmitJsonToClient(Json.obj(
|
transmitJsonToClient(Json.obj(
|
||||||
"id" -> id,
|
"id" -> id,
|
||||||
@@ -90,6 +103,7 @@ class UserWebsocketActor(
|
|||||||
"error" -> result.failed.get.getMessage
|
"error" -> result.failed.get.getMessage
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
session.lock.unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
def transmitJsonToClient(jsonObj: JsValue): Unit = {
|
def transmitJsonToClient(jsonObj: JsValue): Unit = {
|
||||||
@@ -100,4 +114,20 @@ class UserWebsocketActor(
|
|||||||
transmitJsonToClient(WebsocketEventMapper.toJson(event, session))
|
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"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
MAJOR=4
|
MAJOR=4
|
||||||
MINOR=7
|
MINOR=7
|
||||||
PATCH=1
|
PATCH=3
|
||||||
|
|||||||
Reference in New Issue
Block a user