2023-08-05 16:08:50 +03:00

298 lines
9.4 KiB
Scala

package actors
import actors.UserActor._
import akka.actor.{Actor, ActorRef}
import akka.event.LoggingReceive
import akka.util.Timeout
import com.typesafe.config.ConfigFactory
import com.typesafe.scalalogging.LazyLogging
import java.util
import scala.collection.JavaConverters.iterableAsScalaIterableConverter
import scala.collection.immutable.HashSet
import scala.concurrent.duration._
import scala.util.Try
case class LobbyUser(name: String, actorRef: ActorRef)
case class DeciderMap(map: String, description: Option[String] = None, 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, deciderName: String) 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 = Last1()
private var firstPlayerSelectedRaces: Option[SelectedRaces] = None
private var secondPlayerSelectedRaces: Option[SelectedRaces] = None
private val mapsLobby: Set[DeciderMap] = {
val configMaps = config.getList(s"deciders.$deciderName.maps").asScala
configMaps.map(e => {
val mapConfig = e.unwrapped().asInstanceOf[util.ArrayList[String]].asScala
DeciderMap(mapConfig.head, mapConfig.tail.headOption)
}).toSet
}
hostUser.actorRef.tell(
SetUserAsHost(LobbyInfo(
UserInfo(hostUser.name, firstPlayerReady),
UserInfo("", secondPlayerReady),
self.path.name,
deciderName,
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 (isFinish) {
status = Finish()
}
refreshAndBanMap(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 selectedRaces: SelectedRaces =>
if (sender == host.actorRef) {
firstPlayerSelectedRaces = Some(selectedRaces)
logger.info(s"First player select $selectedRaces")
} else if (sender == secondPlayer.get.actorRef) {
secondPlayerSelectedRaces = Some(selectedRaces)
logger.info(s"Second player select $selectedRaces")
}
if (firstPlayerSelectedRaces.nonEmpty && secondPlayerSelectedRaces.nonEmpty) {
status = Draft()
users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse))
}
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 = SelectRace()
}
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 "last1" =>
lobbyType = Last1()
case "last3" =>
lobbyType = Last3()
case "last5" =>
lobbyType = Last5()
case "last7" =>
lobbyType = Last7()
}
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() || status == SelectRace()) {
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, firstPlayerSelectedRaces),
UserInfo(user2Name, secondPlayerReady, secondPlayerSelectedRaces),
self.path.name,
deciderName,
status.toString(),
playerTurn,
lobbyType.toString(),
mapsLobby)
}
private def isFinish: Boolean = {
mapsLobby.count(_.isBanned == false) == 1 && lobbyType == Last1() ||
mapsLobby.count(_.isBanned == false) == 3 && lobbyType == Last3() ||
mapsLobby.count(_.isBanned == false) == 5 && lobbyType == Last5() ||
mapsLobby.count(_.isBanned == false) == 7 && lobbyType == Last7()
}
private def refreshAndBanMap(mapName: String): Unit = {
val senderName: String = if (sender == host.actorRef) {
host.name
} else secondPlayer.get.name
users.foreach(_ ! SendBanMapMessage(BanMapMessage(senderName, mapName)))
users.foreach(_ ! RefreshLobbyInfo(getLobbyInfoResponse))
}
}
case class BanMap(mapName: String)
case class MapsUpdate(maps: Set[String])
case class CreateLobby(userName: String, deciderName: 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 class ChangeIsNecronSelected(isSelected: Boolean)
case object InfoQuery
case class SelectedRaces(mainRaces: List[Int], additionalRaces: List[Int])
case class UserInfo(name: String, isReady: Boolean, selectedRaces: Option[SelectedRaces] = None)
case class LobbyInfo(user1Info: UserInfo,
user2Info: UserInfo,
lobbyActorName: String,
deciderName: String,
status: String,
playerTurn: BigDecimal,
selectedType: String,
maps: Set[DeciderMap])
class LobbyStatus
sealed case class NotStarted() extends LobbyStatus
sealed case class SelectRace() extends LobbyStatus
sealed case class Draft() extends LobbyStatus
sealed case class Finish() extends LobbyStatus
class LobbyType
sealed case class Last1() extends LobbyType
sealed case class Last3() extends LobbyType
sealed case class Last5() extends LobbyType
sealed case class Last7() extends LobbyType
sealed case class Superfinal() extends LobbyType