feat(ui): Websocket

Started implementing functionality to Websocket.
This commit is contained in:
LQ63
2025-11-26 01:35:46 +01:00
committed by Janis
parent 2aee79bb68
commit e0f16a224d
14 changed files with 258 additions and 8 deletions

View File

@@ -2,7 +2,7 @@ package logic.game
import de.knockoutwhist.cards.{Hand, Suit}
import de.knockoutwhist.control.GameLogic
import de.knockoutwhist.control.GameState.{Lobby, MainMenu}
import de.knockoutwhist.control.GameState.{InGame, Lobby, MainMenu}
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.{MatchUtil, PlayerUtil}
import de.knockoutwhist.events.global.{GameStateChangeEvent, SessionClosed}
import de.knockoutwhist.events.player.PlayerEvent
@@ -59,6 +59,9 @@ class GameLobby private(
if (event.oldState == MainMenu && event.newState == Lobby) {
return
}
if (event.oldState == Lobby && event.newState == InGame) {
println("RECEIVED GAMESTATEEVENT")
}
users.values.foreach(session => session.updatePlayer(event))
case event: SimpleEvent =>
users.values.foreach(session => session.updatePlayer(event))
@@ -71,6 +74,7 @@ class GameLobby private(
* @param user the user who wants to start the game.
*/
def startGame(user: User): Unit = {
println("STARTED GAME IN LOGIC")
val sessionOpt = users.get(user.id)
if (sessionOpt.isEmpty) {
throw new NotInThisGameException("You are not in this game!")

View File

@@ -2,9 +2,11 @@ package model.sessions
import de.knockoutwhist.events.player.{RequestCardEvent, RequestTieChoiceEvent, RequestTrumpSuitEvent}
import de.knockoutwhist.utils.events.SimpleEvent
import logic.PodManager
import logic.game.GameLobby
import model.users.User
import play.api.libs.json.JsObject
import play.api.libs.json.Format.GenericFormat
import play.api.libs.json.{JsError, JsObject, JsResult, JsSuccess, JsValue}
import java.util.UUID
import java.util.concurrent.locks.ReentrantLock
@@ -26,7 +28,7 @@ class UserSession(val user: User, val host: Boolean, val gameLobby: GameLobby) e
else canInteract = Some(InteractionType.Card)
case _ =>
}
websocketActor.foreach(_.transmitEventToClient(event))
websocketActor.foreach(_.transmitEventToClient(event, gameLobby))
}
override def id: UUID = user.id
@@ -44,6 +46,21 @@ class UserSession(val user: User, val host: Boolean, val gameLobby: GameLobby) e
case "Ping" =>
// No action needed for Ping
()
case "Start Game" =>
println("INSIDE HANDLE WEB RESPONSE" + data)
val gameId: String = (data \ "gameId").get.toString
val cleanGameId: String = gameId.replaceAll("^[\"']|[\"']$", "")
val user: JsObject = (data \ "user").asOpt[JsObject].get
val gameLobby: GameLobby = PodManager.getGame(cleanGameId).get
val realUser: JsResult[User] = user.validate[User]
val uu: User = realUser match {
case JsSuccess(extractedUser, _) =>
extractedUser
case e: JsError =>
println("FAILED" + JsError.toJson(e).toString())
throw new Exception("Failed to deserialize User object: " + JsError.toJson(e).toString())
}
gameLobby.startGame(uu)
}
}
lock.unlock()

View File

