2020-11-28 19:27:33 +03:00

196 lines
6.1 KiB
Scala

package actors
import actors.UserActor.{GetName, HostLeaveLobby, LobbyFatal, RefreshLobbyInfo, SecondPlayerLeaveLobby, SetUserAsHost, SetUserAsObs, SetUserAsSecondPlayer}
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(
UserInfo(hostUser.name,firstPlayerReady),
UserInfo("", secondPlayerReady),
self.path.name,
status.toString(),
playerTurn,
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 KickSecondPlayer =>
if (sender() == host.actorRef) {
secondPlayer.foreach(player => player.actorRef ! LobbyFatal("You were kicked from lobby!"))
secondPlayerReady = false
secondPlayer = None
users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse))
}
case JoinLobbyAsPlayer(sp) =>
logger.info(s"User ${sender.path.name} join lobby ${self.path.name} as player")
if (secondPlayer.isEmpty) {
secondPlayer = Some(sp)
users = users + sp.actorRef
sp.actorRef.tell(
SetUserAsSecondPlayer(getLobbyInfoResponse), this.self)
users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse))
} else {
sp.actorRef ! LobbyFatal("Lobby already full")
}
case WatchLobby(_) =>
// add the watcher to the list
users = users + sender
sender ! SetUserAsObs(getLobbyInfoResponse)
case LeaveLobby =>
users = users - sender
if(host.actorRef == sender()){
users.foreach(_ ! HostLeaveLobby)
context.stop(self)
}else if(secondPlayer.exists(_.actorRef == sender())){
users.foreach(_ ! SecondPlayerLeaveLobby)
secondPlayer = None
context.stop(self)
}
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(
UserInfo(host.name, firstPlayerReady),
UserInfo(user2Name, secondPlayerReady),
self.path.name,
status.toString(),
playerTurn,
mapsLobby)
}
}
case class BanMap(mapName: String)
case class MapsUpdate(maps: Set[String])
case class CreateLobby(userName: String)
case class JoinLobbyAsPlayer(lobbyUser: LobbyUser)
case object KickSecondPlayer
case class WatchLobby(lobbyName: String)
case object LeaveLobby
case object SetReady
case object SetNotReady
case object InfoQuery
case class UserInfo(name: String, isReady: Boolean)
case class LobbyInfo(user1Info: UserInfo,
user2Info: UserInfo,
lobbyActorName: String,
status: String,
playerTurn: BigDecimal,
maps: Set[DeciderMap])
class LobbyStatus
sealed case class NotStarted() extends LobbyStatus
sealed case class Draft() extends LobbyStatus
sealed case class Finish() extends LobbyStatus