feat(ui): Websocket
Started implementing functionality to Websocket.
This commit is contained in:
@@ -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
|
||||
@@ -56,6 +56,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))
|
||||
@@ -68,6 +71,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!")
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package model.sessions
|
||||
|
||||
import de.knockoutwhist.utils.events.SimpleEvent
|
||||
import logic.game.GameLobby
|
||||
import org.apache.pekko.actor.{Actor, ActorRef}
|
||||
import play.api.libs.json.{JsObject, JsValue, Json}
|
||||
import util.WebsocketEventMapper
|
||||
@@ -27,6 +28,7 @@ class UserWebsocketActor(
|
||||
Try {
|
||||
jsonObject match {
|
||||
case Success(value) =>
|
||||
println("SUCCESS, RECEIVED SUCCESSFUL MESSAGE..." + jsonObject)
|
||||
handle(value)
|
||||
case Failure(exception) =>
|
||||
transmitTextToClient(s"Error parsing JSON: ${exception.getMessage}")
|
||||
@@ -86,12 +88,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))
|
||||
def transmitEventToClient(event: SimpleEvent, gameLobby: GameLobby): Unit = {
|
||||
transmitJsonToClient(WebsocketEventMapper.toJson(event, gameLobby))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
@@ -1,17 +1,16 @@
|
||||
package util
|
||||
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||
import com.fasterxml.jackson.module.scala.ScalaModule
|
||||
import de.knockoutwhist.utils.events.SimpleEvent
|
||||
import logic.game.GameLobby
|
||||
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.{GameStateChangeEventMapper, NewRoundEventMapper, NewTrickEventMapper, ReceivedHandEventMapper, RequestCardEventMapper, SimpleEventMapper, TrickEndEventMapper}
|
||||
|
||||
object WebsocketEventMapper {
|
||||
|
||||
private val scalaModule = ScalaModule.builder()
|
||||
.addAllBuiltinModules()
|
||||
.supportScala3Classes(true)
|
||||
//.supportScala3Classes(true)
|
||||
.build()
|
||||
|
||||
private val mapper = JsonMapper.builder().addModule(scalaModule).build()
|
||||
@@ -24,10 +23,15 @@ object WebsocketEventMapper {
|
||||
|
||||
// Register all custom mappers here
|
||||
registerCustomMapper(ReceivedHandEventMapper)
|
||||
registerCustomMapper(NewRoundEventMapper)
|
||||
registerCustomMapper(NewTrickEventMapper)
|
||||
registerCustomMapper(TrickEndEventMapper)
|
||||
registerCustomMapper(RequestCardEventMapper)
|
||||
registerCustomMapper(GameStateChangeEventMapper)
|
||||
|
||||
def toJson(obj: SimpleEvent, gameLobby: GameLobby): JsValue = {
|
||||
val data: Option[JsValue] = if (customMappers.contains(obj.id)) {
|
||||
Some(customMappers(obj.id).toJson(obj))
|
||||
Some(customMappers(obj.id).toJson(obj, gameLobby))
|
||||
}else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
16
knockoutwhistweb/app/util/mapper/NewRoundEventMapper.scala
Normal file
16
knockoutwhistweb/app/util/mapper/NewRoundEventMapper.scala
Normal 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
13
knockoutwhistweb/app/util/mapper/NewTrickEventMapper.scala
Normal file
13
knockoutwhistweb/app/util/mapper/NewTrickEventMapper.scala
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
18
knockoutwhistweb/app/util/mapper/TrickEndEventMapper.scala
Normal file
18
knockoutwhistweb/app/util/mapper/TrickEndEventMapper.scala
Normal 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"))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,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">
|
||||
@@ -69,6 +69,7 @@
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
/*
|
||||
function waitForFunction(name, checkInterval = 100) {
|
||||
return new Promise(resolve => {
|
||||
const timer = setInterval(() => {
|
||||
@@ -80,5 +81,6 @@
|
||||
});
|
||||
}
|
||||
waitForFunction("pollForUpdates").then(fn => fn('@gamelobby.id'));
|
||||
*/
|
||||
connectWebSocket()
|
||||
</script>
|
||||
@@ -23,11 +23,11 @@
|
||||
* the page content. *@
|
||||
@content
|
||||
</body>
|
||||
<script src="@routes.JavaScriptRoutingController.javascriptRoutes()" type="text/javascript"></script>
|
||||
<script src="@routes.Assets.versioned("javascripts/main.js")" type="text/javascript"></script>
|
||||
<script src="@routes.Assets.versioned("../../public/javascripts/websocket.js")" type="text/javascript"></script>
|
||||
<script src="@routes.Assets.versioned("../../public/javascripts/events.js")" type="text/javascript"></script>
|
||||
<script src="@routes.Assets.versioned("../../public/javascripts/interact.js")" type="text/javascript"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="@routes.JavaScriptRoutingController.javascriptRoutes()" type="text/javascript"></script>
|
||||
<script src="@routes.Assets.versioned("javascripts/websocket.js")" type="text/javascript"></script>
|
||||
<script src="@routes.Assets.versioned("javascripts/events.js")" type="text/javascript"></script>
|
||||
<script src="@routes.Assets.versioned("javascripts/main.js")" type="text/javascript"></script>
|
||||
<script src="@routes.Assets.versioned("javascripts/interact.js")" type="text/javascript"></script>
|
||||
</html>
|
||||
|
||||
@@ -30,4 +30,4 @@
|
||||
</main>
|
||||
<script>
|
||||
disconnectWebSocket();
|
||||
</script>
|
||||
</script>
|
||||
Reference in New Issue
Block a user