diff --git a/app/actors/LobbieActor.scala b/app/actors/LobbieActor.scala index 11506de..4170759 100644 --- a/app/actors/LobbieActor.scala +++ b/app/actors/LobbieActor.scala @@ -1,74 +1,159 @@ package actors +import actors.UserActor.{GetName, HostLeaveLobby, RefreshLobbyInfo, SetUserAsHost} import akka.actor.{Actor, ActorLogging, ActorRef} import akka.event.LoggingReceive import com.typesafe.scalalogging.LazyLogging +import akka.pattern.ask +import akka.util.Timeout +import com.typesafe.config.{Config, ConfigFactory} +import scala.collection.JavaConverters.iterableAsScalaIterableConverter import scala.collection.immutable.{HashSet, Queue} +import scala.concurrent.Await import scala.concurrent.duration._ -case class DeciderMap(map: String, isBanned: Boolean = false) +case class LobbyUser(name: String, actorRef: ActorRef) +case class DeciderMap(map: String, var isBanned: Boolean = false) /** * There is one StockActor per stock symbol. The StockActor maintains a list of users watching the stock and the stock * values. Each StockActor updates a rolling dataset of randomly generated stock values. */ -class LobbieActor(lobbyName: String, hostUser: ActorRef) extends Actor with LazyLogging { +class LobbieActor(hostUser: LobbyUser) extends Actor with LazyLogging { - private val name: String = lobbyName + val config = ConfigFactory.load() + logger.info(s"Create lobby... host: ${hostUser.actorRef.path.name}") - // user actors + + private val host: LobbyUser = hostUser + private var secondPlayer: Option[LobbyUser] = None + + private var firstPlayerReady: Boolean = false + private var secondPlayerReady: Boolean = false + + private var playerTurn = 1 + + // all lobby users actors (observer and players) protected[this] var users: HashSet[ActorRef] = HashSet.empty[ActorRef] - private var status = NotStarted + users = users + host.actorRef - private var maps: List[DeciderMap] = List(DeciderMap("map1"), DeciderMap("map2"), DeciderMap("map3")) + private var status: LobbyStatus = NotStarted() - private var secondPlayer: Option[ActorRef] = None + private val mapsLobby: Set[DeciderMap] = { + val configMaps = config.getStringList("maps").asScala + configMaps.map(e => { + DeciderMap(e) + }).toSet + } - logger.info(s"Create lobby with name $lobbyName") + hostUser.actorRef.tell( + SetUserAsHost(LobbyInfo(hostUser.name, "", firstPlayerReady, secondPlayerReady, self.path.name, status.toString(), mapsLobby)), this.self) + + implicit val timeout: Timeout = 1.second def receive = LoggingReceive { case BanMap(mapName: String) => // notify watchers logger.info(s"Ban map $mapName by ${sender.path.name}") - users.foreach(_ ! MapsUpdate( maps)) - case JoinLobbyAsPlayer(_) => - secondPlayer = Some(sender) + if(status == Draft()){ + if((playerTurn == 1 && sender == host.actorRef) || + (playerTurn == 2 && secondPlayer.exists(_.actorRef == sender))){ + mapsLobby.find(p => p.map == mapName).foreach(_.isBanned = true) + if (playerTurn== 1) playerTurn = 2 else playerTurn = 1 + if(mapsLobby.count(_.isBanned == false) == 1){ + status = Finish() + } + users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse)) + } else{ + logger.warn(s"Player ${sender.path.name} ban map, but turn is $playerTurn") + } + } else { + logger.warn(s"Player ${sender.path.name} ban map, but it is '$status' status") + } + case SetReady => + // notify watchers + var readyPlayer = sender() + if(readyPlayer == hostUser.actorRef){ + firstPlayerReady = true + } else if (secondPlayer.exists(sp => sp.actorRef == readyPlayer)) { + secondPlayerReady = true + } + + if(firstPlayerReady && secondPlayerReady){ + status = Draft() + } + + users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse)) + + case SetNotReady => + // notify watchers + var notReadyPlayer = sender() + if(notReadyPlayer == hostUser.actorRef){ + firstPlayerReady = false + } else if (secondPlayer.exists(sp => sp.actorRef == notReadyPlayer)) { + secondPlayerReady = false + } + users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse)) + + case JoinLobbyAsPlayer(sp) => + logger.info(s"User ${sender.path.name} join lobby ${self.path.name} as player") + secondPlayer = Some(sp) + users = users + sp.actorRef + sp.actorRef.tell( + SetUserAsHost(LobbyInfo(hostUser.name, sp.name, firstPlayerReady, secondPlayerReady, self.path.name, status.toString(), mapsLobby)), this.self) + users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse)) case WatchLobby(_) => // add the watcher to the list users = users + sender case LeaveLobby => users = users - sender - if(secondPlayer.contains(sender)) secondPlayer = None + if(secondPlayer.exists(sp => sp.actorRef == sender)) secondPlayer = None + if(host.actorRef == sender()){ + users.foreach(_ ! HostLeaveLobby) + } if (users.isEmpty) { - logger.info(s"Stop lobby with name: $name") + logger.info(s"Stop lobby ${self.path.name}") context.stop(self) } case InfoQuery => - sender ! LobbyInfoResponse(name, self.path.name, status.toString()) + logger.info(s"Info query for lobby ${self.path.name} and host ${host.actorRef.path.name}") + sender ! RefreshLobbyInfo(getLobbyInfoResponse) + } + + private def getLobbyInfoResponse: LobbyInfo = { + val user2Name = secondPlayer.map(_.name).getOrElse("") + LobbyInfo(host.name, user2Name, firstPlayerReady, secondPlayerReady, self.path.name, status.toString(), mapsLobby) } } - case class BanMap(mapName: String) -case class MapsUpdate(maps: List[DeciderMap]) - -case class MapsHistory(symbol: String, maps: List[DeciderMap]) +case class MapsUpdate(maps: Set[String]) case class CreateLobby(userName: String) -case class JoinLobbyAsPlayer(lobbyName: String) - case class WatchLobby(lobbyName: String) -case class LeaveLobby(lobbyName: String) +case class JoinLobbyAsPlayer(lobbyUser: LobbyUser) + +case object LeaveLobby + +case object SetReady + +case object SetNotReady case object InfoQuery -case class LobbyInfoResponse(lobbyName: String, lobbyActorName: String, status: String) +case class LobbyInfo(user1Name: String, + user2Name: String, + user1Ready: Boolean, + user2Ready: Boolean, + lobbyActorName: String, + status: String, + maps: Set[DeciderMap]) class LobbyStatus diff --git a/app/actors/LobbiesActor.scala b/app/actors/LobbiesActor.scala index 0b70406..bb0e0c0 100644 --- a/app/actors/LobbiesActor.scala +++ b/app/actors/LobbiesActor.scala @@ -16,23 +16,26 @@ class LobbiesActor extends Actor with LazyLogging { val lobbies: ListBuffer[String] = ListBuffer() - def receive = LoggingReceive { - case CreateLobby(userName) => - val lobbyActor = context.actorOf(Props(new LobbieActor(userName, sender)), + def receive: Receive = LoggingReceive { + case CreateLobby(hostName) => + val hostActorRef = sender + logger.info(s"Player ${hostActorRef.path.name} create lobby.") + val lobbyActor = context.actorOf(Props(new LobbieActor(LobbyUser(hostName,hostActorRef))), s"lobbyActor-${(math.random*100000000L).toLong}") - lobbyActor.tell(WatchLobby("watchIt"), sender) + lobbyActor.tell(WatchLobby("watchIt"), hostActorRef) + case JoinLobbyByActorName(lobbyName, userName) => + // get or create the StockActor for the symbol and forward this message + val user = sender + context.child(lobbyName) match { + case Some(lobbyActor) => lobbyActor ! JoinLobbyAsPlayer(LobbyUser(userName, user)) + case None => logger.error(s"Can't watch lobby $lobbyName - lobby not exists") + } case watchLobby@WatchLobby(lobbyName) => // get or create the StockActor for the symbol and forward this message context.child(lobbyName) match { case Some(lobbyActor) => lobbyActor forward watchLobby case None => logger.error(s"Can't watch lobby $lobbyName - lobby not exists") } - case unwatchLobby@LeaveLobby(symbol) => - // if there is a StockActor for the symbol forward this message - context.child(symbol).foreach(_.forward(unwatchLobby)) - case banMap@BanMap(map) => - logger.info(s"forvard ban $map") - context.children.foreach(_.forward(banMap)) case UnWatchAllLobbies => context.children.foreach( _.tell(LeaveLobby, sender)) case GetAllLobbies => @@ -45,4 +48,6 @@ class LobbiesActor extends Actor with LazyLogging { case object GetAllLobbies +case class JoinLobbyByActorName(actorName: String, userName: String) + case object UnWatchAllLobbies \ No newline at end of file diff --git a/app/actors/UserActor.scala b/app/actors/UserActor.scala index 12c2848..232f421 100644 --- a/app/actors/UserActor.scala +++ b/app/actors/UserActor.scala @@ -1,6 +1,6 @@ package actors -import actors.UserActor.GetName +import actors.UserActor.{GetName, HostLeaveLobby, RefreshLobbyInfo, SetUserAsHost, SetUserAsSecondPlayer} import akka.actor._ import akka.event.LoggingReceive import akka.pattern.ask @@ -18,16 +18,7 @@ class UserActor(out: ActorRef, implicit val timeout: Timeout = 3.seconds - var userStatus: UserStatus = InLobby() - - implicit val lobbyResponseWrites: Writes[List[LobbyInfoResponse]] = new Writes[List[LobbyInfoResponse]] { - - implicit val lobbyWrites: OWrites[LobbyInfoResponse] = Json.writes[LobbyInfoResponse] - - override def writes(o: List[LobbyInfoResponse]): JsValue = { - JsArray(o.map(lobby => Json.toJson(lobby))) - } - } + var lobbieActor: Option[ActorRef] = None implicit val mapJson: Writes[DeciderMap] = new Writes[DeciderMap] { def writes(deciderMap: DeciderMap): JsValue = { @@ -38,6 +29,15 @@ class UserActor(out: ActorRef, } } + implicit val lobbyWrites: OWrites[LobbyInfo] = Json.writes[LobbyInfo] + + implicit val lobbyResponseWrites: Writes[List[LobbyInfo]] = new Writes[List[LobbyInfo]] { + + override def writes(o: List[LobbyInfo]): JsValue = { + JsArray(o.map(lobby => Json.toJson(lobby))) + } + } + var name = "" override def preStart(): Unit = { @@ -52,14 +52,29 @@ class UserActor(out: ActorRef, override def receive: Receive = LoggingReceive { - case MapsUpdate(deciderMaps) => - val maps = deciderMaps.map(map => Json.toJson(map)) - out ! Json.obj("type" -> "maps", "deciderMaps" -> maps) case GetName => sender ! name + case SetUserAsHost(lobbyInfo) => + val lobbyActor = sender() + logger.info(s"Receive set user ${self.path.name} as host from ${lobbyActor.path.name}") + setUserAsJoinedOrCreatedLobby(lobbyActor, lobbyInfo) + + case SetUserAsSecondPlayer(lobbyInfo) => + val lobbyActor = sender() + logger.info(s"Receive set user ${self.path.name} as second player from ${lobbyActor.path.name}") + setUserAsJoinedOrCreatedLobby(lobbyActor, lobbyInfo) + + case HostLeaveLobby => + logger.info(s"Host leave from lobby ${sender.path.name}") + out ! Json.obj("type" -> "hostLeaveLobby") + + case RefreshLobbyInfo(lobbyInfo) => + logger.trace(s"Refresh lobby info: $lobbyInfo") + out ! Json.obj("type" -> "refreshLobby", "lobby" -> lobbyInfo) + case json: JsValue => - // When the user types in a stock in the upper right corner, this is triggered + // Обрабатываем запросы с фронта val comType = (json \ "type").asOpt[String] comType match { @@ -67,8 +82,8 @@ class UserActor(out: ActorRef, name = (json \ "name").as[String] logger.debug(s"Set user name: $name for actor ${this.self}") case Some("getAllUsers") => - val usersActorFuture = (userParentActor ? UserParentActor.GetAllUsers).mapTo[Iterable[ActorRef]] - usersActorFuture.map(actorRefs => { + val userActorsFuture = (userParentActor ? UserParentActor.GetAllUsers).mapTo[Iterable[ActorRef]] + userActorsFuture.map(actorRefs => { logger.debug(s"There are ${actorRefs.size} users on site") actorRefs.map(userActorRef => userActorRef ? GetName).map(res => { res.onComplete { @@ -81,23 +96,41 @@ class UserActor(out: ActorRef, lobbiesActor ! CreateLobby(name) case Some("leaveDecider") => - lobbiesActor ! LeaveLobby(name) + lobbieActor.foreach(lobby => lobby ! LeaveLobby) - case Some("updateDecider") => - lobbiesActor ! BanMap("map1") + case Some("setReady") => + lobbieActor.foreach(lobby => lobby ! SetReady) + + case Some("setNotReady") => + lobbieActor.foreach(lobby => lobby ! SetNotReady) + + case Some("joinDecider") => + val lobbyActorName = (json \ "lobbyActorName").as[String] + logger.info(s"Player ${self.path.name} join lobby $lobbyActorName") + lobbiesActor ! JoinLobbyByActorName(lobbyActorName, name) + + case Some("banMap") => + val map = (json \ "map").as[String] + lobbieActor.foreach(lobby => lobby ! BanMap(map)) case Some("getLobbies") => logger.debug("Get all lobby request") - (lobbiesActor ? GetAllLobbies).mapTo[List[LobbyInfoResponse]] onComplete { + (lobbiesActor ? GetAllLobbies).mapTo[List[RefreshLobbyInfo]] onComplete { case Success(lobbies) => { logger.info(s"Received lobbies: $lobbies") - out ! Json.obj("type" -> "lobbies", "lobbies" -> lobbies) + out ! Json.obj("type" -> "lobbies", "lobbies" -> lobbies.map(res => res.lobbyInfo)) } case Failure(ex) => logger.error("Received error", ex) } } } + + private def setUserAsJoinedOrCreatedLobby(lobbyActor: ActorRef, lobbyInfo: LobbyInfo): Unit = { + this.lobbieActor = Some(lobbyActor) + out ! Json.obj("type" -> "switchToLobby", "lobby" -> lobbyInfo) + } + } class UserParentActor(actorSystem: ActorSystem) extends Actor with ActorLogging { @@ -113,14 +146,6 @@ class UserParentActor(actorSystem: ActorSystem) extends Actor with ActorLogging } } -class UserStatus - -case class InLobby() extends UserStatus - -case class NotReady() extends UserStatus - -case class Ready() extends UserStatus - object UserParentActor { @@ -139,7 +164,16 @@ object UserActor { case object GetName + case class RefreshLobbyInfo(lobbyInfo: LobbyInfo) + + case class SetUserAsHost(lobbyInfo: LobbyInfo) + + case class SetUserAsSecondPlayer(lobbyInfo: LobbyInfo) + case object CreateLobby + case object HostLeaveLobby + + } diff --git a/app/assets/images/buttons/isNotAuto.png b/app/assets/images/buttons/isNotAuto.png new file mode 100644 index 0000000..391f7e6 Binary files /dev/null and b/app/assets/images/buttons/isNotAuto.png differ diff --git a/app/assets/images/maps/2p_blood_river_[Rem].jpg b/app/assets/images/maps/2p_blood_river_[Rem].jpg new file mode 100644 index 0000000..776f5d1 Binary files /dev/null and b/app/assets/images/maps/2p_blood_river_[Rem].jpg differ diff --git a/app/assets/images/maps/2p_fata_morgana_[Rem].jpg b/app/assets/images/maps/2p_fata_morgana_[Rem].jpg new file mode 100644 index 0000000..d086ea2 Binary files /dev/null and b/app/assets/images/maps/2p_fata_morgana_[Rem].jpg differ diff --git a/app/assets/javascripts/index.js b/app/assets/javascripts/index.js index 244ee75..d6f15d1 100644 --- a/app/assets/javascripts/index.js +++ b/app/assets/javascripts/index.js @@ -1,8 +1,7 @@ - -var updateDecider; - var ws; +var userName = "" + window.onload = function() { 'use strict'; @@ -11,36 +10,6 @@ window.onload = function() { return Math.floor(Math.random() * Math.floor(max)); } - var DeciderLobby = Backbone.View.extend({ - - lobbies: undefined, - - initialize: function (number) { - console.log(number); - this.elCount = number; - }, - - template: _.template('
| Player1 | \n' + + 'Player2 | \n' + + 'Status | \n' + + 'Action |
|---|
";
+ var notReadyImg = "
";
+
+ if(lobby.user1Name === userName){
+ if(lobby.user1Ready){
+ player1ReadyBtn = ""
+ }else{
+ player1ReadyBtn = ""
+ }
+ }else{
+ if(lobby.user1Ready){
+ player1ReadyBtn = readyImg
+ }else{
+ player1ReadyBtn = notReadyImg
+ }
+ }
+
+ if(lobby.user2Name === userName){
+ if(lobby.user2Ready){
+ player2ReadyBtn = ""
+ }else{
+ player2ReadyBtn = ""
+ }
+ } else {
+ if(lobby.user2Ready){
+ player2ReadyBtn = readyImg
+ }else{
+ player2ReadyBtn = notReadyImg
+ }
+ }
+
+ console.log(lobby);
+
+ switch (lobby.status){
+ case "NotStarted()":
+ resHtml = "