@@ -1,5 +1,7 @@
package model.users
import play.api.libs.json.{Format, Json}
import java.util.UUID
case class User(
@@ -16,5 +18,7 @@ case class User(
private def withPasswordHash(newPasswordHash: String): User = {
this.copy(passwordHash = newPasswordHash)
}
}
object User {
implicit val userFormat: Format[User] = Json.format[User]
}

View File

@@ -27,6 +27,10 @@ object WebsocketEventMapper {
registerCustomMapper(ReceivedHandEventMapper)
registerCustomMapper(GameStateEventMapper)
registerCustomMapper(CardPlayedEventMapper)
registerCustomMapper(NewRoundEventMapper)
registerCustomMapper(NewTrickEventMapper)
registerCustomMapper(TrickEndEventMapper)
registerCustomMapper(RequestCardEventMapper)
registerCustomMapper(LobbyUpdateEventMapper)
registerCustomMapper(LeftEventMapper)
registerCustomMapper(KickEventMapper)

View File

@@ -0,0 +1,18 @@
package util.mapper
import de.knockoutwhist.events.global.GameStateChangeEvent
import logic.game.GameLobby
import play.api.libs.json.{JsObject, Json}
object GameStateChangeEventMapper extends SimpleEventMapper[GameStateChangeEvent]{
override def id: String = "GameStateChangeEvent"
override def toJson(event: GameStateChangeEvent, gameLobby: GameLobby): JsObject = {
println("CALLED toJSON FOR GAMESTATECHANGE")
Json.obj(
"oldState" -> event.oldState.toString,
"newState" -> event.newState.toString,
"gameLobby" -> gameLobby.id
)
}
}

View File

@@ -0,0 +1,16 @@
package util.mapper
import de.knockoutwhist.events.global.NewRoundEvent
import logic.game.GameLobby
import play.api.libs.json.{JsObject, Json}
object NewRoundEventMapper extends SimpleEventMapper[NewRoundEvent]{
override def id: String = "NewRoundEvent"
override def toJson(event: NewRoundEvent, gameLobby: GameLobby): JsObject = {
Json.obj(
"trumpsuit" -> gameLobby.getLogic.getCurrentRound.get.trumpSuit.toString,
"players" -> gameLobby.getLogic.getCurrentMatch.get.playersIn.map(player => player.toString)
)
}
}

View File

@@ -0,0 +1,13 @@
package util.mapper
import de.knockoutwhist.events.global.NewTrickEvent
import logic.game.GameLobby
import play.api.libs.json.{JsObject, Json}
object NewTrickEventMapper extends SimpleEventMapper[NewTrickEvent]{
override def id: String = "NewTrickEvent"
override def toJson(event: NewTrickEvent, gameLobby: GameLobby): JsObject = {
Json.obj()
}
}

View File

@@ -0,0 +1,15 @@
package util.mapper
import de.knockoutwhist.events.player.RequestCardEvent
import logic.game.GameLobby
import play.api.libs.json.{JsObject, Json}
object RequestCardEventMapper extends SimpleEventMapper[RequestCardEvent]{
override def id: String = "RequestCardEvent"
override def toJson(event: RequestCardEvent, gameLobby: GameLobby): JsObject = {
Json.obj(
"player" -> event.player.name
)
}
}

View File

@@ -0,0 +1,18 @@
package util.mapper
import de.knockoutwhist.events.global.TrickEndEvent
import de.knockoutwhist.rounds.Trick
import logic.game.GameLobby
import play.api.libs.json.{JsObject, Json}
object TrickEndEventMapper extends SimpleEventMapper[TrickEndEvent]{
override def id: String = "TrickEndEvent"
override def toJson(event: TrickEndEvent, gameLobby: GameLobby): JsObject = {
Json.obj(
"playerwon" -> event.winner.name,
"playersin" -> gameLobby.getLogic.getCurrentMatch.get.playersIn.map(player => player.name),
"tricklist" -> gameLobby.getLogic.getCurrentRound.get.tricklist.map(trick => trick.winner.map(player => player.name).getOrElse("Trick in Progress"))
)
}
}

View File

@@ -69,7 +69,7 @@
}
</div>
<div class="col-12 text-center mb-5">
<div class="btn btn-success" onclick="startGame('@gamelobby.id')">Start Game</div>
<div class="btn btn-success" onclick="startGame('@gamelobby.id', '@user.get.id', '@user.get.name','@user.get.passwordHash', @user.get.internalId)">Start Game</div>
</div>
} else {
<div id="players" class="justify-content-center align-items-center d-flex">
@@ -98,6 +98,7 @@
</div>
</main>
<script>
/*
function waitForFunction(name, checkInterval = 100) {
return new Promise(resolve => {
const timer = setInterval(() => {
@@ -109,5 +110,6 @@
});
}
waitForFunction("pollForUpdates").then(fn => fn('@gamelobby.id'));
*/
connectWebSocket()
</script>

View File

@@ -30,4 +30,4 @@
</main>
<script>
disconnectWebSocket();
</script>
</script>