296 lines
9.5 KiB
Scala
296 lines
9.5 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._
|
|
|
|
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 isNecrons: Boolean = false
|
|
|
|
private var firstPlayerSelectedRaces: Option[SelectedRaces] = None
|
|
private var secondPlayerSelectedRaces: Option[SelectedRaces] = None
|
|
private var thirdPlayerSelectedRaces: 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(),
|
|
isNecrons,
|
|
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) == 1 && lobbyType == Last1() ||
|
|
mapsLobby.count(_.isBanned == false) == 3 && lobbyType == Last3() ||
|
|
mapsLobby.count(_.isBanned == false) == 5 && lobbyType == Last5() ||
|
|
mapsLobby.count(_.isBanned == false) == 7 && lobbyType == Last7()
|
|
) {
|
|
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 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(),
|
|
isNecrons,
|
|
mapsLobby)
|
|
}
|
|
}
|
|
|
|
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(firstRace: Int, secondRace: Int, thirdRace: Int, fourthRace: 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,
|
|
isNecrons: Boolean,
|
|
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
|