2021-08-28 00:44:20 +03:00

254 lines
8.2 KiB
Scala

package actors
import actors.UserActor.{BanMapMessage, GetName, HostLeaveLobby, LobbyFatal, Message, RefreshLobbyInfo, SecondPlayerLeaveLobby, SendBanMapMessage, SendMessage, 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 var lobbyType: LobbyType = Last3()
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,
lobbyType.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) match {
case Some(map) =>
if(!map.isBanned){
map.isBanned = true
if (playerTurn== 1) playerTurn = 2 else playerTurn = 1
if(mapsLobby.count(_.isBanned == false) == 3 && lobbyType == Last3() ||
mapsLobby.count(_.isBanned == false) == 4 && lobbyType == Last4() ||
mapsLobby.count(_.isBanned == false) == 5 && lobbyType == Last5() ||
mapsLobby.count(_.isBanned == false) == 1 && lobbyType == LooserPick()){
status = Finish()
}
users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse))
val senderName: String = if (sender == host.actorRef) {
host.name
} else secondPlayer.get.name
users.foreach(_ ! SendBanMapMessage(BanMapMessage(senderName, mapName)))
} else {
logger.warn(s"User ban map $mapName, but map already banned")
}
case None =>
logger.error(s"Map $mapName not exist")
}
} 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 lobby status 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 ChangeLobbyType(lobbyTypeNew) =>
lobbyTypeNew match {
case "last3" =>
lobbyType = Last3()
case "last4" =>
lobbyType = Last4()
case "last5" =>
lobbyType = Last5()
case "looserpick" =>
lobbyType = LooserPick()
}
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 MessageForLobby(message) =>
if (sender() == host.actorRef) {
users.foreach(_ ! SendMessage(Message(host.name, message)))
}else if (secondPlayer.exists(sp => sp.actorRef == sender())){
users.foreach(_ ! SendMessage(Message(secondPlayer.get.name, message)))
}
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())){
secondPlayerReady = false
secondPlayer = None
if(status == Draft()){
users.foreach(_ ! SecondPlayerLeaveLobby)
context.stop(self)
}else {
users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse))
}
}
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,
lobbyType.toString(),
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 class MessageForLobby(message: String)
case class ChangeLobbyType(lobbyType: String)
case object InfoQuery
case class UserInfo(name: String, isReady: Boolean)
case class LobbyInfo(user1Info: UserInfo,
user2Info: UserInfo,
lobbyActorName: String,
status: String,
playerTurn: BigDecimal,
selectedType: String,
maps: Set[DeciderMap])
class LobbyStatus
sealed case class NotStarted() extends LobbyStatus
sealed case class Draft() extends LobbyStatus
sealed case class Finish() extends LobbyStatus
class LobbyType
sealed case class Last3() extends LobbyType
sealed case class Last4() extends LobbyType
sealed case class Last5() extends LobbyType
sealed case class LooserPick() extends LobbyType