Compare commits
2 Commits
93692ca152
...
4.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14b4473f72 | ||
| 1ef5e8a72f |
@@ -154,3 +154,8 @@
|
|||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **api:** Implemented card played event via websocket ([#85](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/issues/85)) ([3c0828f](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/commit/3c0828fdbeb507706b86f1662476c46e760533e4))
|
* **api:** Implemented card played event via websocket ([#85](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/issues/85)) ([3c0828f](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/commit/3c0828fdbeb507706b86f1662476c46e760533e4))
|
||||||
|
## (2025-11-26)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **api:** Implemented session closed and kick event via websocket ([#87](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/issues/87)) ([1ef5e8a](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/commit/1ef5e8a72fdf8a3d1ae624c8c3d7c6595017bc6f))
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ lazy val knockoutwhistweb = project.in(file("knockoutwhistweb"))
|
|||||||
libraryDependencies += "de.mkammerer" % "argon2-jvm" % "2.12",
|
libraryDependencies += "de.mkammerer" % "argon2-jvm" % "2.12",
|
||||||
libraryDependencies += "com.auth0" % "java-jwt" % "4.5.0",
|
libraryDependencies += "com.auth0" % "java-jwt" % "4.5.0",
|
||||||
libraryDependencies += "com.github.ben-manes.caffeine" % "caffeine" % "3.2.3",
|
libraryDependencies += "com.github.ben-manes.caffeine" % "caffeine" % "3.2.3",
|
||||||
libraryDependencies += "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.16.1",
|
libraryDependencies += "tools.jackson.module" %% "jackson-module-scala" % "3.0.2",
|
||||||
JsEngineKeys.engineType := JsEngineKeys.EngineType.Node
|
JsEngineKeys.engineType := JsEngineKeys.EngineType.Node
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class IngameController @Inject()(
|
|||||||
val game = PodManager.getGame(gameId)
|
val game = PodManager.getGame(gameId)
|
||||||
val playerToKickUUID = UUID.fromString(playerToKick)
|
val playerToKickUUID = UUID.fromString(playerToKick)
|
||||||
val result = Try {
|
val result = Try {
|
||||||
game.get.leaveGame(playerToKickUUID)
|
game.get.leaveGame(playerToKickUUID, true)
|
||||||
}
|
}
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
Ok(Json.obj(
|
Ok(Json.obj(
|
||||||
@@ -107,7 +107,7 @@ class IngameController @Inject()(
|
|||||||
def leaveGame(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
def leaveGame(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||||
val game = PodManager.getGame(gameId)
|
val game = PodManager.getGame(gameId)
|
||||||
val result = Try {
|
val result = Try {
|
||||||
game.get.leaveGame(request.user.id)
|
game.get.leaveGame(request.user.id, false)
|
||||||
}
|
}
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
Ok(Json.obj(
|
Ok(Json.obj(
|
||||||
|
|||||||
9
knockoutwhistweb/app/events/KickEvent.scala
Normal file
9
knockoutwhistweb/app/events/KickEvent.scala
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package events
|
||||||
|
|
||||||
|
import model.users.User
|
||||||
|
|
||||||
|
case class KickEvent(user: User) extends UserEvent(user) {
|
||||||
|
|
||||||
|
override def id: String = "KickEvent"
|
||||||
|
|
||||||
|
}
|
||||||
9
knockoutwhistweb/app/events/LeftEvent.scala
Normal file
9
knockoutwhistweb/app/events/LeftEvent.scala
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package events
|
||||||
|
|
||||||
|
import model.users.User
|
||||||
|
|
||||||
|
case class LeftEvent(user: User) extends UserEvent(user) {
|
||||||
|
|
||||||
|
override def id: String = "LeftEvent"
|
||||||
|
|
||||||
|
}
|
||||||
9
knockoutwhistweb/app/events/LobbyUpdateEvent.scala
Normal file
9
knockoutwhistweb/app/events/LobbyUpdateEvent.scala
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package events
|
||||||
|
|
||||||
|
import de.knockoutwhist.utils.events.SimpleEvent
|
||||||
|
|
||||||
|
case class LobbyUpdateEvent() extends SimpleEvent {
|
||||||
|
|
||||||
|
override def id: String = "LobbyUpdateEvent"
|
||||||
|
|
||||||
|
}
|
||||||
12
knockoutwhistweb/app/events/UserEvent.scala
Normal file
12
knockoutwhistweb/app/events/UserEvent.scala
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package events
|
||||||
|
|
||||||
|
import de.knockoutwhist.utils.events.SimpleEvent
|
||||||
|
import model.users.User
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
abstract class UserEvent(user: User) extends SimpleEvent {
|
||||||
|
|
||||||
|
def userId: UUID = user.id
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ package logic.game
|
|||||||
|
|
||||||
import de.knockoutwhist.cards.{Hand, Suit}
|
import de.knockoutwhist.cards.{Hand, Suit}
|
||||||
import de.knockoutwhist.control.GameLogic
|
import de.knockoutwhist.control.GameLogic
|
||||||
import de.knockoutwhist.control.GameState.{InGame, Lobby, MainMenu}
|
import de.knockoutwhist.control.GameState.{Lobby, MainMenu}
|
||||||
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.{MatchUtil, PlayerUtil}
|
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.{MatchUtil, PlayerUtil}
|
||||||
import de.knockoutwhist.events.global.{GameStateChangeEvent, SessionClosed}
|
import de.knockoutwhist.events.global.{GameStateChangeEvent, SessionClosed}
|
||||||
import de.knockoutwhist.events.player.PlayerEvent
|
import de.knockoutwhist.events.player.PlayerEvent
|
||||||
@@ -10,13 +10,14 @@ import de.knockoutwhist.player.Playertype.HUMAN
|
|||||||
import de.knockoutwhist.player.{AbstractPlayer, PlayerFactory}
|
import de.knockoutwhist.player.{AbstractPlayer, PlayerFactory}
|
||||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||||
import de.knockoutwhist.utils.events.{EventListener, SimpleEvent}
|
import de.knockoutwhist.utils.events.{EventListener, SimpleEvent}
|
||||||
|
import events.{KickEvent, LeftEvent, LobbyUpdateEvent, UserEvent}
|
||||||
import exceptions.*
|
import exceptions.*
|
||||||
import logic.PodManager
|
import logic.PodManager
|
||||||
import model.sessions.{InteractionType, UserSession}
|
import model.sessions.{InteractionType, UserSession}
|
||||||
import model.users.User
|
import model.users.User
|
||||||
import play.api.libs.json.{JsObject, Json}
|
import play.api.libs.json.{JsObject, Json}
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.{Timer, TimerTask, UUID}
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ class GameLobby private(
|
|||||||
)
|
)
|
||||||
users += (user.id -> userSession)
|
users += (user.id -> userSession)
|
||||||
PodManager.registerUserToGame(user, id)
|
PodManager.registerUserToGame(user, id)
|
||||||
//TODO : transmit Lobby Update transmitToAll()
|
logic.invoke(LobbyUpdateEvent())
|
||||||
userSession
|
userSession
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,13 +53,12 @@ class GameLobby private(
|
|||||||
event match {
|
event match {
|
||||||
case event: PlayerEvent =>
|
case event: PlayerEvent =>
|
||||||
users.get(event.playerId).foreach(session => session.updatePlayer(event))
|
users.get(event.playerId).foreach(session => session.updatePlayer(event))
|
||||||
|
case event: UserEvent =>
|
||||||
|
users.get(event.userId).foreach(session => session.updatePlayer(event))
|
||||||
case event: GameStateChangeEvent =>
|
case event: GameStateChangeEvent =>
|
||||||
if (event.oldState == MainMenu && event.newState == Lobby) {
|
if (event.oldState == MainMenu && event.newState == Lobby) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (event.oldState == Lobby && event.newState == InGame) {
|
|
||||||
println("RECEIVED GAMESTATEEVENT")
|
|
||||||
}
|
|
||||||
users.values.foreach(session => session.updatePlayer(event))
|
users.values.foreach(session => session.updatePlayer(event))
|
||||||
case event: SimpleEvent =>
|
case event: SimpleEvent =>
|
||||||
users.values.foreach(session => session.updatePlayer(event))
|
users.values.foreach(session => session.updatePlayer(event))
|
||||||
@@ -71,7 +71,6 @@ class GameLobby private(
|
|||||||
* @param user the user who wants to start the game.
|
* @param user the user who wants to start the game.
|
||||||
*/
|
*/
|
||||||
def startGame(user: User): Unit = {
|
def startGame(user: User): Unit = {
|
||||||
println("STARTED GAME IN LOGIC")
|
|
||||||
val sessionOpt = users.get(user.id)
|
val sessionOpt = users.get(user.id)
|
||||||
if (sessionOpt.isEmpty) {
|
if (sessionOpt.isEmpty) {
|
||||||
throw new NotInThisGameException("You are not in this game!")
|
throw new NotInThisGameException("You are not in this game!")
|
||||||
@@ -97,8 +96,9 @@ class GameLobby private(
|
|||||||
* Remove the user from the game lobby.
|
* Remove the user from the game lobby.
|
||||||
*
|
*
|
||||||
* @param user the user who wants to leave the game.
|
* @param user the user who wants to leave the game.
|
||||||
|
* @param kicked whether the user was kicked or left voluntarily.
|
||||||
*/
|
*/
|
||||||
def leaveGame(userId: UUID): Unit = {
|
def leaveGame(userId: UUID, kicked: Boolean): Unit = {
|
||||||
val sessionOpt = users.get(userId)
|
val sessionOpt = users.get(userId)
|
||||||
if (sessionOpt.isEmpty) {
|
if (sessionOpt.isEmpty) {
|
||||||
throw new NotInThisGameException("You are not in this game!")
|
throw new NotInThisGameException("You are not in this game!")
|
||||||
@@ -109,16 +109,14 @@ class GameLobby private(
|
|||||||
PodManager.removeGame(id)
|
PodManager.removeGame(id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sessionOpt.get.websocketActor.foreach(act => act.transmitJsonToClient(Json.obj(
|
if (kicked) {
|
||||||
"id" -> "-1",
|
logic.invoke(KickEvent(sessionOpt.get.user))
|
||||||
"event" -> "SessionClosed",
|
} else {
|
||||||
"data" -> Json.obj(
|
logic.invoke(LeftEvent(sessionOpt.get.user))
|
||||||
"reason" -> "You left the game (or got kicked)."
|
}
|
||||||
)
|
|
||||||
)))
|
|
||||||
users.remove(userId)
|
users.remove(userId)
|
||||||
PodManager.unregisterUserFromGame(sessionOpt.get.user)
|
PodManager.unregisterUserFromGame(sessionOpt.get.user)
|
||||||
//TODO: transmit Lobby Update transmitToAll()
|
logic.invoke(LobbyUpdateEvent())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ package model.sessions
|
|||||||
|
|
||||||
import de.knockoutwhist.events.player.{RequestCardEvent, RequestTieChoiceEvent, RequestTrumpSuitEvent}
|
import de.knockoutwhist.events.player.{RequestCardEvent, RequestTieChoiceEvent, RequestTrumpSuitEvent}
|
||||||
import de.knockoutwhist.utils.events.SimpleEvent
|
import de.knockoutwhist.utils.events.SimpleEvent
|
||||||
import logic.PodManager
|
|
||||||
import logic.game.GameLobby
|
import logic.game.GameLobby
|
||||||
import model.users.User
|
import model.users.User
|
||||||
import play.api.libs.json.Format.GenericFormat
|
import play.api.libs.json.JsObject
|
||||||
import play.api.libs.json.{JsError, JsObject, JsResult, JsSuccess, JsValue}
|
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
@@ -28,7 +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(_.transmitEventToClient(event, gameLobby))
|
websocketActor.foreach(_.transmitEventToClient(event))
|
||||||
}
|
}
|
||||||
|
|
||||||
override def id: UUID = user.id
|
override def id: UUID = user.id
|
||||||
@@ -39,16 +37,13 @@ class UserSession(val user: User, val host: Boolean, val gameLobby: GameLobby) e
|
|||||||
canInteract = None
|
canInteract = None
|
||||||
}
|
}
|
||||||
|
|
||||||
def handleWebResponse(eventType: String, data: JsObject, user: User, gameLobby: GameLobby): Unit = {
|
def handleWebResponse(eventType: String, data: JsObject): Unit = {
|
||||||
lock.lock()
|
lock.lock()
|
||||||
Try {
|
Try {
|
||||||
eventType match {
|
eventType match {
|
||||||
case "Ping" =>
|
case "Ping" =>
|
||||||
// No action needed for Ping
|
// No action needed for Ping
|
||||||
()
|
()
|
||||||
case "Start Game" =>
|
|
||||||
println("INSIDE HANDLE WEB RESPONSE" + data)
|
|
||||||
gameLobby.startGame(user)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lock.unlock()
|
lock.unlock()
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class UserWebsocketActor(
|
|||||||
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())
|
||||||
val result = Try {
|
val result = Try {
|
||||||
session.handleWebResponse(event, data, session.user, session.gameLobby)
|
session.handleWebResponse(event, data)
|
||||||
}
|
}
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
transmitJsonToClient(Json.obj(
|
transmitJsonToClient(Json.obj(
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package model.users
|
package model.users
|
||||||
|
|
||||||
import play.api.libs.json.{Format, Json}
|
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
case class User(
|
case class User(
|
||||||
@@ -18,4 +16,5 @@ case class User(
|
|||||||
private def withPasswordHash(newPasswordHash: String): User = {
|
private def withPasswordHash(newPasswordHash: String): User = {
|
||||||
this.copy(passwordHash = newPasswordHash)
|
this.copy(passwordHash = newPasswordHash)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import model.sessions.UserSession
|
|||||||
import play.api.libs.json.{JsValue, Json}
|
import play.api.libs.json.{JsValue, Json}
|
||||||
import tools.jackson.databind.json.JsonMapper
|
import tools.jackson.databind.json.JsonMapper
|
||||||
import tools.jackson.module.scala.ScalaModule
|
import tools.jackson.module.scala.ScalaModule
|
||||||
import util.mapper.{CardPlayedEventMapper, GameStateEventMapper, ReceivedHandEventMapper, SimpleEventMapper}
|
import util.mapper.{CardPlayedEventMapper, GameStateEventMapper, KickEventMapper, LeftEventMapper, LobbyUpdateEventMapper, ReceivedHandEventMapper, SessionClosedMapper, SimpleEventMapper}
|
||||||
|
|
||||||
object WebsocketEventMapper {
|
object WebsocketEventMapper {
|
||||||
|
|
||||||
@@ -27,10 +27,11 @@ object WebsocketEventMapper {
|
|||||||
registerCustomMapper(ReceivedHandEventMapper)
|
registerCustomMapper(ReceivedHandEventMapper)
|
||||||
registerCustomMapper(GameStateEventMapper)
|
registerCustomMapper(GameStateEventMapper)
|
||||||
registerCustomMapper(CardPlayedEventMapper)
|
registerCustomMapper(CardPlayedEventMapper)
|
||||||
registerCustomMapper(NewRoundEventMapper)
|
registerCustomMapper(LobbyUpdateEventMapper)
|
||||||
registerCustomMapper(NewTrickEventMapper)
|
registerCustomMapper(LeftEventMapper)
|
||||||
registerCustomMapper(TrickEndEventMapper)
|
registerCustomMapper(KickEventMapper)
|
||||||
registerCustomMapper(RequestCardEventMapper)
|
registerCustomMapper(SessionClosedMapper)
|
||||||
|
|
||||||
def toJson(obj: SimpleEvent, session: UserSession): JsValue = {
|
def toJson(obj: SimpleEvent, session: UserSession): JsValue = {
|
||||||
val data: Option[JsValue] = if (customMappers.contains(obj.id)) {
|
val data: Option[JsValue] = if (customMappers.contains(obj.id)) {
|
||||||
Some(customMappers(obj.id).toJson(obj, session))
|
Some(customMappers(obj.id).toJson(obj, session))
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
19
knockoutwhistweb/app/util/mapper/KickEventMapper.scala
Normal file
19
knockoutwhistweb/app/util/mapper/KickEventMapper.scala
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package util.mapper
|
||||||
|
|
||||||
|
import controllers.routes
|
||||||
|
import events.KickEvent
|
||||||
|
import model.sessions.UserSession
|
||||||
|
import play.api.libs.json.{JsObject, Json}
|
||||||
|
|
||||||
|
object KickEventMapper extends SimpleEventMapper[KickEvent] {
|
||||||
|
|
||||||
|
override def id: String = "KickEvent"
|
||||||
|
|
||||||
|
override def toJson(event: KickEvent, session: UserSession): JsObject = {
|
||||||
|
Json.obj(
|
||||||
|
"url" -> routes.MainMenuController.mainMenu().url,
|
||||||
|
"content" -> views.html.mainmenu.creategame(Some(session.user)).toString,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
knockoutwhistweb/app/util/mapper/LeftEventMapper.scala
Normal file
19
knockoutwhistweb/app/util/mapper/LeftEventMapper.scala
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package util.mapper
|
||||||
|
|
||||||
|
import controllers.routes
|
||||||
|
import events.{KickEvent, LeftEvent}
|
||||||
|
import model.sessions.UserSession
|
||||||
|
import play.api.libs.json.{JsObject, Json}
|
||||||
|
|
||||||
|
object LeftEventMapper extends SimpleEventMapper[LeftEvent] {
|
||||||
|
|
||||||
|
override def id: String = "LeftEvent"
|
||||||
|
|
||||||
|
override def toJson(event: LeftEvent, session: UserSession): JsObject = {
|
||||||
|
Json.obj(
|
||||||
|
"url" -> routes.MainMenuController.mainMenu().url,
|
||||||
|
"content" -> views.html.mainmenu.creategame(Some(session.user)).toString
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package util.mapper
|
||||||
|
|
||||||
|
import events.LobbyUpdateEvent
|
||||||
|
import model.sessions.UserSession
|
||||||
|
import play.api.libs.json.{JsArray, JsObject, Json}
|
||||||
|
|
||||||
|
object LobbyUpdateEventMapper extends SimpleEventMapper[LobbyUpdateEvent] {
|
||||||
|
|
||||||
|
override def id: String = "LobbyUpdateEvent"
|
||||||
|
|
||||||
|
override def toJson(event: LobbyUpdateEvent, session: UserSession): JsObject = {
|
||||||
|
Json.obj(
|
||||||
|
"host" -> session.host,
|
||||||
|
"maxPlayers" -> session.gameLobby.maxPlayers,
|
||||||
|
"players" -> JsArray(session.gameLobby.getPlayers.values.map(player => {
|
||||||
|
Json.obj(
|
||||||
|
"id" -> player.id,
|
||||||
|
"name" -> player.name,
|
||||||
|
"self" -> (player.id == session.user.id)
|
||||||
|
)
|
||||||
|
}).toList)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
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)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
19
knockoutwhistweb/app/util/mapper/SessionClosedMapper.scala
Normal file
19
knockoutwhistweb/app/util/mapper/SessionClosedMapper.scala
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package util.mapper
|
||||||
|
|
||||||
|
import controllers.routes
|
||||||
|
import de.knockoutwhist.events.global.SessionClosed
|
||||||
|
import model.sessions.UserSession
|
||||||
|
import play.api.libs.json.{JsObject, Json}
|
||||||
|
|
||||||
|
object SessionClosedMapper extends SimpleEventMapper[SessionClosed] {
|
||||||
|
|
||||||
|
override def id: String = "SessionClosed"
|
||||||
|
|
||||||
|
override def toJson(event: SessionClosed, session: UserSession): JsObject = {
|
||||||
|
Json.obj(
|
||||||
|
"url" -> routes.MainMenuController.mainMenu().url,
|
||||||
|
"content" -> views.html.mainmenu.creategame(Some(session.user)).toString,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,35 @@
|
|||||||
@(user: Option[model.users.User], gamelobby: logic.game.GameLobby)
|
@(user: Option[model.users.User], gamelobby: logic.game.GameLobby)
|
||||||
|
|
||||||
<main class="lobby-background vh-100" id="lobbybackground">
|
<main class="lobby-background vh-100" id="lobbybackground">
|
||||||
|
<!-- Kick Modal -->
|
||||||
|
<div class="modal fade" data-backdrop="static" data-keyboard="false" data-focus="true" id="kickedModal" tabindex="-1" role="dialog" aria-labelledby="kickedModalTitle">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="kickedModalTitle">Kicked</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>You've been kicked from the lobby.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Session Closed Modal -->
|
||||||
|
<div class="modal fade" data-backdrop="static" data-keyboard="false" data-focus="true" id="sessionClosed" tabindex="-1" role="dialog" aria-labelledby="sessionClosedModalTitle">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="sessionClosedModalTitle">Session Closed</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>The session was closed.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Lobby -->
|
||||||
<div class="container d-flex flex-column" style="height: calc(100vh - 1rem);">
|
<div class="container d-flex flex-column" style="height: calc(100vh - 1rem);">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
@@ -15,7 +44,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="p-3 text-center fs-4" id="playerAmount">
|
<div class="p-3 text-center fs-4" id="playerAmount">
|
||||||
Playeramount: @gamelobby.getPlayers.size / @gamelobby.maxPlayers</div>
|
Players: @gamelobby.getPlayers.size / @gamelobby.maxPlayers</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-center align-items-center flex-grow-1">
|
<div class="row justify-content-center align-items-center flex-grow-1">
|
||||||
@@ -40,7 +69,7 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 text-center mb-5">
|
<div class="col-12 text-center mb-5">
|
||||||
<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 class="btn btn-success" onclick="startGame('@gamelobby.id')">Start Game</div>
|
||||||
</div>
|
</div>
|
||||||
} else {
|
} else {
|
||||||
<div id="players" class="justify-content-center align-items-center d-flex">
|
<div id="players" class="justify-content-center align-items-center d-flex">
|
||||||
@@ -69,7 +98,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<script>
|
<script>
|
||||||
/*
|
|
||||||
function waitForFunction(name, checkInterval = 100) {
|
function waitForFunction(name, checkInterval = 100) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
@@ -81,6 +109,5 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
waitForFunction("pollForUpdates").then(fn => fn('@gamelobby.id'));
|
waitForFunction("pollForUpdates").then(fn => fn('@gamelobby.id'));
|
||||||
*/
|
|
||||||
connectWebSocket()
|
connectWebSocket()
|
||||||
</script>
|
</script>
|
||||||
@@ -30,4 +30,4 @@
|
|||||||
</main>
|
</main>
|
||||||
<script>
|
<script>
|
||||||
disconnectWebSocket();
|
disconnectWebSocket();
|
||||||
</script>
|
</script>
|
||||||
@@ -1,30 +1,3 @@
|
|||||||
function alertMessage(message) {
|
|
||||||
let newHtml = '';
|
|
||||||
const alertId = `alert-${Date.now()}`;
|
|
||||||
const fadeTime = 500;
|
|
||||||
const duration = 5000;
|
|
||||||
newHtml += `
|
|
||||||
<div class="fixed-top d-flex justify-content-center mt-3" style="z-index: 1050;">
|
|
||||||
<div
|
|
||||||
id="${alertId}" class="alert alert-primary d-flex align-items-center p-2 mb-0 w-auto" role="alert" style="display: none;">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="bi flex-shrink-0 me-2" viewBox="0 0 16 16" role="img" aria-label="Warning:">
|
|
||||||
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/>
|
|
||||||
</svg>
|
|
||||||
<div class="small">
|
|
||||||
<small>${message}</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
$('#main-body').prepend(newHtml);
|
|
||||||
const $notice = $(`#${alertId}`);
|
|
||||||
$notice.fadeIn(fadeTime);
|
|
||||||
setTimeout(function() {
|
|
||||||
$notice.fadeOut(fadeTime, function() {
|
|
||||||
$(this).parent().remove();
|
|
||||||
});
|
|
||||||
}, duration);
|
|
||||||
}
|
|
||||||
function receiveHandEvent(eventData) {
|
function receiveHandEvent(eventData) {
|
||||||
//Data
|
//Data
|
||||||
const dog = eventData.dog;
|
const dog = eventData.dog;
|
||||||
@@ -65,107 +38,13 @@ function receiveHandEvent(eventData) {
|
|||||||
}
|
}
|
||||||
handElement.html(newHtml);
|
handElement.html(newHtml);
|
||||||
}
|
}
|
||||||
function newRoundEvent(eventData) {
|
|
||||||
const trumpsuit = eventData.trumpsuit;
|
|
||||||
const players = eventData.players;
|
|
||||||
|
|
||||||
const tableElement = $('#score-table-body');
|
|
||||||
|
|
||||||
|
|
||||||
let tablehtml = `
|
|
||||||
<h4 class="fw-bold mb-3 text-black">Tricks Won</h4>
|
|
||||||
|
|
||||||
<div class="d-flex justify-content-between score-header pb-1">
|
|
||||||
<div style="width: 50%">PLAYER</div>
|
|
||||||
<div style="width: 50%">TRICKS</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
players.forEach(
|
|
||||||
tablehtml += `
|
|
||||||
<div class="d-flex justify-content-between score-row pt-1">
|
|
||||||
<div style="width: 50%" class="text-truncate">'${players}'</div>
|
|
||||||
<div style="width: 50%">
|
|
||||||
0
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
);
|
|
||||||
tableElement.html(tablehtml);
|
|
||||||
|
|
||||||
const trumpsuitClass = $('#trumpsuit');
|
|
||||||
trumpsuitClass.html(trumpsuit);
|
|
||||||
|
|
||||||
}
|
|
||||||
function trickEndEvent(eventData) {
|
|
||||||
const winner = eventData.playerwon;
|
|
||||||
const players = eventData.playersin;
|
|
||||||
const tricklist = eventData.tricklist;
|
|
||||||
|
|
||||||
let newHtml = '';
|
|
||||||
|
|
||||||
let tricktable = $('#score-table-body');
|
|
||||||
|
|
||||||
newHtml += `
|
|
||||||
<h4 class="fw-bold mb-3 text-black">Tricks Won</h4>
|
|
||||||
|
|
||||||
<div class="d-flex justify-content-between score-header pb-1">
|
|
||||||
<div style="width: 50%">PLAYER</div>
|
|
||||||
<div style="width: 50%">TRICKS</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
let playercounts = new Map();
|
|
||||||
|
|
||||||
players.forEach( player => {
|
|
||||||
playercounts.set(player, 0)
|
|
||||||
});
|
|
||||||
tricklist.forEach( player => {
|
|
||||||
if ( player !== "Trick in Progress" && playercounts.has(player)) {
|
|
||||||
playercounts.set(player, playercounts.get(player) + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const playerorder = players.sort((playerA, playerB) => {
|
|
||||||
const countA = playercounts.get(playerA.name) || 0;
|
|
||||||
const countB = playercounts.get(playerB.name) || 0;
|
|
||||||
return countB - countA;
|
|
||||||
});
|
|
||||||
playerorder.forEach( player => {
|
|
||||||
newHtml += `
|
|
||||||
<div class="d-flex justify-content-between score-row pt-1">
|
|
||||||
<div style="width: 50%" class="text-truncate">'${player}'</div>
|
|
||||||
<div style="width: 50%">
|
|
||||||
'${playercounts.get(player)}'
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
});
|
|
||||||
tricktable.html(newHtml);
|
|
||||||
}
|
|
||||||
function newTrickEvent() {
|
|
||||||
const firstCardContainer = $('first-card-container');
|
|
||||||
|
|
||||||
let newHtml = '';
|
|
||||||
|
|
||||||
newHtml += `
|
|
||||||
<img src="images/cards/1B.png" alt="Blank Card" width="80px"/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
firstCardContainer.html(newHtml);
|
|
||||||
}
|
|
||||||
function requestCardEvent(eventData) {
|
|
||||||
const player = eventData.player;
|
|
||||||
const handElement = $('#card-slide')
|
|
||||||
handElement.removeClass('inactive');
|
|
||||||
}
|
|
||||||
//alertMessage("It worked!")
|
|
||||||
|
|
||||||
|
|
||||||
function receiveGameStateChange(eventData) {
|
function receiveGameStateChange(eventData) {
|
||||||
const content = eventData.content;
|
const content = eventData.content;
|
||||||
const title = eventData.title || 'Knockout Whist';
|
const title = eventData.title || 'Knockout Whist';
|
||||||
|
const url = eventData.url || null;
|
||||||
|
|
||||||
exchangeBody(content, title);
|
exchangeBody(content, title, url);
|
||||||
}
|
}
|
||||||
function receiveCardPlayedEvent(eventData) {
|
function receiveCardPlayedEvent(eventData) {
|
||||||
const firstCard = eventData.firstCard;
|
const firstCard = eventData.firstCard;
|
||||||
@@ -206,10 +85,79 @@ function receiveCardPlayedEvent(eventData) {
|
|||||||
`;
|
`;
|
||||||
firstCardContainer.html(newFirstCardHTML);
|
firstCardContainer.html(newFirstCardHTML);
|
||||||
}
|
}
|
||||||
|
function receiveLobbyUpdateEvent(eventData) {
|
||||||
|
const host = eventData.host;
|
||||||
|
const maxPlayers = eventData.maxPlayers;
|
||||||
|
const players = eventData.players;
|
||||||
|
|
||||||
|
const lobbyPlayersContainer = $('#players');
|
||||||
|
const playerAmountBox = $('#playerAmount');
|
||||||
|
|
||||||
|
let newHtml = ''
|
||||||
|
|
||||||
|
if (host) {
|
||||||
|
players.forEach(user => {
|
||||||
|
|
||||||
|
const inner = user.self ? `<h5 class="card-title">${user.name} (You)</h5>
|
||||||
|
<a href="#" class="btn btn-danger disabled" aria-disabled="true" tabindex="-1">Remove</a>`
|
||||||
|
: ` <h5 class="card-title">${user.name}</h5>
|
||||||
|
<div class="btn btn-danger" onclick="removePlayer('${user.id}')">Remove</div>`
|
||||||
|
|
||||||
|
newHtml += `<div class="col-auto my-auto m-3">
|
||||||
|
<div class="card" style="width: 18rem;">
|
||||||
|
<img src="/assets/images/profile.png" alt="Profile" class="card-img-top w-50 mx-auto mt-3" />
|
||||||
|
<div class="card-body">
|
||||||
|
${inner}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
players.forEach(user => {
|
||||||
|
|
||||||
|
const inner = user.self ? `<h5 class="card-title">${user.name} (You)</h5>` : ` <h5 class="card-title">${user.name}</h5>`
|
||||||
|
|
||||||
|
newHtml += `<div class="col-auto my-auto m-3">
|
||||||
|
<div class="card" style="width: 18rem;">
|
||||||
|
<img src="/assets/images/profile.png" alt="Profile" class="card-img-top w-50 mx-auto mt-3" />
|
||||||
|
<div class="card-body">
|
||||||
|
${inner}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
lobbyPlayersContainer.html(newHtml);
|
||||||
|
playerAmountBox.text(`Players: ${players.length} / ${maxPlayers}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
function receiveKickEvent(eventData) {
|
||||||
|
$('#kickedModal').modal({
|
||||||
|
backdrop: 'static',
|
||||||
|
keyboard: false
|
||||||
|
}).modal('show');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
receiveGameStateChange(eventData)
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
function receiveSessionClosedEvent(eventData) {
|
||||||
|
$('#sessionClosed').modal({
|
||||||
|
backdrop: 'static',
|
||||||
|
keyboard: false
|
||||||
|
}).modal('show');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
receiveGameStateChange(eventData)
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
onEvent("ReceivedHandEvent", receiveHandEvent)
|
onEvent("ReceivedHandEvent", receiveHandEvent)
|
||||||
onEvent("GameStateChangeEvent", receiveGameStateChange)
|
onEvent("GameStateChangeEvent", receiveGameStateChange)
|
||||||
onEvent("NewRoundEvent", newRoundEvent)
|
onEvent("CardPlayedEvent", receiveCardPlayedEvent)
|
||||||
onEvent("TrickEndEvent", trickEndEvent)
|
onEvent("LobbyUpdateEvent", receiveLobbyUpdateEvent)
|
||||||
onEvent("NewTrickEvent", newTrickEvent)
|
onEvent("LeftEvent", receiveGameStateChange)
|
||||||
onEvent("RequestCardEvent", requestCardEvent)
|
onEvent("KickEvent", receiveKickEvent)
|
||||||
onEvent("CardPlayedEvent", receiveCardPlayedEvent)
|
onEvent("SessionClosed", receiveSessionClosedEvent)
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
function handlePlayCard(card, dog) {
|
function handlePlayCard(card, dog) {
|
||||||
// TODO needs implementation
|
// TODO needs implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSkipDogLife(button) {
|
function handleSkipDogLife(button) {
|
||||||
// TODO needs implementation
|
// TODO needs implementation
|
||||||
|
}
|
||||||
|
function handleKickPlayer(playerId) {
|
||||||
|
// TODO needs implementation
|
||||||
}
|
}
|
||||||
function startGame() {
|
|
||||||
console.log("CLICKED START GAME, SENDING EVENT...")
|
|
||||||
sendEvent("Start Game")
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
MAJOR=4
|
MAJOR=4
|
||||||
MINOR=2
|
MINOR=3
|
||||||
PATCH=0
|
PATCH=0
|
||||||
|
|||||||
Reference in New Issue
Block a user