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 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(hostUser: LobbyUser) extends Actor with LazyLogging { val config = ConfigFactory.load() logger.info(s"Create lobby... host: ${hostUser.actorRef.path.name}") 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] users = users + host.actorRef private var status: LobbyStatus = NotStarted() private val mapsLobby: Set[DeciderMap] = { val configMaps = config.getStringList("maps").asScala configMaps.map(e => { DeciderMap(e) }).toSet } 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}") 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.exists(sp => sp.actorRef == sender)) secondPlayer = None if(host.actorRef == sender()){ users.foreach(_ ! HostLeaveLobby) } if (users.isEmpty) { logger.info(s"Stop lobby ${self.path.name}") context.stop(self) } case InfoQuery => 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: Set[String]) case class CreateLobby(userName: String) case class WatchLobby(lobbyName: String) case class JoinLobbyAsPlayer(lobbyUser: LobbyUser) case object LeaveLobby case object SetReady case object SetNotReady case object InfoQuery case class LobbyInfo(user1Name: String, user2Name: String, user1Ready: Boolean, user2Ready: Boolean, lobbyActorName: String, status: String, maps: Set[DeciderMap]) class LobbyStatus sealed case class NotStarted() extends LobbyStatus sealed case class Draft() extends LobbyStatus sealed case class Finish() extends LobbyStatus