feat!: implemented multigame support (#34)
Reviewed-on: #34 Co-authored-by: Janis <janis.e.20@gmx.de> Co-committed-by: Janis <janis.e.20@gmx.de>
This commit is contained in:
@@ -1,93 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import com.google.inject.{Guice, Injector}
|
||||
import controllers.sessions.AdvancedSession
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.components.Configuration
|
||||
import de.knockoutwhist.control.GameState.{InGame, Lobby, SelectTrump, TieBreak}
|
||||
import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic
|
||||
import di.KnockOutWebConfigurationModule
|
||||
import play.api.mvc.*
|
||||
import play.api.*
|
||||
import play.twirl.api.Html
|
||||
|
||||
import java.util.UUID
|
||||
import javax.inject.*
|
||||
|
||||
|
||||
/**
|
||||
* This controller creates an `Action` to handle HTTP requests to the
|
||||
* application's home page.
|
||||
*/
|
||||
@Singleton
|
||||
class HomeController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {
|
||||
|
||||
private var initial = false
|
||||
private val injector: Injector = Guice.createInjector(KnockOutWebConfigurationModule())
|
||||
|
||||
/**
|
||||
* Create an Action to render an HTML page.
|
||||
*
|
||||
* The configuration in the `routes` file means that this method
|
||||
* will be called when the application receives a `GET` request with
|
||||
* a path of `/`.
|
||||
*/
|
||||
def index(): Action[AnyContent] = {
|
||||
if (!initial) {
|
||||
initial = true
|
||||
KnockOutWhist.entry(injector.getInstance(classOf[Configuration]))
|
||||
}
|
||||
Action { implicit request =>
|
||||
Redirect("/sessions")
|
||||
}
|
||||
}
|
||||
def rules(): Action[AnyContent] = {
|
||||
Action { implicit request =>
|
||||
Ok(views.html.rules.apply())
|
||||
}
|
||||
}
|
||||
def sessions(): Action[AnyContent] = {
|
||||
Action { implicit request =>
|
||||
Ok(views.html.sessions.apply(PodGameManager.listSessions()))
|
||||
}
|
||||
}
|
||||
|
||||
def ingame(id: String): Action[AnyContent] = {
|
||||
val uuid: UUID = UUID.fromString(id)
|
||||
if (PodGameManager.identify(uuid).isEmpty) {
|
||||
return Action { implicit request =>
|
||||
NotFound(views.html.tui.apply(List(Html(s"<p>Session with id $id not found!</p>"))))
|
||||
}
|
||||
} else {
|
||||
val session = PodGameManager.identify(uuid).get
|
||||
val player = session.asInstanceOf[AdvancedSession].player
|
||||
val logic = WebUI.logic.get.asInstanceOf[BaseGameLogic]
|
||||
if (logic.getCurrentState == Lobby) {
|
||||
|
||||
} else if (logic.getCurrentState == InGame) {
|
||||
return Action { implicit request =>
|
||||
Ok(views.html.ingame.apply(player, logic))
|
||||
}
|
||||
} else if (logic.getCurrentState == SelectTrump) {
|
||||
return Action { implicit request =>
|
||||
Ok(views.html.selecttrump.apply(player, logic))
|
||||
}
|
||||
} else if (logic.getCurrentState == TieBreak) {
|
||||
return Action { implicit request =>
|
||||
Ok(views.html.tie.apply(player, logic))
|
||||
}
|
||||
}
|
||||
}
|
||||
Action { implicit request =>
|
||||
InternalServerError("Oops")
|
||||
}
|
||||
//if (logic.getCurrentState == Lobby) {
|
||||
//Action { implicit request =>
|
||||
//Ok(views.html.tui.apply(player, logic))
|
||||
//}
|
||||
//} else {
|
||||
//Action { implicit request =>
|
||||
//Ok(views.html.tui.apply(player, logic))
|
||||
//}
|
||||
}
|
||||
}
|
||||
244
knockoutwhistweb/app/controllers/IngameController.scala
Normal file
244
knockoutwhistweb/app/controllers/IngameController.scala
Normal file
@@ -0,0 +1,244 @@
|
||||
package controllers
|
||||
|
||||
import auth.{AuthAction, AuthenticatedRequest}
|
||||
import de.knockoutwhist.control.GameState.{InGame, Lobby, SelectTrump, TieBreak}
|
||||
import exceptions.{CantPlayCardException, GameFullException, NotEnoughPlayersException, NotHostException, NotInThisGameException}
|
||||
import logic.PodManager
|
||||
import play.api.*
|
||||
import play.api.mvc.*
|
||||
|
||||
import javax.inject.*
|
||||
import scala.util.Try
|
||||
|
||||
|
||||
/**
|
||||
* This controller creates an `Action` to handle HTTP requests to the
|
||||
* application's home page.
|
||||
*/
|
||||
@Singleton
|
||||
class IngameController @Inject()(
|
||||
val controllerComponents: ControllerComponents,
|
||||
val authAction: AuthAction,
|
||||
val podManager: PodManager
|
||||
) extends BaseController {
|
||||
|
||||
def game(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val game = podManager.getGame(gameId)
|
||||
game match {
|
||||
case Some(g) =>
|
||||
g.logic.getCurrentState match {
|
||||
case Lobby => Ok("Lobby: " + gameId)
|
||||
case InGame =>
|
||||
Ok(views.html.ingame.ingame(
|
||||
g.getPlayerByUser(request.user),
|
||||
g.logic
|
||||
))
|
||||
case SelectTrump =>
|
||||
Ok(views.html.ingame.selecttrump(
|
||||
g.getPlayerByUser(request.user),
|
||||
g.logic
|
||||
))
|
||||
case TieBreak =>
|
||||
Ok(views.html.ingame.tie(
|
||||
g.getPlayerByUser(request.user),
|
||||
g.logic
|
||||
))
|
||||
case _ =>
|
||||
InternalServerError(s"Invalid game state for in-game view. GameId: $gameId" + s" State: ${g.logic.getCurrentState}")
|
||||
}
|
||||
case None =>
|
||||
NotFound("Game not found")
|
||||
}
|
||||
//NotFound(s"Reached end of game method unexpectedly. GameId: $gameId")
|
||||
}
|
||||
def startGame(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val game = podManager.getGame(gameId)
|
||||
val result = Try {
|
||||
game match {
|
||||
case Some(g) =>
|
||||
g.startGame(request.user)
|
||||
case None =>
|
||||
NotFound("Game not found")
|
||||
}
|
||||
}
|
||||
if (result.isSuccess) {
|
||||
NoContent
|
||||
} else {
|
||||
val throwable = result.failed.get
|
||||
throwable match {
|
||||
case _: NotInThisGameException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: NotHostException =>
|
||||
Forbidden(throwable.getMessage)
|
||||
case _: NotEnoughPlayersException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _ =>
|
||||
InternalServerError(throwable.getMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
def joinGame(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val game = podManager.getGame(gameId)
|
||||
val result = Try {
|
||||
game match {
|
||||
case Some(g) =>
|
||||
g.addUser(request.user)
|
||||
case None =>
|
||||
NotFound("Game not found")
|
||||
}
|
||||
}
|
||||
if (result.isSuccess) {
|
||||
Redirect(routes.IngameController.game(gameId))
|
||||
} else {
|
||||
val throwable = result.failed.get
|
||||
throwable match {
|
||||
case _: GameFullException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: IllegalArgumentException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: IllegalStateException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _ =>
|
||||
InternalServerError(throwable.getMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
def playCard(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] => {
|
||||
val game = podManager.getGame(gameId)
|
||||
game match {
|
||||
case Some(g) =>
|
||||
val cardIdOpt = request.body.asFormUrlEncoded.flatMap(_.get("cardId").flatMap(_.headOption))
|
||||
cardIdOpt match {
|
||||
case Some(cardId) =>
|
||||
val result = Try {
|
||||
g.playCard(g.getUserSession(request.user.id), cardId.toInt)
|
||||
}
|
||||
if (result.isSuccess) {
|
||||
NoContent
|
||||
} else {
|
||||
val throwable = result.failed.get
|
||||
throwable match {
|
||||
case _: CantPlayCardException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: NotInThisGameException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: IllegalArgumentException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: IllegalStateException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _ =>
|
||||
InternalServerError(throwable.getMessage)
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
BadRequest("cardId parameter is missing")
|
||||
}
|
||||
case None =>
|
||||
NotFound("Game not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
def playDogCard(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] => {
|
||||
val game = podManager.getGame(gameId)
|
||||
game match {
|
||||
case Some(g) => {
|
||||
val cardIdOpt = request.body.asFormUrlEncoded.flatMap(_.get("cardId").flatMap(_.headOption))
|
||||
val result = Try {
|
||||
cardIdOpt match {
|
||||
case Some(cardId) if cardId == "skip" =>
|
||||
g.playDogCard(g.getUserSession(request.user.id), -1)
|
||||
case Some(cardId) =>
|
||||
g.playDogCard(g.getUserSession(request.user.id), cardId.toInt)
|
||||
case None =>
|
||||
throw new IllegalArgumentException("cardId parameter is missing")
|
||||
}
|
||||
}
|
||||
if (result.isSuccess) {
|
||||
NoContent
|
||||
} else {
|
||||
val throwable = result.failed.get
|
||||
throwable match {
|
||||
case _: CantPlayCardException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: NotInThisGameException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: IllegalArgumentException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: IllegalStateException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _ =>
|
||||
InternalServerError(throwable.getMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
NotFound("Game not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
def playTrump(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val game = podManager.getGame(gameId)
|
||||
game match {
|
||||
case Some(g) =>
|
||||
val trumpOpt = request.body.asFormUrlEncoded.flatMap(_.get("trump").flatMap(_.headOption))
|
||||
trumpOpt match {
|
||||
case Some(trump) =>
|
||||
val result = Try {
|
||||
g.selectTrump(g.getUserSession(request.user.id), trump.toInt)
|
||||
}
|
||||
if (result.isSuccess) {
|
||||
NoContent
|
||||
} else {
|
||||
val throwable = result.failed.get
|
||||
throwable match {
|
||||
case _: IllegalArgumentException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: NotInThisGameException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: IllegalStateException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _ =>
|
||||
InternalServerError(throwable.getMessage)
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
BadRequest("trump parameter is missing")
|
||||
}
|
||||
case None =>
|
||||
NotFound("Game not found")
|
||||
}
|
||||
}
|
||||
def playTie(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val game = podManager.getGame(gameId)
|
||||
game match {
|
||||
case Some(g) =>
|
||||
val tieOpt = request.body.asFormUrlEncoded.flatMap(_.get("tie").flatMap(_.headOption))
|
||||
tieOpt match {
|
||||
case Some(tie) =>
|
||||
val result = Try {
|
||||
g.selectTie(g.getUserSession(request.user.id), tie.toInt)
|
||||
}
|
||||
if (result.isSuccess) {
|
||||
NoContent
|
||||
} else {
|
||||
val throwable = result.failed.get
|
||||
throwable match {
|
||||
case _: IllegalArgumentException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: NotInThisGameException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _: IllegalStateException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
case _ =>
|
||||
InternalServerError(throwable.getMessage)
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
BadRequest("tie parameter is missing")
|
||||
}
|
||||
case None =>
|
||||
NotFound("Game not found")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
45
knockoutwhistweb/app/controllers/MainMenuController.scala
Normal file
45
knockoutwhistweb/app/controllers/MainMenuController.scala
Normal file
@@ -0,0 +1,45 @@
|
||||
package controllers
|
||||
|
||||
import auth.{AuthAction, AuthenticatedRequest}
|
||||
import logic.PodManager
|
||||
import play.api.*
|
||||
import play.api.mvc.*
|
||||
|
||||
import javax.inject.*
|
||||
|
||||
|
||||
/**
|
||||
* This controller creates an `Action` to handle HTTP requests to the
|
||||
* application's home page.
|
||||
*/
|
||||
@Singleton
|
||||
class MainMenuController @Inject()(
|
||||
val controllerComponents: ControllerComponents,
|
||||
val authAction: AuthAction,
|
||||
val podManager: PodManager
|
||||
) extends BaseController {
|
||||
|
||||
// Pass the request-handling function directly to authAction (no nested Action)
|
||||
def mainMenu(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
Ok("Main Menu for user: " + request.user.name)
|
||||
}
|
||||
|
||||
def index(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
Redirect(routes.MainMenuController.mainMenu())
|
||||
}
|
||||
|
||||
def createGame(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val gameLobby = podManager.createGame(
|
||||
host = request.user,
|
||||
name = s"${request.user.name}'s Game",
|
||||
maxPlayers = 4
|
||||
)
|
||||
Redirect(routes.IngameController.game(gameLobby.id))
|
||||
}
|
||||
|
||||
def rules(): Action[AnyContent] = {
|
||||
Action { implicit request =>
|
||||
Ok(views.html.mainmenu.rules())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import controllers.sessions.PlayerSession
|
||||
import de.knockoutwhist.utils.events.SimpleEvent
|
||||
|
||||
import java.util.UUID
|
||||
import scala.collection.mutable
|
||||
|
||||
object PodGameManager {
|
||||
|
||||
private val sessions: mutable.Map[UUID, PlayerSession] = mutable.Map()
|
||||
|
||||
def addSession(session: PlayerSession): Unit = {
|
||||
sessions.put(session.id, session)
|
||||
}
|
||||
|
||||
def clearSessions(): Unit = {
|
||||
sessions.clear()
|
||||
}
|
||||
|
||||
def identify(id: UUID): Option[PlayerSession] = {
|
||||
sessions.get(id)
|
||||
}
|
||||
|
||||
def transmit(id: UUID, event: SimpleEvent): Unit = {
|
||||
identify(id).foreach(_.updatePlayer(event))
|
||||
}
|
||||
|
||||
def transmitAll(event: SimpleEvent): Unit = {
|
||||
sessions.foreach(session => session._2.updatePlayer(event))
|
||||
}
|
||||
|
||||
def listSessions(): List[PlayerSession] = {
|
||||
sessions.values.toList
|
||||
}
|
||||
|
||||
}
|
||||
70
knockoutwhistweb/app/controllers/UserController.scala
Normal file
70
knockoutwhistweb/app/controllers/UserController.scala
Normal file
@@ -0,0 +1,70 @@
|
||||
package controllers
|
||||
|
||||
import auth.{AuthAction, AuthenticatedRequest}
|
||||
import logic.user.{SessionManager, UserManager}
|
||||
import play.api.*
|
||||
import play.api.mvc.*
|
||||
|
||||
import javax.inject.*
|
||||
|
||||
|
||||
/**
|
||||
* This controller creates an `Action` to handle HTTP requests to the
|
||||
* application's home page.
|
||||
*/
|
||||
@Singleton
|
||||
class UserController @Inject()(
|
||||
val controllerComponents: ControllerComponents,
|
||||
val sessionManager: SessionManager,
|
||||
val userManager: UserManager,
|
||||
val authAction: AuthAction
|
||||
) extends BaseController {
|
||||
|
||||
def login(): Action[AnyContent] = {
|
||||
Action { implicit request =>
|
||||
val session = request.cookies.get("sessionId")
|
||||
if (session.isDefined) {
|
||||
val possibleUser = sessionManager.getUserBySession(session.get.value)
|
||||
if (possibleUser.isDefined) {
|
||||
Redirect(routes.MainMenuController.mainMenu())
|
||||
} else {
|
||||
Ok(views.html.login.login())
|
||||
}
|
||||
} else {
|
||||
Ok(views.html.login.login())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def login_Post(): Action[AnyContent] = {
|
||||
Action { implicit request =>
|
||||
val postData = request.body.asFormUrlEncoded
|
||||
if (postData.isDefined) {
|
||||
// Extract username and password from form data
|
||||
val username = postData.get.get("username").flatMap(_.headOption).getOrElse("")
|
||||
val password = postData.get.get("password").flatMap(_.headOption).getOrElse("")
|
||||
val possibleUser = userManager.authenticate(username, password)
|
||||
if (possibleUser.isDefined) {
|
||||
Redirect(routes.MainMenuController.mainMenu()).withCookies(
|
||||
Cookie("sessionId", sessionManager.createSession(possibleUser.get))
|
||||
)
|
||||
} else {
|
||||
println("Failed login attempt for user: " + username)
|
||||
Unauthorized("Invalid username or password")
|
||||
}
|
||||
} else {
|
||||
BadRequest("Invalid form submission")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass the request-handling function directly to authAction (no nested Action)
|
||||
def logout(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val sessionCookie = request.cookies.get("sessionId")
|
||||
if (sessionCookie.isDefined) {
|
||||
sessionManager.invalidateSession(sessionCookie.get.value)
|
||||
}
|
||||
NoContent.discardingCookies(DiscardingCookie("sessionId"))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import controllers.sessions.AdvancedSession
|
||||
import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit}
|
||||
import de.knockoutwhist.control.GameLogic
|
||||
import de.knockoutwhist.control.GameState.{InGame, Lobby}
|
||||
import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic
|
||||
import de.knockoutwhist.events.*
|
||||
import de.knockoutwhist.events.global.GameStateChangeEvent
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.Match
|
||||
import de.knockoutwhist.ui.UI
|
||||
import de.knockoutwhist.utils.CustomThread
|
||||
import de.knockoutwhist.utils.events.{EventListener, SimpleEvent}
|
||||
|
||||
object WebUI extends CustomThread with EventListener with UI {
|
||||
|
||||
setName("WebUI")
|
||||
|
||||
var init = false
|
||||
var logic: Option[GameLogic] = None
|
||||
|
||||
var latestOutput: String = ""
|
||||
|
||||
override def instance: CustomThread = WebUI
|
||||
|
||||
override def listen(event: SimpleEvent): Unit = {
|
||||
event match {
|
||||
case event: GameStateChangeEvent =>
|
||||
if (event.oldState == Lobby && event.newState == InGame) {
|
||||
val match1: Option[Match] = logic.get.asInstanceOf[BaseGameLogic].getCurrentMatch
|
||||
val players: List[AbstractPlayer] = match1.get.totalplayers
|
||||
players.map(player => PodGameManager.addSession(AdvancedSession(player.id, player)))
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
override def initial(gameLogic: GameLogic): Boolean = {
|
||||
if (init) {
|
||||
return false
|
||||
}
|
||||
init = true
|
||||
this.logic = Some(gameLogic)
|
||||
start()
|
||||
true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package controllers.sessions
|
||||
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.utils.events.SimpleEvent
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
case class AdvancedSession(id: UUID, player: AbstractPlayer) extends PlayerSession {
|
||||
|
||||
def name: String = player.name
|
||||
|
||||
override def updatePlayer(event: SimpleEvent): Unit = {
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package controllers.sessions
|
||||
|
||||
import de.knockoutwhist.utils.events.SimpleEvent
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
trait PlayerSession {
|
||||
|
||||
def id: UUID
|
||||
def name: String
|
||||
def updatePlayer(event: SimpleEvent): Unit
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user