diff --git a/knockoutwhist b/knockoutwhist index ec94ecd..afa6bc1 160000 --- a/knockoutwhist +++ b/knockoutwhist @@ -1 +1 @@ -Subproject commit ec94ecd46c4ae89191f99638fab1466e7093ed1e +Subproject commit afa6bc1406e2a0a09b510f3882fe7bf12d2909f5 diff --git a/knockoutwhistweb/app/controllers/IngameController.scala b/knockoutwhistweb/app/controllers/IngameController.scala index 598b470..3929169 100644 --- a/knockoutwhistweb/app/controllers/IngameController.scala +++ b/knockoutwhistweb/app/controllers/IngameController.scala @@ -1,6 +1,7 @@ package controllers import auth.{AuthAction, AuthenticatedRequest} +import de.knockoutwhist.control.GameState import de.knockoutwhist.control.GameState.* import exceptions.* import logic.PodManager @@ -29,7 +30,7 @@ class IngameController @Inject()( game match { case Some(g) => val results = Try { - returnInnerHTML(g, request.user) + IngameController.returnInnerHTML(g, g.logic.getCurrentState, request.user) } if (results.isSuccess) { Ok(views.html.main("In-Game - Knockout Whist")(results.get)) @@ -41,34 +42,6 @@ class IngameController @Inject()( } } - def returnInnerHTML(gameLobby: GameLobby, user: User): Html = { - gameLobby.logic.getCurrentState match { - case Lobby => views.html.lobby.lobby(Some(user), gameLobby) - case InGame => - views.html.ingame.ingame( - gameLobby.getPlayerByUser(user), - gameLobby - ) - case SelectTrump => - views.html.ingame.selecttrump( - gameLobby.getPlayerByUser(user), - gameLobby - ) - case TieBreak => - views.html.ingame.tie( - gameLobby.getPlayerByUser(user), - gameLobby - ) - case FinishedMatch => - views.html.ingame.finishedMatch( - Some(user), - gameLobby - ) - case _ => - throw new IllegalStateException(s"Invalid game state for in-game view. GameId: ${gameLobby.id}" + s" State: ${gameLobby.logic.getCurrentState}") - } - } - def startGame(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] => val game = PodManager.getGame(gameId) val result = Try { @@ -83,7 +56,7 @@ class IngameController @Inject()( Ok(Json.obj( "status" -> "success", "redirectUrl" -> routes.IngameController.game(gameId).url, - "content" -> returnInnerHTML(game.get, request.user).toString() + "content" -> IngameController.returnInnerHTML(game.get, game.get.logic.getCurrentState, request.user).toString() )) } else { val throwable = result.failed.get @@ -434,5 +407,37 @@ class IngameController @Inject()( )) } } + +} -} \ No newline at end of file +object IngameController { + + def returnInnerHTML(gameLobby: GameLobby, gameState: GameState, user: User): Html = { + gameState match { + case Lobby => views.html.lobby.lobby(Some(user), gameLobby) + case InGame => + views.html.ingame.ingame( + gameLobby.getPlayerByUser(user), + gameLobby + ) + case SelectTrump => + views.html.ingame.selecttrump( + gameLobby.getPlayerByUser(user), + gameLobby + ) + case TieBreak => + views.html.ingame.tie( + gameLobby.getPlayerByUser(user), + gameLobby + ) + case FinishedMatch => + views.html.ingame.finishedMatch( + Some(user), + gameLobby + ) + case _ => + throw new IllegalStateException(s"Invalid game state for in-game view. GameId: ${gameLobby.id}" + s" State: ${gameLobby.logic.getCurrentState}") + } + } + +} diff --git a/knockoutwhistweb/app/controllers/MainMenuController.scala b/knockoutwhistweb/app/controllers/MainMenuController.scala index 4a3eeb7..a8891ed 100644 --- a/knockoutwhistweb/app/controllers/MainMenuController.scala +++ b/knockoutwhistweb/app/controllers/MainMenuController.scala @@ -16,8 +16,7 @@ import javax.inject.* @Singleton class MainMenuController @Inject()( val controllerComponents: ControllerComponents, - val authAction: AuthAction, - val ingameController: IngameController + val authAction: AuthAction ) extends BaseController { // Pass the request-handling function directly to authAction (no nested Action) @@ -46,7 +45,7 @@ class MainMenuController @Inject()( Ok(Json.obj( "status" -> "success", "redirectUrl" -> routes.IngameController.game(gameLobby.id).url, - "content" -> ingameController.returnInnerHTML(gameLobby, request.user).toString + "content" -> IngameController.returnInnerHTML(gameLobby, gameLobby.logic.getCurrentState, request.user).toString )) } else { BadRequest(Json.obj( @@ -70,7 +69,7 @@ class MainMenuController @Inject()( Ok(Json.obj( "status" -> "success", "redirectUrl" -> routes.IngameController.game(g.id).url, - "content" -> ingameController.returnInnerHTML(g, request.user).toString + "content" -> IngameController.returnInnerHTML(g, g.logic.getCurrentState, request.user).toString )) case None => NotFound(Json.obj( diff --git a/knockoutwhistweb/app/model/sessions/UserWebsocketActor.scala b/knockoutwhistweb/app/model/sessions/UserWebsocketActor.scala index d7f80f6..b2b59c7 100644 --- a/knockoutwhistweb/app/model/sessions/UserWebsocketActor.scala +++ b/knockoutwhistweb/app/model/sessions/UserWebsocketActor.scala @@ -12,13 +12,19 @@ class UserWebsocketActor( session: UserSession ) extends Actor { - if (session.websocketActor.isDefined) { - session.websocketActor.foreach(actor => actor.transmitTextToClient("Error: Multiple websocket connections detected. Closing this connection.")) - context.stop(self) - } else { + { + session.lock.lock() + if (session.websocketActor.isDefined) { + val otherWebsocket = session.websocketActor.get + otherWebsocket.transmitTextToClient("Error: Multiple websocket connections detected. Closing your connection.") + context.stop(otherWebsocket.self) + transmitTextToClient("Previous websocket connection closed. You are now connected.") + } session.websocketActor = Some(this) + session.lock.unlock() } + override def receive: Receive = { case msg: String => val jsonObject = Try { @@ -86,12 +92,12 @@ class UserWebsocketActor( } } - def transmitJsonToClient(jsonObj: JsObject): Unit = { + def transmitJsonToClient(jsonObj: JsValue): Unit = { transmitTextToClient(jsonObj.toString()) } def transmitEventToClient(event: SimpleEvent): Unit = { - transmitJsonToClient(WebsocketEventMapper.toJson(event)) + transmitJsonToClient(WebsocketEventMapper.toJson(event, session)) } } diff --git a/knockoutwhistweb/app/util/WebsocketEventMapper.scala b/knockoutwhistweb/app/util/WebsocketEventMapper.scala index c12ecef..f668d29 100644 --- a/knockoutwhistweb/app/util/WebsocketEventMapper.scala +++ b/knockoutwhistweb/app/util/WebsocketEventMapper.scala @@ -2,10 +2,11 @@ package util import de.knockoutwhist.utils.events.SimpleEvent import logic.game.GameLobby +import model.sessions.UserSession import play.api.libs.json.{JsValue, Json} import tools.jackson.databind.json.JsonMapper import tools.jackson.module.scala.ScalaModule -import util.mapper.{ReceivedHandEventMapper, SimpleEventMapper} +import util.mapper.{GameStateEventMapper, ReceivedHandEventMapper, SimpleEventMapper} object WebsocketEventMapper { @@ -24,10 +25,11 @@ object WebsocketEventMapper { // Register all custom mappers here registerCustomMapper(ReceivedHandEventMapper) + registerCustomMapper(GameStateEventMapper) - def toJson(obj: SimpleEvent, gameLobby: GameLobby): JsValue = { + def toJson(obj: SimpleEvent, session: UserSession): JsValue = { val data: Option[JsValue] = if (customMappers.contains(obj.id)) { - Some(customMappers(obj.id).toJson(obj)) + Some(customMappers(obj.id).toJson(obj, session)) }else { None } diff --git a/knockoutwhistweb/app/util/mapper/GameStateEventMapper.scala b/knockoutwhistweb/app/util/mapper/GameStateEventMapper.scala new file mode 100644 index 0000000..3c4c0dc --- /dev/null +++ b/knockoutwhistweb/app/util/mapper/GameStateEventMapper.scala @@ -0,0 +1,18 @@ +package util.mapper + +import controllers.IngameController +import de.knockoutwhist.events.global.GameStateChangeEvent +import model.sessions.UserSession +import play.api.libs.json.{JsObject, Json} + +object GameStateEventMapper extends SimpleEventMapper[GameStateChangeEvent] { + + override def id: String = "GameStateChangeEvent" + + override def toJson(event: GameStateChangeEvent, session: UserSession): JsObject = { + Json.obj( + //Title + "content" -> IngameController.returnInnerHTML(session.gameLobby, event.newState, session.user).toString + ) + } +} diff --git a/knockoutwhistweb/app/util/mapper/ReceivedHandEventMapper.scala b/knockoutwhistweb/app/util/mapper/ReceivedHandEventMapper.scala index 830a761..0d16aac 100644 --- a/knockoutwhistweb/app/util/mapper/ReceivedHandEventMapper.scala +++ b/knockoutwhistweb/app/util/mapper/ReceivedHandEventMapper.scala @@ -1,14 +1,14 @@ package util.mapper import de.knockoutwhist.events.player.ReceivedHandEvent -import logic.game.GameLobby -import play.api.libs.json.{JsArray, JsObject, Json} +import model.sessions.UserSession +import play.api.libs.json.{JsObject, Json} import util.WebUIUtils object ReceivedHandEventMapper extends SimpleEventMapper[ReceivedHandEvent] { override def id: String = "ReceivedHandEvent" - override def toJson(event: ReceivedHandEvent, gameLobby: GameLobby): JsObject = { + override def toJson(event: ReceivedHandEvent, session: UserSession): JsObject = { Json.obj( "dog" -> event.player.isInDogLife, "hand" -> event.player.currentHand().map(hand => WebUIUtils.handToJson(hand)) diff --git a/knockoutwhistweb/app/util/mapper/SimpleEventMapper.scala b/knockoutwhistweb/app/util/mapper/SimpleEventMapper.scala index 660fcea..5fc30dd 100644 --- a/knockoutwhistweb/app/util/mapper/SimpleEventMapper.scala +++ b/knockoutwhistweb/app/util/mapper/SimpleEventMapper.scala @@ -2,11 +2,12 @@ package util.mapper import de.knockoutwhist.utils.events.SimpleEvent import logic.game.GameLobby +import model.sessions.UserSession import play.api.libs.json.JsObject trait SimpleEventMapper[T <: SimpleEvent] { def id: String - def toJson(event: T, gameLobby: GameLobby): JsObject + def toJson(event: T, session: UserSession): JsObject } diff --git a/knockoutwhistweb/app/views/ingame/ingame.scala.html b/knockoutwhistweb/app/views/ingame/ingame.scala.html index 9770a8d..c22a21b 100644 --- a/knockoutwhistweb/app/views/ingame/ingame.scala.html +++ b/knockoutwhistweb/app/views/ingame/ingame.scala.html @@ -10,8 +10,12 @@
@gamelobby.getLogic.getCurrentPlayer.get.name
- @if(!TrickUtil.isOver(gamelobby.getLogic.getCurrentMatch.get, gamelobby.getLogic.getPlayerQueue.get)) { + @if(gamelobby.getLogic.getCurrentPlayer.isDefined) { +@gamelobby.getLogic.getCurrentPlayer.get.name
+ }else { +---
+ } + @if(gamelobby.getLogic.getPlayerQueue.isDefined && gamelobby.getLogic.getCurrentMatch && !TrickUtil.isOver(gamelobby.getLogic.getCurrentMatch.get, gamelobby.getLogic.getPlayerQueue.get)) {@nextplayer
@@ -29,41 +33,56 @@@gamelobby.getLogic.getCurrentRound.get.trumpSuit
+ @if(gamelobby.getLogic.getCurrentRound.isEmpty) { +No Trumpsuit
+ }else { +@gamelobby.getLogic.getCurrentRound.get.trumpSuit
+ }