commit fda47ec522b0f29183fcdbfcacb74dcbc08ef20e Author: Anibus Date: Sun Nov 15 16:54:46 2020 +0300 decider 2 start diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d91d9a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +build +logs +project/project +project/target +target +tmp +.history +dist +/.idea +/*.iml +/out +/.idea_modules +/.classpath +/.gradle +/.project +/RUNNING_PID +/.settings +/project/*-shim.sbt +/activator-sbt-*-shim.sbt diff --git a/README.md b/README.md new file mode 100644 index 0000000..c651322 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +This is decider for Soulstorm \ No newline at end of file diff --git a/app/actors/LobbieActor.scala b/app/actors/LobbieActor.scala new file mode 100644 index 0000000..11506de --- /dev/null +++ b/app/actors/LobbieActor.scala @@ -0,0 +1,77 @@ +package actors + +import akka.actor.{Actor, ActorLogging, ActorRef} +import akka.event.LoggingReceive +import com.typesafe.scalalogging.LazyLogging + +import scala.collection.immutable.{HashSet, Queue} +import scala.concurrent.duration._ + +case class DeciderMap(map: String, 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(lobbyName: String, hostUser: ActorRef) extends Actor with LazyLogging { + + private val name: String = lobbyName + + // user actors + protected[this] var users: HashSet[ActorRef] = HashSet.empty[ActorRef] + + private var status = NotStarted + + private var maps: List[DeciderMap] = List(DeciderMap("map1"), DeciderMap("map2"), DeciderMap("map3")) + + private var secondPlayer: Option[ActorRef] = None + + logger.info(s"Create lobby with name $lobbyName") + + + def receive = LoggingReceive { + case BanMap(mapName: String) => + // notify watchers + logger.info(s"Ban map $mapName by ${sender.path.name}") + users.foreach(_ ! MapsUpdate( maps)) + case JoinLobbyAsPlayer(_) => + secondPlayer = Some(sender) + case WatchLobby(_) => + // add the watcher to the list + users = users + sender + case LeaveLobby => + users = users - sender + if(secondPlayer.contains(sender)) secondPlayer = None + if (users.isEmpty) { + logger.info(s"Stop lobby with name: $name") + context.stop(self) + } + case InfoQuery => + sender ! LobbyInfoResponse(name, self.path.name, status.toString()) + } +} + + +case class BanMap(mapName: String) + +case class MapsUpdate(maps: List[DeciderMap]) + +case class MapsHistory(symbol: String, maps: List[DeciderMap]) + +case class CreateLobby(userName: String) + +case class JoinLobbyAsPlayer(lobbyName: String) + +case class WatchLobby(lobbyName: String) + +case class LeaveLobby(lobbyName: String) + +case object InfoQuery + +case class LobbyInfoResponse(lobbyName: String, lobbyActorName: String, status: String) + +class LobbyStatus + +sealed case class NotStarted() extends LobbyStatus +sealed case class Draft() extends LobbyStatus +sealed case class Finish() extends LobbyStatus diff --git a/app/actors/LobbiesActor.scala b/app/actors/LobbiesActor.scala new file mode 100644 index 0000000..0b70406 --- /dev/null +++ b/app/actors/LobbiesActor.scala @@ -0,0 +1,48 @@ +package actors + +import akka.actor.{Actor, ActorLogging, Props} +import akka.event.LoggingReceive +import akka.pattern.ask +import akka.util.Timeout +import com.typesafe.scalalogging.LazyLogging + +import scala.collection.mutable.ListBuffer +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt + +class LobbiesActor extends Actor with LazyLogging { + + implicit val timeout: Timeout = 1.second + + val lobbies: ListBuffer[String] = ListBuffer() + + def receive = LoggingReceive { + case CreateLobby(userName) => + val lobbyActor = context.actorOf(Props(new LobbieActor(userName, sender)), + s"lobbyActor-${(math.random*100000000L).toLong}") + lobbyActor.tell(WatchLobby("watchIt"), sender) + case watchLobby@WatchLobby(lobbyName) => + // get or create the StockActor for the symbol and forward this message + context.child(lobbyName) match { + case Some(lobbyActor) => lobbyActor forward watchLobby + case None => logger.error(s"Can't watch lobby $lobbyName - lobby not exists") + } + case unwatchLobby@LeaveLobby(symbol) => + // if there is a StockActor for the symbol forward this message + context.child(symbol).foreach(_.forward(unwatchLobby)) + case banMap@BanMap(map) => + logger.info(s"forvard ban $map") + context.children.foreach(_.forward(banMap)) + case UnWatchAllLobbies => + context.children.foreach( _.tell(LeaveLobby, sender)) + case GetAllLobbies => + sender ! context.children.toList.map(lobbyActor => + Await.result(lobbyActor ? InfoQuery, 1.second)) + + } + +} + +case object GetAllLobbies + +case object UnWatchAllLobbies \ No newline at end of file diff --git a/app/actors/UserActor.scala b/app/actors/UserActor.scala new file mode 100644 index 0000000..12c2848 --- /dev/null +++ b/app/actors/UserActor.scala @@ -0,0 +1,145 @@ +package actors + +import actors.UserActor.GetName +import akka.actor._ +import akka.event.LoggingReceive +import akka.pattern.ask +import akka.util.Timeout +import com.typesafe.scalalogging.LazyLogging +import play.api.libs.json._ + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration.DurationInt +import scala.util.{Failure, Success} + +class UserActor(out: ActorRef, + userParentActor: ActorRef, + lobbiesActor: ActorRef) extends Actor with LazyLogging { + + implicit val timeout: Timeout = 3.seconds + + var userStatus: UserStatus = InLobby() + + implicit val lobbyResponseWrites: Writes[List[LobbyInfoResponse]] = new Writes[List[LobbyInfoResponse]] { + + implicit val lobbyWrites: OWrites[LobbyInfoResponse] = Json.writes[LobbyInfoResponse] + + override def writes(o: List[LobbyInfoResponse]): JsValue = { + JsArray(o.map(lobby => Json.toJson(lobby))) + } + } + + implicit val mapJson: Writes[DeciderMap] = new Writes[DeciderMap] { + def writes(deciderMap: DeciderMap): JsValue = { + Json.obj( + "map" -> deciderMap.map, + "isBanned" -> deciderMap.isBanned + ) + } + } + + var name = "" + + override def preStart(): Unit = { + super.preStart() + + configureDefaultStocks() + } + + def configureDefaultStocks(): Unit = { + logger.info(s"Creating user actor") + } + + override def receive: Receive = LoggingReceive { + + case MapsUpdate(deciderMaps) => + val maps = deciderMaps.map(map => Json.toJson(map)) + out ! Json.obj("type" -> "maps", "deciderMaps" -> maps) + case GetName => + sender ! name + + case json: JsValue => + // When the user types in a stock in the upper right corner, this is triggered + + val comType = (json \ "type").asOpt[String] + comType match { + case Some("userName") => + name = (json \ "name").as[String] + logger.debug(s"Set user name: $name for actor ${this.self}") + case Some("getAllUsers") => + val usersActorFuture = (userParentActor ? UserParentActor.GetAllUsers).mapTo[Iterable[ActorRef]] + usersActorFuture.map(actorRefs => { + logger.debug(s"There are ${actorRefs.size} users on site") + actorRefs.map(userActorRef => userActorRef ? GetName).map(res => { + res.onComplete { + case Success(name) => logger.debug(s"There is $name on site") + } + }) + }) + + case Some("createDecider") => + lobbiesActor ! CreateLobby(name) + + case Some("leaveDecider") => + lobbiesActor ! LeaveLobby(name) + + case Some("updateDecider") => + lobbiesActor ! BanMap("map1") + + case Some("getLobbies") => + logger.debug("Get all lobby request") + (lobbiesActor ? GetAllLobbies).mapTo[List[LobbyInfoResponse]] onComplete { + case Success(lobbies) => { + logger.info(s"Received lobbies: $lobbies") + out ! Json.obj("type" -> "lobbies", "lobbies" -> lobbies) + } + case Failure(ex) => logger.error("Received error", ex) + } + + } + } +} + +class UserParentActor(actorSystem: ActorSystem) extends Actor with ActorLogging { + + import UserParentActor._ + + override def receive: Receive = LoggingReceive { + case Create(id, out, lobbiesActor) => + val child: ActorRef = actorSystem.actorOf(Props(classOf[UserActor], out, self, lobbiesActor), s"userActor-$id") + sender() ! child + case GetAllUsers => + sender() ! context.children + } +} + +class UserStatus + +case class InLobby() extends UserStatus + +case class NotReady() extends UserStatus + +case class Ready() extends UserStatus + + +object UserParentActor { + + case class Create(id: String, out: ActorRef, lobbiesActor: ActorRef) + + case object GetAllUsers + +} + +object UserActor { + + trait Factory { + // Corresponds to the @Assisted parameters defined in the constructor + def apply(out: ActorRef): Actor + } + + case object GetName + + case object CreateLobby + +} + diff --git a/app/assets/images/maps/2p_abandon_all_hope.jpg b/app/assets/images/maps/2p_abandon_all_hope.jpg new file mode 100644 index 0000000..0c26b4d Binary files /dev/null and b/app/assets/images/maps/2p_abandon_all_hope.jpg differ diff --git a/app/assets/images/maps/2p_absolute_zero.jpg b/app/assets/images/maps/2p_absolute_zero.jpg new file mode 100644 index 0000000..7151413 Binary files /dev/null and b/app/assets/images/maps/2p_absolute_zero.jpg differ diff --git a/app/assets/images/maps/2p_antiga_bay.jpg b/app/assets/images/maps/2p_antiga_bay.jpg new file mode 100644 index 0000000..5e7b30c Binary files /dev/null and b/app/assets/images/maps/2p_antiga_bay.jpg differ diff --git a/app/assets/images/maps/2p_battle_marshes.jpg b/app/assets/images/maps/2p_battle_marshes.jpg new file mode 100644 index 0000000..070f1a0 Binary files /dev/null and b/app/assets/images/maps/2p_battle_marshes.jpg differ diff --git a/app/assets/images/maps/2p_blood_river.jpg b/app/assets/images/maps/2p_blood_river.jpg new file mode 100644 index 0000000..435390c Binary files /dev/null and b/app/assets/images/maps/2p_blood_river.jpg differ diff --git a/app/assets/images/maps/2p_blood_river_remix.jpg b/app/assets/images/maps/2p_blood_river_remix.jpg new file mode 100644 index 0000000..9b5b910 Binary files /dev/null and b/app/assets/images/maps/2p_blood_river_remix.jpg differ diff --git a/app/assets/images/maps/2p_bloody hell.jpg b/app/assets/images/maps/2p_bloody hell.jpg new file mode 100644 index 0000000..5537a6a Binary files /dev/null and b/app/assets/images/maps/2p_bloody hell.jpg differ diff --git a/app/assets/images/maps/2p_bloody_hell.jpg b/app/assets/images/maps/2p_bloody_hell.jpg new file mode 100644 index 0000000..5537a6a Binary files /dev/null and b/app/assets/images/maps/2p_bloody_hell.jpg differ diff --git a/app/assets/images/maps/2p_bridge_too_far.jpg b/app/assets/images/maps/2p_bridge_too_far.jpg new file mode 100644 index 0000000..8b0a2db Binary files /dev/null and b/app/assets/images/maps/2p_bridge_too_far.jpg differ diff --git a/app/assets/images/maps/2p_colosseum.jpg b/app/assets/images/maps/2p_colosseum.jpg new file mode 100644 index 0000000..15d2e69 Binary files /dev/null and b/app/assets/images/maps/2p_colosseum.jpg differ diff --git a/app/assets/images/maps/2p_colosseum_suicide.jpg b/app/assets/images/maps/2p_colosseum_suicide.jpg new file mode 100644 index 0000000..5d6623a Binary files /dev/null and b/app/assets/images/maps/2p_colosseum_suicide.jpg differ diff --git a/app/assets/images/maps/2p_colosseum_suicide_pro.jpg b/app/assets/images/maps/2p_colosseum_suicide_pro.jpg new file mode 100644 index 0000000..4c28c0a Binary files /dev/null and b/app/assets/images/maps/2p_colosseum_suicide_pro.jpg differ diff --git a/app/assets/images/maps/2p_deadly_fun_archeology.jpg b/app/assets/images/maps/2p_deadly_fun_archeology.jpg new file mode 100644 index 0000000..e577ae9 Binary files /dev/null and b/app/assets/images/maps/2p_deadly_fun_archeology.jpg differ diff --git a/app/assets/images/maps/2p_deadmans_crossing.jpg b/app/assets/images/maps/2p_deadmans_crossing.jpg new file mode 100644 index 0000000..48ce5f2 Binary files /dev/null and b/app/assets/images/maps/2p_deadmans_crossing.jpg differ diff --git a/app/assets/images/maps/2p_edemus_gamble.jpg b/app/assets/images/maps/2p_edemus_gamble.jpg new file mode 100644 index 0000000..57947ee Binary files /dev/null and b/app/assets/images/maps/2p_edemus_gamble.jpg differ diff --git a/app/assets/images/maps/2p_eden.jpg b/app/assets/images/maps/2p_eden.jpg new file mode 100644 index 0000000..942e2f3 Binary files /dev/null and b/app/assets/images/maps/2p_eden.jpg differ diff --git a/app/assets/images/maps/2p_emerald_river.jpg b/app/assets/images/maps/2p_emerald_river.jpg new file mode 100644 index 0000000..08b4150 Binary files /dev/null and b/app/assets/images/maps/2p_emerald_river.jpg differ diff --git a/app/assets/images/maps/2p_emperors_valley.jpg b/app/assets/images/maps/2p_emperors_valley.jpg new file mode 100644 index 0000000..c597279 Binary files /dev/null and b/app/assets/images/maps/2p_emperors_valley.jpg differ diff --git a/app/assets/images/maps/2p_faceoff.jpg b/app/assets/images/maps/2p_faceoff.jpg new file mode 100644 index 0000000..3ade6f4 Binary files /dev/null and b/app/assets/images/maps/2p_faceoff.jpg differ diff --git a/app/assets/images/maps/2p_fallen_city.jpg b/app/assets/images/maps/2p_fallen_city.jpg new file mode 100644 index 0000000..a600425 Binary files /dev/null and b/app/assets/images/maps/2p_fallen_city.jpg differ diff --git a/app/assets/images/maps/2p_fata_morgana.jpg b/app/assets/images/maps/2p_fata_morgana.jpg new file mode 100644 index 0000000..2c98774 Binary files /dev/null and b/app/assets/images/maps/2p_fata_morgana.jpg differ diff --git a/app/assets/images/maps/2p_fear.jpg b/app/assets/images/maps/2p_fear.jpg new file mode 100644 index 0000000..9f55846 Binary files /dev/null and b/app/assets/images/maps/2p_fear.jpg differ diff --git a/app/assets/images/maps/2p_fraziersdemise.jpg b/app/assets/images/maps/2p_fraziersdemise.jpg new file mode 100644 index 0000000..68618e6 Binary files /dev/null and b/app/assets/images/maps/2p_fraziersdemise.jpg differ diff --git a/app/assets/images/maps/2p_frostbite_river.jpg b/app/assets/images/maps/2p_frostbite_river.jpg new file mode 100644 index 0000000..548f045 Binary files /dev/null and b/app/assets/images/maps/2p_frostbite_river.jpg differ diff --git a/app/assets/images/maps/2p_galenas_crusade.jpg b/app/assets/images/maps/2p_galenas_crusade.jpg new file mode 100644 index 0000000..32439cd Binary files /dev/null and b/app/assets/images/maps/2p_galenas_crusade.jpg differ diff --git a/app/assets/images/maps/2p_haines_demise.jpg b/app/assets/images/maps/2p_haines_demise.jpg new file mode 100644 index 0000000..238e4e6 Binary files /dev/null and b/app/assets/images/maps/2p_haines_demise.jpg differ diff --git a/app/assets/images/maps/2p_hellfire_canyon.jpg b/app/assets/images/maps/2p_hellfire_canyon.jpg new file mode 100644 index 0000000..9c5a0d0 Binary files /dev/null and b/app/assets/images/maps/2p_hellfire_canyon.jpg differ diff --git a/app/assets/images/maps/2p_jungle_morning.jpg b/app/assets/images/maps/2p_jungle_morning.jpg new file mode 100644 index 0000000..483e82d Binary files /dev/null and b/app/assets/images/maps/2p_jungle_morning.jpg differ diff --git a/app/assets/images/maps/2p_light_brigade.jpg b/app/assets/images/maps/2p_light_brigade.jpg new file mode 100644 index 0000000..b219444 Binary files /dev/null and b/app/assets/images/maps/2p_light_brigade.jpg differ diff --git a/app/assets/images/maps/2p_light_brigade_pro.jpg b/app/assets/images/maps/2p_light_brigade_pro.jpg new file mode 100644 index 0000000..a869329 Binary files /dev/null and b/app/assets/images/maps/2p_light_brigade_pro.jpg differ diff --git a/app/assets/images/maps/2p_meeting_of_minds.jpg b/app/assets/images/maps/2p_meeting_of_minds.jpg new file mode 100644 index 0000000..ede2462 Binary files /dev/null and b/app/assets/images/maps/2p_meeting_of_minds.jpg differ diff --git a/app/assets/images/maps/2p_meeting_of_minds_pro.jpg b/app/assets/images/maps/2p_meeting_of_minds_pro.jpg new file mode 100644 index 0000000..5949924 Binary files /dev/null and b/app/assets/images/maps/2p_meeting_of_minds_pro.jpg differ diff --git a/app/assets/images/maps/2p_meeting_of_minds_pro_5p.jpg b/app/assets/images/maps/2p_meeting_of_minds_pro_5p.jpg new file mode 100644 index 0000000..c088092 Binary files /dev/null and b/app/assets/images/maps/2p_meeting_of_minds_pro_5p.jpg differ diff --git a/app/assets/images/maps/2p_meeting_of_minds_pro_lis.jpg b/app/assets/images/maps/2p_meeting_of_minds_pro_lis.jpg new file mode 100644 index 0000000..c088092 Binary files /dev/null and b/app/assets/images/maps/2p_meeting_of_minds_pro_lis.jpg differ diff --git a/app/assets/images/maps/2p_meeting_of_minds_pro_lis_without_1p.jpg b/app/assets/images/maps/2p_meeting_of_minds_pro_lis_without_1p.jpg new file mode 100644 index 0000000..5949924 Binary files /dev/null and b/app/assets/images/maps/2p_meeting_of_minds_pro_lis_without_1p.jpg differ diff --git a/app/assets/images/maps/2p_moonbase.jpg b/app/assets/images/maps/2p_moonbase.jpg new file mode 100644 index 0000000..99c1789 Binary files /dev/null and b/app/assets/images/maps/2p_moonbase.jpg differ diff --git a/app/assets/images/maps/2p_outer_reaches.jpg b/app/assets/images/maps/2p_outer_reaches.jpg new file mode 100644 index 0000000..933ff57 Binary files /dev/null and b/app/assets/images/maps/2p_outer_reaches.jpg differ diff --git a/app/assets/images/maps/2p_quests_triumph.jpg b/app/assets/images/maps/2p_quests_triumph.jpg new file mode 100644 index 0000000..0d707e7 Binary files /dev/null and b/app/assets/images/maps/2p_quests_triumph.jpg differ diff --git a/app/assets/images/maps/2p_quests_triumph_pro.jpg b/app/assets/images/maps/2p_quests_triumph_pro.jpg new file mode 100644 index 0000000..514356e Binary files /dev/null and b/app/assets/images/maps/2p_quests_triumph_pro.jpg differ diff --git a/app/assets/images/maps/2p_railway.jpg b/app/assets/images/maps/2p_railway.jpg new file mode 100644 index 0000000..cbdad35 Binary files /dev/null and b/app/assets/images/maps/2p_railway.jpg differ diff --git a/app/assets/images/maps/2p_river_bed.jpg b/app/assets/images/maps/2p_river_bed.jpg new file mode 100644 index 0000000..90639eb Binary files /dev/null and b/app/assets/images/maps/2p_river_bed.jpg differ diff --git a/app/assets/images/maps/2p_short_below_zero.jpg b/app/assets/images/maps/2p_short_below_zero.jpg new file mode 100644 index 0000000..a5bee0f Binary files /dev/null and b/app/assets/images/maps/2p_short_below_zero.jpg differ diff --git a/app/assets/images/maps/2p_shrine_of_excellion.jpg b/app/assets/images/maps/2p_shrine_of_excellion.jpg new file mode 100644 index 0000000..5560c7b Binary files /dev/null and b/app/assets/images/maps/2p_shrine_of_excellion.jpg differ diff --git a/app/assets/images/maps/2p_shrine_of_excellion_[Rem].jpg b/app/assets/images/maps/2p_shrine_of_excellion_[Rem].jpg new file mode 100644 index 0000000..d862670 Binary files /dev/null and b/app/assets/images/maps/2p_shrine_of_excellion_[Rem].jpg differ diff --git a/app/assets/images/maps/2p_sugaroasis.jpg b/app/assets/images/maps/2p_sugaroasis.jpg new file mode 100644 index 0000000..472f6d5 Binary files /dev/null and b/app/assets/images/maps/2p_sugaroasis.jpg differ diff --git a/app/assets/images/maps/2p_tainted_pair.jpg b/app/assets/images/maps/2p_tainted_pair.jpg new file mode 100644 index 0000000..c8da128 Binary files /dev/null and b/app/assets/images/maps/2p_tainted_pair.jpg differ diff --git a/app/assets/images/maps/2p_tazins_folly.jpg b/app/assets/images/maps/2p_tazins_folly.jpg new file mode 100644 index 0000000..96846ba Binary files /dev/null and b/app/assets/images/maps/2p_tazins_folly.jpg differ diff --git a/app/assets/images/maps/2p_tiboraxx.jpg b/app/assets/images/maps/2p_tiboraxx.jpg new file mode 100644 index 0000000..db575dd Binary files /dev/null and b/app/assets/images/maps/2p_tiboraxx.jpg differ diff --git a/app/assets/images/maps/2p_titan_fall.jpg b/app/assets/images/maps/2p_titan_fall.jpg new file mode 100644 index 0000000..1f6d453 Binary files /dev/null and b/app/assets/images/maps/2p_titan_fall.jpg differ diff --git a/app/assets/images/maps/2p_titan_fall_[Rem].jpg b/app/assets/images/maps/2p_titan_fall_[Rem].jpg new file mode 100644 index 0000000..e82e712 Binary files /dev/null and b/app/assets/images/maps/2p_titan_fall_[Rem].jpg differ diff --git a/app/assets/images/maps/2p_tower_ruins.jpg b/app/assets/images/maps/2p_tower_ruins.jpg new file mode 100644 index 0000000..d5081e1 Binary files /dev/null and b/app/assets/images/maps/2p_tower_ruins.jpg differ diff --git a/app/assets/images/maps/2p_tranquilitys_end.jpg b/app/assets/images/maps/2p_tranquilitys_end.jpg new file mode 100644 index 0000000..b44552f Binary files /dev/null and b/app/assets/images/maps/2p_tranquilitys_end.jpg differ diff --git a/app/assets/images/maps/2p_tranquilitys_end_[rem].jpg b/app/assets/images/maps/2p_tranquilitys_end_[rem].jpg new file mode 100644 index 0000000..4b95c23 Binary files /dev/null and b/app/assets/images/maps/2p_tranquilitys_end_[rem].jpg differ diff --git a/app/assets/images/maps/2p_tranquilitys_end_pro.jpg b/app/assets/images/maps/2p_tranquilitys_end_pro.jpg new file mode 100644 index 0000000..d3130dd Binary files /dev/null and b/app/assets/images/maps/2p_tranquilitys_end_pro.jpg differ diff --git a/app/assets/images/maps/2p_valley_of_khorne.jpg b/app/assets/images/maps/2p_valley_of_khorne.jpg new file mode 100644 index 0000000..9759aa3 Binary files /dev/null and b/app/assets/images/maps/2p_valley_of_khorne.jpg differ diff --git a/app/assets/images/maps/2p_velvet_duress.jpg b/app/assets/images/maps/2p_velvet_duress.jpg new file mode 100644 index 0000000..746555e Binary files /dev/null and b/app/assets/images/maps/2p_velvet_duress.jpg differ diff --git a/app/assets/images/maps/2p_vile_reef.jpg b/app/assets/images/maps/2p_vile_reef.jpg new file mode 100644 index 0000000..4b28d34 Binary files /dev/null and b/app/assets/images/maps/2p_vile_reef.jpg differ diff --git a/app/assets/images/maps/2p_volatile_ground.jpg b/app/assets/images/maps/2p_volatile_ground.jpg new file mode 100644 index 0000000..ac9b528 Binary files /dev/null and b/app/assets/images/maps/2p_volatile_ground.jpg differ diff --git a/app/assets/images/maps/2p_vortex_plateau.jpg b/app/assets/images/maps/2p_vortex_plateau.jpg new file mode 100644 index 0000000..38343a5 Binary files /dev/null and b/app/assets/images/maps/2p_vortex_plateau.jpg differ diff --git a/app/assets/images/maps/3p_coral_reef.jpg b/app/assets/images/maps/3p_coral_reef.jpg new file mode 100644 index 0000000..143fe6d Binary files /dev/null and b/app/assets/images/maps/3p_coral_reef.jpg differ diff --git a/app/assets/images/maps/3p_faded_dreams.jpg b/app/assets/images/maps/3p_faded_dreams.jpg new file mode 100644 index 0000000..5fafc21 Binary files /dev/null and b/app/assets/images/maps/3p_faded_dreams.jpg differ diff --git a/app/assets/images/maps/3p_fortress.jpg b/app/assets/images/maps/3p_fortress.jpg new file mode 100644 index 0000000..355c58a Binary files /dev/null and b/app/assets/images/maps/3p_fortress.jpg differ diff --git a/app/assets/images/maps/3p_heat_wave.jpg b/app/assets/images/maps/3p_heat_wave.jpg new file mode 100644 index 0000000..8511d12 Binary files /dev/null and b/app/assets/images/maps/3p_heat_wave.jpg differ diff --git a/app/assets/images/maps/4p_antiga_bay.jpg b/app/assets/images/maps/4p_antiga_bay.jpg new file mode 100644 index 0000000..0278cf2 Binary files /dev/null and b/app/assets/images/maps/4p_antiga_bay.jpg differ diff --git a/app/assets/images/maps/4p_ariel_highlands.jpg b/app/assets/images/maps/4p_ariel_highlands.jpg new file mode 100644 index 0000000..9b441b2 Binary files /dev/null and b/app/assets/images/maps/4p_ariel_highlands.jpg differ diff --git a/app/assets/images/maps/4p_biffys_peril.jpg b/app/assets/images/maps/4p_biffys_peril.jpg new file mode 100644 index 0000000..8604ccc Binary files /dev/null and b/app/assets/images/maps/4p_biffys_peril.jpg differ diff --git a/app/assets/images/maps/4p_boramus.jpg b/app/assets/images/maps/4p_boramus.jpg new file mode 100644 index 0000000..cf679e2 Binary files /dev/null and b/app/assets/images/maps/4p_boramus.jpg differ diff --git a/app/assets/images/maps/4p_broken_lands.jpg b/app/assets/images/maps/4p_broken_lands.jpg new file mode 100644 index 0000000..5bcf77f Binary files /dev/null and b/app/assets/images/maps/4p_broken_lands.jpg differ diff --git a/app/assets/images/maps/4p_cape_of_despair.jpg b/app/assets/images/maps/4p_cape_of_despair.jpg new file mode 100644 index 0000000..c7c2eff Binary files /dev/null and b/app/assets/images/maps/4p_cape_of_despair.jpg differ diff --git a/app/assets/images/maps/4p_cold_war.jpg b/app/assets/images/maps/4p_cold_war.jpg new file mode 100644 index 0000000..332ea1c Binary files /dev/null and b/app/assets/images/maps/4p_cold_war.jpg differ diff --git a/app/assets/images/maps/4p_colosseum_of_deadman.jpg b/app/assets/images/maps/4p_colosseum_of_deadman.jpg new file mode 100644 index 0000000..d1d6f25 Binary files /dev/null and b/app/assets/images/maps/4p_colosseum_of_deadman.jpg differ diff --git a/app/assets/images/maps/4p_doom_spiral.jpg b/app/assets/images/maps/4p_doom_spiral.jpg new file mode 100644 index 0000000..d9de122 Binary files /dev/null and b/app/assets/images/maps/4p_doom_spiral.jpg differ diff --git a/app/assets/images/maps/4p_dread_peak.jpg b/app/assets/images/maps/4p_dread_peak.jpg new file mode 100644 index 0000000..e48adbc Binary files /dev/null and b/app/assets/images/maps/4p_dread_peak.jpg differ diff --git a/app/assets/images/maps/4p_eres_badlands.jpg b/app/assets/images/maps/4p_eres_badlands.jpg new file mode 100644 index 0000000..e905b33 Binary files /dev/null and b/app/assets/images/maps/4p_eres_badlands.jpg differ diff --git a/app/assets/images/maps/4p_forgotten_isles.jpg b/app/assets/images/maps/4p_forgotten_isles.jpg new file mode 100644 index 0000000..6102b25 Binary files /dev/null and b/app/assets/images/maps/4p_forgotten_isles.jpg differ diff --git a/app/assets/images/maps/4p_gorhael_crater.jpg b/app/assets/images/maps/4p_gorhael_crater.jpg new file mode 100644 index 0000000..d41f6d0 Binary files /dev/null and b/app/assets/images/maps/4p_gorhael_crater.jpg differ diff --git a/app/assets/images/maps/4p_gurmuns_pass.jpg b/app/assets/images/maps/4p_gurmuns_pass.jpg new file mode 100644 index 0000000..cc964d7 Binary files /dev/null and b/app/assets/images/maps/4p_gurmuns_pass.jpg differ diff --git a/app/assets/images/maps/4p_ice_flow.jpg b/app/assets/images/maps/4p_ice_flow.jpg new file mode 100644 index 0000000..0d54929 Binary files /dev/null and b/app/assets/images/maps/4p_ice_flow.jpg differ diff --git a/app/assets/images/maps/4p_into_the_breach.jpg b/app/assets/images/maps/4p_into_the_breach.jpg new file mode 100644 index 0000000..afdc787 Binary files /dev/null and b/app/assets/images/maps/4p_into_the_breach.jpg differ diff --git a/app/assets/images/maps/4p_janus_savannah.jpg b/app/assets/images/maps/4p_janus_savannah.jpg new file mode 100644 index 0000000..c93b8fc Binary files /dev/null and b/app/assets/images/maps/4p_janus_savannah.jpg differ diff --git a/app/assets/images/maps/4p_janus_savannah_pro.jpg b/app/assets/images/maps/4p_janus_savannah_pro.jpg new file mode 100644 index 0000000..6c9c315 Binary files /dev/null and b/app/assets/images/maps/4p_janus_savannah_pro.jpg differ diff --git a/app/assets/images/maps/4p_lost_relic.jpg b/app/assets/images/maps/4p_lost_relic.jpg new file mode 100644 index 0000000..c287b37 Binary files /dev/null and b/app/assets/images/maps/4p_lost_relic.jpg differ diff --git a/app/assets/images/maps/4p_marconia.jpg b/app/assets/images/maps/4p_marconia.jpg new file mode 100644 index 0000000..913bc45 Binary files /dev/null and b/app/assets/images/maps/4p_marconia.jpg differ diff --git a/app/assets/images/maps/4p_mariza.jpg b/app/assets/images/maps/4p_mariza.jpg new file mode 100644 index 0000000..4917421 Binary files /dev/null and b/app/assets/images/maps/4p_mariza.jpg differ diff --git a/app/assets/images/maps/4p_mountain_trail-1.jpg b/app/assets/images/maps/4p_mountain_trail-1.jpg new file mode 100644 index 0000000..48200cc Binary files /dev/null and b/app/assets/images/maps/4p_mountain_trail-1.jpg differ diff --git a/app/assets/images/maps/4p_mountain_trail.jpg b/app/assets/images/maps/4p_mountain_trail.jpg new file mode 100644 index 0000000..48200cc Binary files /dev/null and b/app/assets/images/maps/4p_mountain_trail.jpg differ diff --git a/app/assets/images/maps/4p_murad_swamplands.jpg b/app/assets/images/maps/4p_murad_swamplands.jpg new file mode 100644 index 0000000..0c5edd7 Binary files /dev/null and b/app/assets/images/maps/4p_murad_swamplands.jpg differ diff --git a/app/assets/images/maps/4p_oderas valley.jpg b/app/assets/images/maps/4p_oderas valley.jpg new file mode 100644 index 0000000..424f940 Binary files /dev/null and b/app/assets/images/maps/4p_oderas valley.jpg differ diff --git a/app/assets/images/maps/4p_oderas_valley.jpg b/app/assets/images/maps/4p_oderas_valley.jpg new file mode 100644 index 0000000..7e4c098 Binary files /dev/null and b/app/assets/images/maps/4p_oderas_valley.jpg differ diff --git a/app/assets/images/maps/4p_panrea_lowlands.jpg b/app/assets/images/maps/4p_panrea_lowlands.jpg new file mode 100644 index 0000000..fbde147 Binary files /dev/null and b/app/assets/images/maps/4p_panrea_lowlands.jpg differ diff --git a/app/assets/images/maps/4p_quatra.jpg b/app/assets/images/maps/4p_quatra.jpg new file mode 100644 index 0000000..8ba939c Binary files /dev/null and b/app/assets/images/maps/4p_quatra.jpg differ diff --git a/app/assets/images/maps/4p_refinery.jpg b/app/assets/images/maps/4p_refinery.jpg new file mode 100644 index 0000000..f7ee215 Binary files /dev/null and b/app/assets/images/maps/4p_refinery.jpg differ diff --git a/app/assets/images/maps/4p_rokclaw_foothills.jpg b/app/assets/images/maps/4p_rokclaw_foothills.jpg new file mode 100644 index 0000000..e8ad7fc Binary files /dev/null and b/app/assets/images/maps/4p_rokclaw_foothills.jpg differ diff --git a/app/assets/images/maps/4p_sad_place.jpg b/app/assets/images/maps/4p_sad_place.jpg new file mode 100644 index 0000000..8750ce8 Binary files /dev/null and b/app/assets/images/maps/4p_sad_place.jpg differ diff --git a/app/assets/images/maps/4p_saints_square.jpg b/app/assets/images/maps/4p_saints_square.jpg new file mode 100644 index 0000000..548a60c Binary files /dev/null and b/app/assets/images/maps/4p_saints_square.jpg differ diff --git a/app/assets/images/maps/4p_sands_of_victory.jpg b/app/assets/images/maps/4p_sands_of_victory.jpg new file mode 100644 index 0000000..1294c5e Binary files /dev/null and b/app/assets/images/maps/4p_sands_of_victory.jpg differ diff --git a/app/assets/images/maps/4p_skerries.jpg b/app/assets/images/maps/4p_skerries.jpg new file mode 100644 index 0000000..a4255a7 Binary files /dev/null and b/app/assets/images/maps/4p_skerries.jpg differ diff --git a/app/assets/images/maps/4p_skerries_pro.jpg b/app/assets/images/maps/4p_skerries_pro.jpg new file mode 100644 index 0000000..2091a19 Binary files /dev/null and b/app/assets/images/maps/4p_skerries_pro.jpg differ diff --git a/app/assets/images/maps/4p_st_mathias_bridge_se.jpg b/app/assets/images/maps/4p_st_mathias_bridge_se.jpg new file mode 100644 index 0000000..8aa6eb1 Binary files /dev/null and b/app/assets/images/maps/4p_st_mathias_bridge_se.jpg differ diff --git a/app/assets/images/maps/4p_starving_equator.jpg b/app/assets/images/maps/4p_starving_equator.jpg new file mode 100644 index 0000000..16da6d8 Binary files /dev/null and b/app/assets/images/maps/4p_starving_equator.jpg differ diff --git a/app/assets/images/maps/4p_tainted_soul.jpg b/app/assets/images/maps/4p_tainted_soul.jpg new file mode 100644 index 0000000..3728759 Binary files /dev/null and b/app/assets/images/maps/4p_tainted_soul.jpg differ diff --git a/app/assets/images/maps/4p_tartarus_center.jpg b/app/assets/images/maps/4p_tartarus_center.jpg new file mode 100644 index 0000000..e686295 Binary files /dev/null and b/app/assets/images/maps/4p_tartarus_center.jpg differ diff --git a/app/assets/images/maps/4p_temple_of_arcanum.jpg b/app/assets/images/maps/4p_temple_of_arcanum.jpg new file mode 100644 index 0000000..80a1986 Binary files /dev/null and b/app/assets/images/maps/4p_temple_of_arcanum.jpg differ diff --git a/app/assets/images/maps/4p_testcake.jpg b/app/assets/images/maps/4p_testcake.jpg new file mode 100644 index 0000000..39febc4 Binary files /dev/null and b/app/assets/images/maps/4p_testcake.jpg differ diff --git a/app/assets/images/maps/4p_tiboraxx.jpg b/app/assets/images/maps/4p_tiboraxx.jpg new file mode 100644 index 0000000..b7e6662 Binary files /dev/null and b/app/assets/images/maps/4p_tiboraxx.jpg differ diff --git a/app/assets/images/maps/4p_torrents.jpg b/app/assets/images/maps/4p_torrents.jpg new file mode 100644 index 0000000..e23acaa Binary files /dev/null and b/app/assets/images/maps/4p_torrents.jpg differ diff --git a/app/assets/images/maps/4p_van_de_mar_mountains.jpg b/app/assets/images/maps/4p_van_de_mar_mountains.jpg new file mode 100644 index 0000000..6d6a2d1 Binary files /dev/null and b/app/assets/images/maps/4p_van_de_mar_mountains.jpg differ diff --git a/app/assets/images/maps/4p_volcanic reaction.jpg b/app/assets/images/maps/4p_volcanic reaction.jpg new file mode 100644 index 0000000..369f410 Binary files /dev/null and b/app/assets/images/maps/4p_volcanic reaction.jpg differ diff --git a/app/assets/images/maps/4p_volcanic_reaction.jpg b/app/assets/images/maps/4p_volcanic_reaction.jpg new file mode 100644 index 0000000..369f410 Binary files /dev/null and b/app/assets/images/maps/4p_volcanic_reaction.jpg differ diff --git a/app/assets/images/maps/4p_white_silence.jpg b/app/assets/images/maps/4p_white_silence.jpg new file mode 100644 index 0000000..827633e Binary files /dev/null and b/app/assets/images/maps/4p_white_silence.jpg differ diff --git a/app/assets/images/maps/5p_aceria_forests.jpg b/app/assets/images/maps/5p_aceria_forests.jpg new file mode 100644 index 0000000..f86d4ff Binary files /dev/null and b/app/assets/images/maps/5p_aceria_forests.jpg differ diff --git a/app/assets/images/maps/5p_eye_of_gorgon.jpg b/app/assets/images/maps/5p_eye_of_gorgon.jpg new file mode 100644 index 0000000..3c4e26d Binary files /dev/null and b/app/assets/images/maps/5p_eye_of_gorgon.jpg differ diff --git a/app/assets/images/maps/5p_gorhjan_jungle.jpg b/app/assets/images/maps/5p_gorhjan_jungle.jpg new file mode 100644 index 0000000..014143e Binary files /dev/null and b/app/assets/images/maps/5p_gorhjan_jungle.jpg differ diff --git a/app/assets/images/maps/5p_red_jungle.jpg b/app/assets/images/maps/5p_red_jungle.jpg new file mode 100644 index 0000000..6747498 Binary files /dev/null and b/app/assets/images/maps/5p_red_jungle.jpg differ diff --git a/app/assets/images/maps/5p_totmachers_prison.jpg b/app/assets/images/maps/5p_totmachers_prison.jpg new file mode 100644 index 0000000..08e9448 Binary files /dev/null and b/app/assets/images/maps/5p_totmachers_prison.jpg differ diff --git a/app/assets/images/maps/5p_vyasastan.jpg b/app/assets/images/maps/5p_vyasastan.jpg new file mode 100644 index 0000000..ecdbf6e Binary files /dev/null and b/app/assets/images/maps/5p_vyasastan.jpg differ diff --git a/app/assets/images/maps/6p_agamar_desert.jpg b/app/assets/images/maps/6p_agamar_desert.jpg new file mode 100644 index 0000000..3bc4d14 Binary files /dev/null and b/app/assets/images/maps/6p_agamar_desert.jpg differ diff --git a/app/assets/images/maps/6p_alvarus.jpg b/app/assets/images/maps/6p_alvarus.jpg new file mode 100644 index 0000000..233a435 Binary files /dev/null and b/app/assets/images/maps/6p_alvarus.jpg differ diff --git a/app/assets/images/maps/6p_aqueduct.jpg b/app/assets/images/maps/6p_aqueduct.jpg new file mode 100644 index 0000000..44cce94 Binary files /dev/null and b/app/assets/images/maps/6p_aqueduct.jpg differ diff --git a/app/assets/images/maps/6p_bloodshed_alley.jpg b/app/assets/images/maps/6p_bloodshed_alley.jpg new file mode 100644 index 0000000..aba5f01 Binary files /dev/null and b/app/assets/images/maps/6p_bloodshed_alley.jpg differ diff --git a/app/assets/images/maps/6p_confrontation.jpg b/app/assets/images/maps/6p_confrontation.jpg new file mode 100644 index 0000000..dc0b97d Binary files /dev/null and b/app/assets/images/maps/6p_confrontation.jpg differ diff --git a/app/assets/images/maps/6p_crossroads.jpg b/app/assets/images/maps/6p_crossroads.jpg new file mode 100644 index 0000000..ebe6ee7 Binary files /dev/null and b/app/assets/images/maps/6p_crossroads.jpg differ diff --git a/app/assets/images/maps/6p_crozius_arcanum.jpg b/app/assets/images/maps/6p_crozius_arcanum.jpg new file mode 100644 index 0000000..9e126ee Binary files /dev/null and b/app/assets/images/maps/6p_crozius_arcanum.jpg differ diff --git a/app/assets/images/maps/6p_desiderata_highlands.jpg b/app/assets/images/maps/6p_desiderata_highlands.jpg new file mode 100644 index 0000000..a7abbd5 Binary files /dev/null and b/app/assets/images/maps/6p_desiderata_highlands.jpg differ diff --git a/app/assets/images/maps/6p_dread_alley.jpg b/app/assets/images/maps/6p_dread_alley.jpg new file mode 100644 index 0000000..50db1ca Binary files /dev/null and b/app/assets/images/maps/6p_dread_alley.jpg differ diff --git a/app/assets/images/maps/6p_fury_island.jpg b/app/assets/images/maps/6p_fury_island.jpg new file mode 100644 index 0000000..4686b6b Binary files /dev/null and b/app/assets/images/maps/6p_fury_island.jpg differ diff --git a/app/assets/images/maps/6p_hyperion_peaks.jpg b/app/assets/images/maps/6p_hyperion_peaks.jpg new file mode 100644 index 0000000..f69a28c Binary files /dev/null and b/app/assets/images/maps/6p_hyperion_peaks.jpg differ diff --git a/app/assets/images/maps/6p_irridene.jpg b/app/assets/images/maps/6p_irridene.jpg new file mode 100644 index 0000000..cebd843 Binary files /dev/null and b/app/assets/images/maps/6p_irridene.jpg differ diff --git a/app/assets/images/maps/6p_jungle_walls.jpg b/app/assets/images/maps/6p_jungle_walls.jpg new file mode 100644 index 0000000..55ca9fa Binary files /dev/null and b/app/assets/images/maps/6p_jungle_walls.jpg differ diff --git a/app/assets/images/maps/6p_kasyr_lutien.jpg b/app/assets/images/maps/6p_kasyr_lutien.jpg new file mode 100644 index 0000000..daadce4 Binary files /dev/null and b/app/assets/images/maps/6p_kasyr_lutien.jpg differ diff --git a/app/assets/images/maps/6p_kaurav_city.jpg b/app/assets/images/maps/6p_kaurav_city.jpg new file mode 100644 index 0000000..0932191 Binary files /dev/null and b/app/assets/images/maps/6p_kaurav_city.jpg differ diff --git a/app/assets/images/maps/6p_kocapb.jpg b/app/assets/images/maps/6p_kocapb.jpg new file mode 100644 index 0000000..84add64 Binary files /dev/null and b/app/assets/images/maps/6p_kocapb.jpg differ diff --git a/app/assets/images/maps/6p_maiden_world.jpg b/app/assets/images/maps/6p_maiden_world.jpg new file mode 100644 index 0000000..1974f49 Binary files /dev/null and b/app/assets/images/maps/6p_maiden_world.jpg differ diff --git a/app/assets/images/maps/6p_mortalis.jpg b/app/assets/images/maps/6p_mortalis.jpg new file mode 100644 index 0000000..ed4be29 Binary files /dev/null and b/app/assets/images/maps/6p_mortalis.jpg differ diff --git a/app/assets/images/maps/6p_nirraein.jpg b/app/assets/images/maps/6p_nirraein.jpg new file mode 100644 index 0000000..e519718 Binary files /dev/null and b/app/assets/images/maps/6p_nirraein.jpg differ diff --git a/app/assets/images/maps/6p_orestan_plains.jpg b/app/assets/images/maps/6p_orestan_plains.jpg new file mode 100644 index 0000000..bc3ce37 Binary files /dev/null and b/app/assets/images/maps/6p_orestan_plains.jpg differ diff --git a/app/assets/images/maps/6p_parmenian_heath.jpg b/app/assets/images/maps/6p_parmenian_heath.jpg new file mode 100644 index 0000000..17d4717 Binary files /dev/null and b/app/assets/images/maps/6p_parmenian_heath.jpg differ diff --git a/app/assets/images/maps/6p_parmenie.jpg b/app/assets/images/maps/6p_parmenie.jpg new file mode 100644 index 0000000..e0b973f Binary files /dev/null and b/app/assets/images/maps/6p_parmenie.jpg differ diff --git a/app/assets/images/maps/6p_pavonian_heartland.jpg b/app/assets/images/maps/6p_pavonian_heartland.jpg new file mode 100644 index 0000000..f2e15e4 Binary files /dev/null and b/app/assets/images/maps/6p_pavonian_heartland.jpg differ diff --git a/app/assets/images/maps/6p_pavonis.jpg b/app/assets/images/maps/6p_pavonis.jpg new file mode 100644 index 0000000..be3a3d3 Binary files /dev/null and b/app/assets/images/maps/6p_pavonis.jpg differ diff --git a/app/assets/images/maps/6p_paynes_retribution.jpg b/app/assets/images/maps/6p_paynes_retribution.jpg new file mode 100644 index 0000000..f59802e Binary files /dev/null and b/app/assets/images/maps/6p_paynes_retribution.jpg differ diff --git a/app/assets/images/maps/6p_principian_badlands.jpg b/app/assets/images/maps/6p_principian_badlands.jpg new file mode 100644 index 0000000..d9cb9e9 Binary files /dev/null and b/app/assets/images/maps/6p_principian_badlands.jpg differ diff --git a/app/assets/images/maps/6p_rhean_floodlands.jpg b/app/assets/images/maps/6p_rhean_floodlands.jpg new file mode 100644 index 0000000..fc03810 Binary files /dev/null and b/app/assets/images/maps/6p_rhean_floodlands.jpg differ diff --git a/app/assets/images/maps/6p_ruined_greatway.jpg b/app/assets/images/maps/6p_ruined_greatway.jpg new file mode 100644 index 0000000..cd00692 Binary files /dev/null and b/app/assets/images/maps/6p_ruined_greatway.jpg differ diff --git a/app/assets/images/maps/6p_shakun_coast.jpg b/app/assets/images/maps/6p_shakun_coast.jpg new file mode 100644 index 0000000..692a82b Binary files /dev/null and b/app/assets/images/maps/6p_shakun_coast.jpg differ diff --git a/app/assets/images/maps/6p_tavesta_wastelands.jpg b/app/assets/images/maps/6p_tavesta_wastelands.jpg new file mode 100644 index 0000000..293c050 Binary files /dev/null and b/app/assets/images/maps/6p_tavesta_wastelands.jpg differ diff --git a/app/assets/images/maps/6p_team_streets_of_vogen.jpg b/app/assets/images/maps/6p_team_streets_of_vogen.jpg new file mode 100644 index 0000000..c22cd40 Binary files /dev/null and b/app/assets/images/maps/6p_team_streets_of_vogen.jpg differ diff --git a/app/assets/images/maps/6p_temple_cyrene.jpg b/app/assets/images/maps/6p_temple_cyrene.jpg new file mode 100644 index 0000000..7191447 Binary files /dev/null and b/app/assets/images/maps/6p_temple_cyrene.jpg differ diff --git a/app/assets/images/maps/6p_temple_cyrene_pro.jpg b/app/assets/images/maps/6p_temple_cyrene_pro.jpg new file mode 100644 index 0000000..0e896d1 Binary files /dev/null and b/app/assets/images/maps/6p_temple_cyrene_pro.jpg differ diff --git a/app/assets/images/maps/6p_testing_grounds.jpg b/app/assets/images/maps/6p_testing_grounds.jpg new file mode 100644 index 0000000..d75ea9b Binary files /dev/null and b/app/assets/images/maps/6p_testing_grounds.jpg differ diff --git a/app/assets/images/maps/6p_thargorum.jpg b/app/assets/images/maps/6p_thargorum.jpg new file mode 100644 index 0000000..6e7e2c2 Binary files /dev/null and b/app/assets/images/maps/6p_thargorum.jpg differ diff --git a/app/assets/images/maps/6p_tristram_plains.jpg b/app/assets/images/maps/6p_tristram_plains.jpg new file mode 100644 index 0000000..da206b0 Binary files /dev/null and b/app/assets/images/maps/6p_tristram_plains.jpg differ diff --git a/app/assets/images/maps/6p_trivian_groves.jpg b/app/assets/images/maps/6p_trivian_groves.jpg new file mode 100644 index 0000000..162231e Binary files /dev/null and b/app/assets/images/maps/6p_trivian_groves.jpg differ diff --git a/app/assets/images/maps/6p_vandea_coast.jpg b/app/assets/images/maps/6p_vandea_coast.jpg new file mode 100644 index 0000000..6895892 Binary files /dev/null and b/app/assets/images/maps/6p_vandea_coast.jpg differ diff --git a/app/assets/images/maps/6p_western_barrens.jpg b/app/assets/images/maps/6p_western_barrens.jpg new file mode 100644 index 0000000..5b81095 Binary files /dev/null and b/app/assets/images/maps/6p_western_barrens.jpg differ diff --git a/app/assets/images/maps/6pteam_streets_of_vogen.jpg b/app/assets/images/maps/6pteam_streets_of_vogen.jpg new file mode 100644 index 0000000..c22cd40 Binary files /dev/null and b/app/assets/images/maps/6pteam_streets_of_vogen.jpg differ diff --git a/app/assets/images/maps/8p_burial_grounds.jpg b/app/assets/images/maps/8p_burial_grounds.jpg new file mode 100644 index 0000000..ff08db5 Binary files /dev/null and b/app/assets/images/maps/8p_burial_grounds.jpg differ diff --git a/app/assets/images/maps/8p_cerulea.jpg b/app/assets/images/maps/8p_cerulea.jpg new file mode 100644 index 0000000..f09a93e Binary files /dev/null and b/app/assets/images/maps/8p_cerulea.jpg differ diff --git a/app/assets/images/maps/8p_clooth-na-bare.jpg b/app/assets/images/maps/8p_clooth-na-bare.jpg new file mode 100644 index 0000000..d530b6f Binary files /dev/null and b/app/assets/images/maps/8p_clooth-na-bare.jpg differ diff --git a/app/assets/images/maps/8p_dantoonvale.jpg b/app/assets/images/maps/8p_dantoonvale.jpg new file mode 100644 index 0000000..c376bd0 Binary files /dev/null and b/app/assets/images/maps/8p_dantoonvale.jpg differ diff --git a/app/assets/images/maps/8p_daturias_pits.jpg b/app/assets/images/maps/8p_daturias_pits.jpg new file mode 100644 index 0000000..66e0f55 Binary files /dev/null and b/app/assets/images/maps/8p_daturias_pits.jpg differ diff --git a/app/assets/images/maps/8p_demes_northlands.jpg b/app/assets/images/maps/8p_demes_northlands.jpg new file mode 100644 index 0000000..8997cf7 Binary files /dev/null and b/app/assets/images/maps/8p_demes_northlands.jpg differ diff --git a/app/assets/images/maps/8p_doom_chamber.jpg b/app/assets/images/maps/8p_doom_chamber.jpg new file mode 100644 index 0000000..c13cde2 Binary files /dev/null and b/app/assets/images/maps/8p_doom_chamber.jpg differ diff --git a/app/assets/images/maps/8p_forbidden_jungle.jpg b/app/assets/images/maps/8p_forbidden_jungle.jpg new file mode 100644 index 0000000..e2f8b88 Binary files /dev/null and b/app/assets/images/maps/8p_forbidden_jungle.jpg differ diff --git a/app/assets/images/maps/8p_fort atlantis.jpg b/app/assets/images/maps/8p_fort atlantis.jpg new file mode 100644 index 0000000..9107ccd Binary files /dev/null and b/app/assets/images/maps/8p_fort atlantis.jpg differ diff --git a/app/assets/images/maps/8p_jalaganda_lowlands.jpg b/app/assets/images/maps/8p_jalaganda_lowlands.jpg new file mode 100644 index 0000000..02ae66e Binary files /dev/null and b/app/assets/images/maps/8p_jalaganda_lowlands.jpg differ diff --git a/app/assets/images/maps/8p_kier_harrad.jpg b/app/assets/images/maps/8p_kier_harrad.jpg new file mode 100644 index 0000000..077f7c7 Binary files /dev/null and b/app/assets/images/maps/8p_kier_harrad.jpg differ diff --git a/app/assets/images/maps/8p_lost_hope.jpg b/app/assets/images/maps/8p_lost_hope.jpg new file mode 100644 index 0000000..c685951 Binary files /dev/null and b/app/assets/images/maps/8p_lost_hope.jpg differ diff --git a/app/assets/images/maps/8p_marinus.jpg b/app/assets/images/maps/8p_marinus.jpg new file mode 100644 index 0000000..2e613d8 Binary files /dev/null and b/app/assets/images/maps/8p_marinus.jpg differ diff --git a/app/assets/images/maps/8p_morholt_range.jpg b/app/assets/images/maps/8p_morholt_range.jpg new file mode 100644 index 0000000..c29d338 Binary files /dev/null and b/app/assets/images/maps/8p_morholt_range.jpg differ diff --git a/app/assets/images/maps/8p_morriah_coast.jpg b/app/assets/images/maps/8p_morriah_coast.jpg new file mode 100644 index 0000000..10646a6 Binary files /dev/null and b/app/assets/images/maps/8p_morriah_coast.jpg differ diff --git a/app/assets/images/maps/8p_oasis_of_sharr.jpg b/app/assets/images/maps/8p_oasis_of_sharr.jpg new file mode 100644 index 0000000..ca7b563 Binary files /dev/null and b/app/assets/images/maps/8p_oasis_of_sharr.jpg differ diff --git a/app/assets/images/maps/8p_penal_colony.jpg b/app/assets/images/maps/8p_penal_colony.jpg new file mode 100644 index 0000000..7a58469 Binary files /dev/null and b/app/assets/images/maps/8p_penal_colony.jpg differ diff --git a/app/assets/images/maps/8p_rhean_jungle.jpg b/app/assets/images/maps/8p_rhean_jungle.jpg new file mode 100644 index 0000000..45b79b2 Binary files /dev/null and b/app/assets/images/maps/8p_rhean_jungle.jpg differ diff --git a/app/assets/images/maps/8p_thurabis_plateau.jpg b/app/assets/images/maps/8p_thurabis_plateau.jpg new file mode 100644 index 0000000..336d0de Binary files /dev/null and b/app/assets/images/maps/8p_thurabis_plateau.jpg differ diff --git a/app/assets/images/maps/8p_verdant_isles.jpg b/app/assets/images/maps/8p_verdant_isles.jpg new file mode 100644 index 0000000..4379bd1 Binary files /dev/null and b/app/assets/images/maps/8p_verdant_isles.jpg differ diff --git a/app/assets/images/maps/[tp_mod]4p_ice_flow.jpg b/app/assets/images/maps/[tp_mod]4p_ice_flow.jpg new file mode 100644 index 0000000..b586c85 Binary files /dev/null and b/app/assets/images/maps/[tp_mod]4p_ice_flow.jpg differ diff --git a/app/assets/images/maps/[tp_mod]edemus_gamble.jpg b/app/assets/images/maps/[tp_mod]edemus_gamble.jpg new file mode 100644 index 0000000..2e507fc Binary files /dev/null and b/app/assets/images/maps/[tp_mod]edemus_gamble.jpg differ diff --git a/app/assets/images/maps/[tp_mod]galenas_crusade.jpg b/app/assets/images/maps/[tp_mod]galenas_crusade.jpg new file mode 100644 index 0000000..57ee418 Binary files /dev/null and b/app/assets/images/maps/[tp_mod]galenas_crusade.jpg differ diff --git a/app/assets/images/maps/[tp_mod]jungle_morning.jpg b/app/assets/images/maps/[tp_mod]jungle_morning.jpg new file mode 100644 index 0000000..271cd16 Binary files /dev/null and b/app/assets/images/maps/[tp_mod]jungle_morning.jpg differ diff --git a/app/assets/images/maps/[tp_mod]light_brigade.jpg b/app/assets/images/maps/[tp_mod]light_brigade.jpg new file mode 100644 index 0000000..220e8a0 Binary files /dev/null and b/app/assets/images/maps/[tp_mod]light_brigade.jpg differ diff --git a/app/assets/images/maps/[tp_mod]meeting_of_minds.jpg b/app/assets/images/maps/[tp_mod]meeting_of_minds.jpg new file mode 100644 index 0000000..944c40b Binary files /dev/null and b/app/assets/images/maps/[tp_mod]meeting_of_minds.jpg differ diff --git a/app/assets/images/maps/[tp_mod]quests_triumph.jpg b/app/assets/images/maps/[tp_mod]quests_triumph.jpg new file mode 100644 index 0000000..01e2eae Binary files /dev/null and b/app/assets/images/maps/[tp_mod]quests_triumph.jpg differ diff --git a/app/assets/images/maps/[tp_mod]shrine_of_excellion.jpg b/app/assets/images/maps/[tp_mod]shrine_of_excellion.jpg new file mode 100644 index 0000000..7365a62 Binary files /dev/null and b/app/assets/images/maps/[tp_mod]shrine_of_excellion.jpg differ diff --git a/app/assets/images/maps/allMaps.json b/app/assets/images/maps/allMaps.json new file mode 100644 index 0000000..87a57f7 --- /dev/null +++ b/app/assets/images/maps/allMaps.json @@ -0,0 +1,155 @@ +{ + "2p_vile_reef": "Vile Reef (2)", + "2p_eden": "Eden (2)", + "2p_frostbite_river": "Frostbite River (2)", + "2p_quests_triumph_pro":"Quests Triumph Pro (2)", + "2p_light_brigade": "Light Brigade (2)", + "2p_quests_triumph": "Quests Triumph (2)", + "2p_light_brigade_pro":"Light Brigade Pro (2)", + "2p_bridge_too_far":"Bridge Too Far (2)", + "2p_railway": "Railway (2)", + "2p_battle_marshes": "Battle Marshes (2)", + "2p_colosseum_suicide_pro":"Colosseum Suicide Pro (2)", + "2p_blood_river": "Blood River (2)", + "2p_blood_river_remix": "Blood River Remix (2)", + "2p_antiga_bay":"Antiga Bay (2)", + "2p_outer_reaches": "Outer Reaches (2)", + "2p_moonbase": "Moonbase (2)", + "2p_deadly_fun_archeology": "Deadly Fun Archeology (2)", + "2p_sugaroasis": "SugarOasis (2)", + "2p_tower_ruins": "Tower Ruins (2)", + "2p_meeting_of_minds_pro_lis":"Meeting of Minds Pro (2)", + "2p_jungle_morning": "Jungle Morning (2)", + "2p_colosseum_suicide": "Colosseum Suicide (2)", + "2p_shrine_of_excellion": "Shrine of Excellion (2)", + "2p_shrine_of_excellion_[Rem]": "Shrine of excellion [Rem] (2)", + "2p_meeting_of_minds_pro": "Meeting of Minds Pro (2)", + "2p_emerald_river": "Emerald River (2)", + "2p_haines_demise": "Haines Demise (2)", + "2p_meeting_of_minds_pro_lis_without_1p": "Meeting of Minds Pro (2)", + "2p_meeting_of_minds": "Meeting of Minds (2)", + "2p_bloody hell": "Bloody Hell (2)", + "2p_bloody_hell": "Bloody Hell (2)", + "2p_abandon_all_hope": "Abandon All Hope (2)", + "2p_faceoff": "Faceoff (2)", + "2p_river_bed": "Riverbed (2)", + "2p_tranquilitys_end": "Tranquilitys End (2)", + "2p_tranquilitys_end_[Rem]": "Tranquility's End [Rem] (2)", + "2p_tranquilitys_end_pro": "Tranquilitys End Pro (2)", + "2p_deadmans_crossing": "Deadman's Crossing (2)", + "2p_tainted_pair": "Tainted Pair (2)", + "2p_emperors_valley": "Emperors Valley (2)", + "2p_titan_fall": "Titans Fall (2)", + "2p_titan_fall_[Rem]": "Titan's Fall [Rem] (2)", + "2p_fear": "Fear (2)", + "2p_edemus_gamble": "Edemus Gamble (2)", + "2p_tazins_folly": "Tazins Folly (2)", + "2p_volatile_ground": "Volatile Ground (2)", + "2p_hellfire_canyon": "Hellfire Canyon (2)", + "2p_absolute_zero": "Absolute Zero (2)", + "2p_meeting_of_minds_pro_5p": "Meeting of Minds Pro (2)", + "2p_short_below_zero": "Short Below Zero (2)", + "2p_fata_morgana": "Fata Morga (2)", + "2p_fallen_city": "Fallen City (2)", + "2p_velvet_duress": "Velvet Duress (2)", + "2p_fraziersdemise": "Fraziers Demise (2)", + "2p_vortex_plateau": "Vortex Plateau (2)", + "2p_valley_of_khorne": "Valley of Khorne (2)", + "2p_tiboraxx":"Tiboraxx (2)", + "3p_fortress": "Fortress (3)", + "4p_marconia": "Marconia (4)", + "4p_quatra": "Quatra (4)", + "4p_tartarus_center": "Tartarus Center (4)", + "4p_rokclaw_foothills": "Rokclaw Foothills (4)", + "4p_saints_square": "Saint's Square (4)", + "4p_tiboraxx": "Tiboraxx (4)", + "4p_sad_place": "Sad Place (4)", + "4p_biffys_peril": "Biffy's Peril (4)", + "4p_mountain_trail": "Mountain Trail (4)", + "4p_tainted_soul": "Tainted Place (4)", + "4p_into_the_breach": "Into the Breach (4)", + "4p_cape_of_despair": "Cape of Despair (4)", + "4p_ice_flow": "Ice Flow (4)", + "4p_doom_spiral": "Doom Spiral (4)", + "4p_gorhael_crater": "GorHael Crater (4)", + "4p_ariel_highlands": "Ariel Highlands (4)", + "4p_forgotten_isles": "Forgotten Isles (4)", + "4p_gurmuns_pass": "Gurmuns Pass (4)", + "4p_temple_of_arcanum": "Temple of Cyrene (6)", + "4p_murad_swamplands": "Murad Swamplands (4)", + "4p_dread_peak": "Dread Peak (4)", + "4p_eres_badlands": "Eres Badlands (4)", + "4p_janus_savannah": "Janus Savannah (4)", + "4p_janus_savannah_pro": "Janus Savannah Pro (4)", + "4p_panrea_lowlands": "Panrea Lowlands (4)", + "4p_skerries": "The Skerries (4)", + "4p_skerries_pro": "The Skerries Pro (4)", + "4p_torrents": "Torrents (4)", + "4p_van_de_mar_mountains": "Van de Mar Mountains (4)", + "4p_sands_of_victory":"Sands of Victory (4)", + "4p_antiga_bay":"Antiga Bay (4)", + "4p_volcanic_reaction": "Volcanic Reaction (4)", + "4p_broken_lands": "Broken Lands (4)", + "5p_vyasastan": "Vyasastan (5)", + "5p_totmachers_prison": "Totmachers Prison (5)", + "5p_red_jungle": "Red Jungle (5)", + "5p_aceria_forests": "Aceria Forests (5)", + "5p_eye_of_gorgon.jpg": "Eye of gorgon (5)", + "6p_parmenie": "Parmenie (6)", + "6p_temple_cyrene": "Temple of Cyrene (6)", + "6p_nirraein": "Nirraein (6)", + "6p_shakun_coast": "Shakun Coast (6)", + "6pteam_streets_of_vogen": "Streets of Vogen (6)", + "6p_tristram_plains": "Tristram Plains (6)", + "6p_pavonis": "Pavonis (6)", + "6p_pavonian_heartland": "Pavonian Heartland (6)", + "6p_paynes_retribution": "Paynes Retribution (6)", + "6p_thargorum": "Thargorum (6)", + "6p_parmenian_heath": "Parmenian Heath (6)", + "6p_jungle_walls": "Jungle Walls (6)", + "6p_trivian_groves": "Trivian Groves (6)", + "6p_kasyr_lutien": "Kasyr Lutien (6)", + "6p_mortalis": "Mortalis (6)", + "6p_irridene": "Irridene (6)", + "6p_fury_island": "Fury Island (6)", + "6p_confrontation": "Confrontation (6)", + "6p_crozius_arcanum": "Crozius Arcanum (6)", + "6p_dread_alley": "Dread Alley (6)", + "6p_vandea_coast": "Vandean Coast (6)", + "6p_orestan_plains": "Orestan Plains (6)", + "6p_western_barrens": "Western Barrens (6)", + "6p_principian_badlands": "Principian Badlands (6)", + "6p_rhean_floodlands": "Rhean Floodlands (6)", + "6p_hyperion_peaks": "Hyperion Peaks (6)", + "6p_crossroads": "Crossroads (6)", + "6p_testing_grounds": "Testing Grounds (6)", + "6p_bloodshed_alley": "Bloodshed Alley (6)", + "6p_agamar_desert": "Agamar Desert (6)", + "6p_alvarus": "Alvarus (6)", + "6p_kaurav_city": "Kaurav City (6)", + "8p_forbidden_jungle": "Forbidden Jungle (8)", + "8p_rhean_jungle": "Rhean Jungle (8)", + "8p_thurabis_plateau": "ThurAbis Plateau (8)", + "8p_penal_colony": "Penal Colony (8)", + "8p_morriah_coast": "Morriah Coast (8)", + "8p_demes_northlands": "Demes Northlands (8)", + "8p_morholt_range": "Morholt Range (8)", + "8p_clooth-na-bare": "Monse (8)", + "8p_oasis_of_sharr": "Oasis of Sharr (8)", + "8p_doom_chamber": "Doom Chamber (8)", + "8p_daturias_pits": "Daturias Pits (8)", + "8p_burial_grounds": "Burial Grounds (8)", + "8p_jalaganda_lowlands": "Jalaganda Lowlands (8)", + "8p_lost_hope": "Lost Hope (8)", + "8p_cerulea": "Cerulea (8)", + "8p_kier_harrad": "Kier Harrad (8)", + "[tp_mod]4p_ice_flow": "[TP MOD]Ice Flow (4)", + "[tp_mod]edemus_gamble": "[TP MOD] Edemus Gamble (2)", + "[tp_mod]galenas_crusade": "[TP MOD] Galenas Crusade (2)", + "[tp mod]jungle_morning": "[TP MOD] Jungle Morning (2)", + "[tp_mod]light_brigade": "[TP MOD] Light Brigade (2)", + "[tp_mod]quests_triumph": "[TP MOD] Quests Triumph (2)", + "[tp_mod]shrine_of_excellion": "[TP MOD] Shrine of Excellion (2)", + "[tp_mod]shrine of excellion": "[TP MOD] Shrine of Excellion (2)", + "[tp_mod]meeting_of_minds": "[TP MOD] Meeting of Minds (2)" +} \ No newline at end of file diff --git a/app/assets/images/maps/default.jpg b/app/assets/images/maps/default.jpg new file mode 100644 index 0000000..83565ce Binary files /dev/null and b/app/assets/images/maps/default.jpg differ diff --git a/app/assets/images/raceIcons/chaos.png b/app/assets/images/raceIcons/chaos.png new file mode 100644 index 0000000..3655aa4 Binary files /dev/null and b/app/assets/images/raceIcons/chaos.png differ diff --git a/app/assets/images/raceIcons/darkEldar.png b/app/assets/images/raceIcons/darkEldar.png new file mode 100644 index 0000000..390f384 Binary files /dev/null and b/app/assets/images/raceIcons/darkEldar.png differ diff --git a/app/assets/images/raceIcons/eldar.png b/app/assets/images/raceIcons/eldar.png new file mode 100644 index 0000000..c1cb79c Binary files /dev/null and b/app/assets/images/raceIcons/eldar.png differ diff --git a/app/assets/images/raceIcons/ig.png b/app/assets/images/raceIcons/ig.png new file mode 100644 index 0000000..d4d3dee Binary files /dev/null and b/app/assets/images/raceIcons/ig.png differ diff --git a/app/assets/images/raceIcons/necron.png b/app/assets/images/raceIcons/necron.png new file mode 100644 index 0000000..94d52d7 Binary files /dev/null and b/app/assets/images/raceIcons/necron.png differ diff --git a/app/assets/images/raceIcons/ork.png b/app/assets/images/raceIcons/ork.png new file mode 100644 index 0000000..7bd47d9 Binary files /dev/null and b/app/assets/images/raceIcons/ork.png differ diff --git a/app/assets/images/raceIcons/sob.png b/app/assets/images/raceIcons/sob.png new file mode 100644 index 0000000..13a5ad6 Binary files /dev/null and b/app/assets/images/raceIcons/sob.png differ diff --git a/app/assets/images/raceIcons/spaceMarine.png b/app/assets/images/raceIcons/spaceMarine.png new file mode 100644 index 0000000..acc18cc Binary files /dev/null and b/app/assets/images/raceIcons/spaceMarine.png differ diff --git a/app/assets/images/raceIcons/tau.png b/app/assets/images/raceIcons/tau.png new file mode 100644 index 0000000..8426e61 Binary files /dev/null and b/app/assets/images/raceIcons/tau.png differ diff --git a/app/assets/javascripts/index.js b/app/assets/javascripts/index.js new file mode 100644 index 0000000..244ee75 --- /dev/null +++ b/app/assets/javascripts/index.js @@ -0,0 +1,154 @@ + +var updateDecider; + +var ws; + +window.onload = function() { + + 'use strict'; + + function getRandomInt(max) { + return Math.floor(Math.random() * Math.floor(max)); + } + + var DeciderLobby = Backbone.View.extend({ + + lobbies: undefined, + + initialize: function (number) { + console.log(number); + this.elCount = number; + }, + + template: _.template('<%= title %>' + + '<%= title %>' + + '<%= content %>'), + + events: { + "click .title": "check" // Обработчик клика на кнопке "Проверить" + }, + + check: function () { + this.elCount = "azaz" + getRandomInt(100000000); + this.render(); + }, + + render: function() { + console.log("render"); + this.$el.html(this.template({title: "Vasyan " + this.elCount, content: "noob"})); + console.log(this.$el); + return this; + } + }); + + var LobbyList = Backbone.View.extend({ + + lobbies: undefined, + + initialize: function () { + this.elCount = 0; + }, + + template: _.template('\n' + + ' \n' + + ' \n' + + ' \n' + + '
HostStatusAction
'), + + events: { + "click .title": "check" // Обработчик клика на кнопке "Проверить" + }, + + check: function () { + this.elCount = "azaz" + getRandomInt(100000000); + this.render(); + }, + + render: function() { + this.$el.html(this.template()); + var lobbiesAsTableRows = _.reduce(this.lobbies, function (memo, lobby) { + console.log(lobby, memo); + return memo + '\n' + + ' '+ lobby.lobbyName +'\n' + + ' '+ lobby.status +'\n' + + ' Action' + + ''; + }, ""); + $("#deciderList tbody", this.el).append(lobbiesAsTableRows); + return this; + } + }); + + var lobbyList = new LobbyList; + $("#lobbiesList").append(lobbyList.el); + lobbyList.render(); + + $("#createDecider").click(function(event) { + event.preventDefault(); + ws.send(JSON.stringify({ + type: "createDecider" + })); + }); + + $("#updateDecider").click(function(event) { + event.preventDefault(); + ws.send(JSON.stringify({ + type: "updateDecider" + })); + }); + + $("#getAllUsers").click(function(event) { + event.preventDefault(); + ws.send(JSON.stringify({ + type: "getLobbies" + })); + }); + + if($.cookie('user')=== undefined){ + result = prompt("Введите ник", ); + $.cookie('user', result, { expires: 7 }); + }else{ + console.log("Welcome, " + $.cookie('user')); + } + + ws = new WebSocket($("body").data("ws-url")); + ws.onmessage = function(event) { + var message; + message = JSON.parse(event.data); + switch (message.type) { + case "maps": + return updateDecider(message); + default: + case "lobbies": + lobbyList.lobbies = message.lobbies; + lobbyList.render(); + return console.log(message); + } + }; + ws.onopen = function () { + ws.send(JSON.stringify({ + type: "userName", + name: $.cookie('user') + })); + } + + let timerId = setInterval(() => + ws.send(JSON.stringify({ + type: "getLobbies" + })), 2000); +} + +function changeNick() { + result = prompt("Введите ник", $.cookie('user')); + $.cookie('user', result, { expires: 7 }); + ws.send(JSON.stringify({ + type: "userName", + name: $.cookie('user') + })); +} + + +updateDecider = function(message) { + console.log(message.deciderMaps); +}; + diff --git a/app/assets/stylesheets/main.less b/app/assets/stylesheets/main.less new file mode 100644 index 0000000..c8fa81c --- /dev/null +++ b/app/assets/stylesheets/main.less @@ -0,0 +1,124 @@ +.perspective (@value) { + -webkit-perspective: @value; + -moz-perspective: @value; + perspective: @value; +} + +.transform (@value) { + -webkit-transform: rotateY(@value); + -moz-transform: rotateY(@value); + transform: rotateY(@value); +} + +.border-radius (@value) { + -webkit-border-radius: @value; + -moz-border-radius: @value; + border-radius: @value; +} + + +body { + margin-top: 50px; +} + +.flip-container { + .perspective(1000); + margin-bottom: 20px; + &:hover .flipper { + .transform(10deg); + } + &.flipped .flipper { + .transform(180deg); + } +} + +.flipper { + height: 250px; + + background-color: #fafafa; + border: 1px solid #ddd; + + .border-radius(4px); + + cursor: hand; + cursor: pointer; + + -webkit-transition: 0.6s; + -moz-transition: 0.6s; + transition: 0.6s; + + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + transform-style: preserve-3d; + + &:after { + content: attr(data-content); + position: absolute; + top: -1px; + left: -1px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + background-color: #ffffff; + border: 1px solid #ddd; + color: #9da0a4; + + .border-radius(4px 0 4px 0); + } +} + +.chart-holder, .details-holder { + position: absolute; + width: 100%; + height: 250px; + top: 0px; + left: 0px; + + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + backface-visibility: hidden; +} + +.chart-holder { + z-index: 2; + & p { + position: absolute; + bottom: 7px; + right: 20px; + font-size: 10px; + color: #aaaaaa; + font-style: italic; + } +} + +.details-holder { + .transform(180deg); + text-align: center; + z-index: 1; + & h4 { + padding: 20px; + } + & .progress { + padding: 20px; + background: none; + border: none; + + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } + & img { + height: 128px; + width: 128px; + } +} + +.chart { + position: relative; + width: 920px; + height: 210px; + margin-top: 30px; + margin-bottom: 10px; + margin-left: 10px; + margin-right: 10px; +} \ No newline at end of file diff --git a/app/controllers/HomeController.scala b/app/controllers/HomeController.scala new file mode 100644 index 0000000..b34ec44 --- /dev/null +++ b/app/controllers/HomeController.scala @@ -0,0 +1,189 @@ +package controllers + +import javax.inject._ +import actors._ +import akka.NotUsed +import akka.actor._ +import akka.event.Logging +import akka.pattern.ask +import akka.stream._ +import akka.stream.scaladsl._ +import akka.util.Timeout +import org.reactivestreams.Publisher +import play.api.libs.json._ +import play.api.mvc._ + +import scala.concurrent.duration._ +import scala.concurrent.{ExecutionContext, Future} + +/** + * This class creates the actions and the websocket needed. + */ +@Singleton +class HomeController @Inject()(cc: ControllerComponents) extends AbstractController(cc) { + + implicit val actorSystem: ActorSystem = ActorSystem() + implicit val ec: ExecutionContext = defaultExecutionContext + + // Use a direct reference to SLF4J + private val logger = org.slf4j.LoggerFactory.getLogger("controllers.HomeController") + + val lobbiesActror: ActorRef = actorSystem.actorOf(Props[LobbiesActor]()) + val userParentActor: ActorRef = actorSystem.actorOf(Props(classOf[UserParentActor], actorSystem)) + + // Home page that renders template + def index() = Action { implicit request => + Ok(views.html.index()) + } + + /** + * Creates a websocket. `acceptOrResult` is preferable here because it returns a + * Future[Flow], which is required internally. + * + * @return a fully realized websocket. + */ + def ws: WebSocket = WebSocket.acceptOrResult[JsValue, JsValue] { + case rh if sameOriginCheck(rh) => + wsFutureFlow(rh).map { flow => + Right(flow) + }.recover { + case e: Exception => + logger.error("Cannot create websocket", e) + val jsError = Json.obj("error" -> "Cannot create websocket") + val result = Results.InternalServerError(jsError) + Left(result) + } + + case rejected => + logger.error(s"Request ${rejected} failed same origin check") + Future.successful { + Left(Results.Forbidden("forbidden")) + } + } + + /** + * Checks that the WebSocket comes from the same origin. This is necessary to protect + * against Cross-Site WebSocket Hijacking as WebSocket does not implement Same Origin Policy. + * + * See https://tools.ietf.org/html/rfc6455#section-1.3 and + * http://blog.dewhurstsecurity.com/2013/08/30/security-testing-html5-websockets.html + */ + def sameOriginCheck(rh: RequestHeader): Boolean = { + rh.headers.get("Origin") match { + case Some(originValue) if originMatches(originValue) => + logger.debug(s"originCheck: originValue = $originValue") + true + + case Some(badOrigin) => + logger.error(s"originCheck: rejecting request because Origin header value ${badOrigin} is not in the same origin") + false + + case None => + logger.error("originCheck: rejecting request because no Origin header found") + false + } + } + + /** + * Returns true if the value of the Origin header contains an acceptable value. + */ + def originMatches(origin: String): Boolean = { + origin.contains("localhost:9000") || origin.contains("localhost:19001") + } + + /** + * Creates a Future containing a Flow of JsValue in and out. + */ + private def wsFutureFlow(request: RequestHeader): Future[Flow[JsValue, JsValue, NotUsed]] = { + // create an actor ref source and associated publisher for sink + val (webSocketOut: ActorRef, webSocketIn: Publisher[JsValue]) = createWebSocketConnections() + + // Create a user actor off the request id and attach it to the source + val userActorFuture = createUserActor(request.id.toString, webSocketOut) + + // Once we have an actor available, create a flow... + userActorFuture.map { userActor => + createWebSocketFlow(webSocketIn, userActor) + } + } + + /** + * Creates a materialized flow for the websocket, exposing the source and sink. + * + * @return the materialized input and output of the flow. + */ + def createWebSocketConnections(): (ActorRef, Publisher[JsValue]) = { + + // Creates a source to be materialized as an actor reference. + val source: Source[JsValue, ActorRef] = { + // If you want to log on a flow, you have to use a logging adapter. + // http://doc.akka.io/docs/akka/2.4.4/scala/logging.html#SLF4J + val logging = Logging(actorSystem.eventStream, logger.getName) + + // Creating a source can be done through various means, but here we want + // the source exposed as an actor so we can send it messages from other + // actors. + Source.actorRef[JsValue](10, OverflowStrategy.dropTail).log("actorRefSource")(logging) + } + + // Creates a sink to be materialized as a publisher. Fanout is false as we only want + // a single subscriber here. + val sink: Sink[JsValue, Publisher[JsValue]] = Sink.asPublisher(fanout = false) + + // Connect the source and sink into a flow, telling it to keep the materialized values, + // and then kicks the flow into existence. + source.toMat(sink)(Keep.both).run() + } + + /** + * Creates a flow of events from the websocket to the user actor. + * + * When the flow is terminated, the user actor is no longer needed and is stopped. + * + * @param userActor the user actor receiving websocket events. + * @param webSocketIn the "read" side of the websocket, that publishes JsValue to UserActor. + * @return a Flow of JsValue in both directions. + */ + def createWebSocketFlow(webSocketIn: Publisher[JsValue], userActor: ActorRef): Flow[JsValue, JsValue, NotUsed] = { + // http://doc.akka.io/docs/akka/current/scala/stream/stream-flows-and-basics.html#stream-materialization + // http://doc.akka.io/docs/akka/current/scala/stream/stream-integrations.html#integrating-with-actors + + // source is what comes in: browser ws events -> play -> publisher -> userActor + // sink is what comes out: userActor -> websocketOut -> play -> browser ws events + val flow = { + val sink = Sink.actorRef(userActor, akka.actor.Status.Success(())) + val source = Source.fromPublisher(webSocketIn) + Flow.fromSinkAndSource(sink, source) + } + + // Unhook the user actor when the websocket flow terminates + // http://doc.akka.io/docs/akka/current/scala/stream/stages-overview.html#watchTermination + val flowWatch: Flow[JsValue, JsValue, NotUsed] = flow.watchTermination() { (_, termination) => + termination.foreach { done => + logger.info(s"Terminating actor $userActor") + lobbiesActror.tell(UnWatchAllLobbies, userActor) + actorSystem.stop(userActor) + } + NotUsed + } + + flowWatch + } + + /** + * Creates a user actor with a given name, using the websocket out actor for output. + * + * @param name the name of the user actor. + * @param webSocketOut the "write" side of the websocket, that the user actor sends JsValue to. + * @return a user actor for this ws connection. + */ + private def createUserActor(name: String, webSocketOut: ActorRef): Future[ActorRef] = { + // Use guice assisted injection to instantiate and configure the child actor. + val userActorFuture = { + implicit val timeout = Timeout(100.millis) + (userParentActor ? UserParentActor.Create(name, webSocketOut, lobbiesActror)).mapTo[ActorRef] + } + userActorFuture + } + +} diff --git a/app/views/index.scala.html b/app/views/index.scala.html new file mode 100644 index 0000000..953afe4 --- /dev/null +++ b/app/views/index.scala.html @@ -0,0 +1,44 @@ +@()(implicit r: Request[_]) + + + + + Reactive Stock News Dashboard + + + + + + + + + + + + + +
+
+
+ + + +
+ +
+ +
+
+
+
+ + + diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..7fef6fb --- /dev/null +++ b/build.sbt @@ -0,0 +1,29 @@ +name := "decider-2" + +version := "1.0-SNAPSHOT" + +lazy val root = (project in file(".")).enablePlugins(PlayScala) + +scalaVersion := "2.12.8" + + +// scalaz-bintray resolver needed for specs2 library +resolvers += "scalaz-bintray" at "https://dl.bintray.com/scalaz/releases" + +libraryDependencies += ws + +libraryDependencies += "org.webjars" % "flot" % "0.8.3" +libraryDependencies += "org.webjars" % "bootstrap" % "3.3.6" +libraryDependencies += "org.webjars" % "jquery-cookie" % "1.4.1-1" +libraryDependencies += "org.webjars.npm" % "underscore" % "1.11.0" +libraryDependencies += "org.webjars" % "backbonejs" % "1.3.3" + +libraryDependencies += guice +libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2" + +libraryDependencies += "com.typesafe.play" %% "play" % "2.8.2" +libraryDependencies += "com.typesafe.play" %% "play-json" % "2.9.1" + +lazy val akkaVersion = "2.4.18" +libraryDependencies += "com.typesafe.akka" %% "akka-testkit" % akkaVersion % Test +libraryDependencies += "com.typesafe.akka" %% "akka-stream-testkit" % akkaVersion % Test diff --git a/conf/application.conf b/conf/application.conf new file mode 100644 index 0000000..e1c5fb9 --- /dev/null +++ b/conf/application.conf @@ -0,0 +1,12 @@ +# https://www.playframework.com/documentation/latest/SecurityHeaders +# Connect to localhost:9000 for content security policy on websockets +play.filters.headers { + contentSecurityPolicy = "connect-src 'self' ws://localhost:9000" +} + +# https://www.playframework.com/documentation/latest/AllowedHostsFilter +# Allow requests to localhost:9000. +play.filters.hosts { + allowed = ["localhost:9000"] +} + diff --git a/conf/logback.xml b/conf/logback.xml new file mode 100644 index 0000000..970fc68 --- /dev/null +++ b/conf/logback.xml @@ -0,0 +1,33 @@ + + + + + + logs/application.log + + %date [%level] from %logger in %thread - %message%n%xException + + + + + + %coloredLevel %logger{15} - [%marker] %message%n%xException{10} + + + + + + + + + + + + + + + + + + + diff --git a/conf/routes b/conf/routes new file mode 100644 index 0000000..f0442d1 --- /dev/null +++ b/conf/routes @@ -0,0 +1,9 @@ +# Routes +# This file defines all application routes (Higher priority routes first) +# ~~~~ + +GET / controllers.HomeController.index +GET /ws controllers.HomeController.ws + +# Map static resources from the /public folder to the /assets URL path +GET /assets/*file controllers.Assets.at(path="/public", file) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..01b8bf6 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..89dba2d --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..c0bab04 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.2.8 diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 0000000..426577a --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1,4 @@ +// Use the Play sbt plugin for Play projects +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.7.0") + +addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.1.2") diff --git a/public/images/buy.png b/public/images/buy.png new file mode 100644 index 0000000..ccb20e5 Binary files /dev/null and b/public/images/buy.png differ diff --git a/public/images/favicon.png b/public/images/favicon.png new file mode 100644 index 0000000..c7d92d2 Binary files /dev/null and b/public/images/favicon.png differ diff --git a/public/images/hold.png b/public/images/hold.png new file mode 100644 index 0000000..2645b27 Binary files /dev/null and b/public/images/hold.png differ diff --git a/public/images/sell.png b/public/images/sell.png new file mode 100644 index 0000000..294cc20 Binary files /dev/null and b/public/images/sell.png differ diff --git a/scripts/test-gradle b/scripts/test-gradle new file mode 100644 index 0000000..02a6e97 --- /dev/null +++ b/scripts/test-gradle @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +# Using cut because TRAVIS_SCALA_VERSION is the full Scala +# version (for example 2.12.4), but Gradle expects just the +# binary version (for example 2.12) +scala_binary_version=$(echo $TRAVIS_SCALA_VERSION | cut -c1-4) + +echo "+------------------------------+" +echo "| Executing tests using Gradle |" +echo "+------------------------------+" +./gradlew -Dscala.binary.version=$scala_binary_version check -i --stacktrace diff --git a/scripts/test-sbt b/scripts/test-sbt new file mode 100644 index 0000000..886637e --- /dev/null +++ b/scripts/test-sbt @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +echo "+----------------------------+" +echo "| Executing tests using sbt |" +echo "+----------------------------+" +sbt ++$TRAVIS_SCALA_VERSION test diff --git a/tutorial/index.html b/tutorial/index.html new file mode 100644 index 0000000..d91e364 --- /dev/null +++ b/tutorial/index.html @@ -0,0 +1,104 @@ + + + + Reactive Stocks - Activator Template + + +
+

The Reactive Stocks application has been created!

+ +

Explore the App

+ +

+ Once the application has been compiled and the server started, your application can be accessed at: http://localhost:9000
+ Check in Run to see the server status.
+
+ The first thing you will see are three stock charts which are being pushed values in real-time from the server. These values are simulated so the application always has interesting data flowing in real-time. Clicking on a stock chart will fetch recent news mentioning the stock symbol, use a service to do sentiment analysis on each news, and then display a buy, sell, or hold recommendation based on the aggregate sentiments. New stocks can be added to the list using the form in the header. +

+
+
+

Reactive Apps

+ +

+ As The Reactive Manifesto outlines, Reactive apps are Resilient, Interactive, Scalable, and Event-Driven. The Reactive Stocks application has all of these characteristics because it is built using the Typesafe Platform, including Play Framework for the web interface and Akka for managing concurrency, scalability and fault-tolerance. Both Java and Scala are for the back-end code since Play and Akka support both of those languages. Any piece written in Java could have been written in Scala, or vice-versa. The front-end uses HTML, LESS (compiled to CSS), and CoffeeScript (compiled to JavaScript). WebSockets are used to push data in real-time from the server to the client.
+
+ The Reactive Stocks application showcases four types of Reactive: Reactive Push, Reactive Requests, Reactive Composition, and Reactive UIs. Two types of Reactive which are not directly used in this application are Reactive Pull and 2-way Reactive. In a real stock feed application, the stock stream would be attached to an actual stock feed service using Reactive Pull. Those values would then be pushed to the client using Reactive Push. The combination of Reactive Push & Reactive Pull is 2-way Reactive. Play's internal network handling is 2-way Reactive. +

+
+
+

Reactive Push

+ +

+ This application uses a WebSocket to push data to the browser in real-time. To create a WebSocket connection in Play, first a route must be defined in the routes file. Here is the route which will be used to setup the WebSocket connection:
+

GET /ws controllers.Application.ws
+ The ws method in the Application.java controller handles the request and does the protocol upgrade to the WebSocket connection. This method create a new UserActor defined in UsersActor.java (tests). The UserActor stores the handle to the WebSocket connection.
+
+ Once the UserActor is created, the default stocks (defined in application.conf) are added to the user's list of watched stocks.
+
+ Each stock symbol has its own StockActor defined in StockActor.scala (tests). This actor holds the last 50 prices for the stock. Using a FetchHistory message the whole history can be retrieved. A FetchLatest message will generate a new price using the newPrice method in the FakeStockQuote.java file. Every StockActor sends itself a FetchLatest message every 75 milliseconds. Once a new price is generated it is added to the history and then a message is sent to each UserActor that is watching the stock. The UserActor then serializes the data as JSON and pushes it to the client using the WebSocket.
+
+ Underneath the covers, resources (threads) are only allocated to the Actors and WebSockets when they are needed. This is why Reactive Push is scalable with Play and Akka. +

+
+
+

Reactive UI - Real-time Chart

+ +

+ On the client-side a Reactive UI updates the stock charts every time a message is received. The index.scala.html file produces the web page at http://localhost:9000 and loads the JavaScript and CSS needed render the page and setup the UI.
+
+ The JavaScript for the page is compiled from the index.js file which is written in CoffeeScript (an elegant way to write JavaScript). Using jQuery, a page ready handler sets up the WebSocket connection and sets up functions which will be called when the server sends a message to the client through the WebSocket: +

$ ->
+  ws = new WebSocket $("body").data("ws-url")
+  ws.onmessage = (event) ->
+    message = JSON.parse event.data
+ The message is parsed and depending on whether the message contains the stock history or a stock update, a stock chart is either created or updated. The charts are created using the Flot JavaScript charting library. Using CoffeeScript, jQuery, and Flot makes it easy to build Reactive UI in the browser that can receive WebSocket push events and update the UI in real-time. +

+
+
+

Reactive Requests

+ +

+ When a web server gets a request, it allocates a thread to handle the request and produce a response. In a typical model the thread is allocated for the entire duration of the request and response, even if the web request is waiting for some other resource. A Reactive Request is a typical web request and response, but handled in an asynchronous and non-blocking way on the server. This means that when the thread for a web request is not actively being used, it can be released and reused for something else.
+ In the Reactive Stocks application the service which determines the stock sentiments is a Reactive Request. The route is defined in the routes file: +

GET /sentiment/:symbol controllers.StockSentiment.get(symbol)
+ A GET request to /sentiment/GOOG will call get("GOOG") on the StockSentiment.scala controller. That method begins with: +
def get(symbol: String): Action[AnyContent] = Action.async {
+ The async block indicates that the controller will return a Future[Result] which is a handle to something that will produce a Result in the future. The Future provides a way to do asynchronous handling but doesn't necessarily have to be non-blocking. Often times web requests need to talk to other systems (databases, web services, etc). If a thread can't be deallocated while waiting for those other systems to respond, then it is blocking.
+ In this case a request is made to Twitter and then for each tweet, another request is made to a sentiment service. All of these requests, including the request from the browser, are all handled as Reactive Requests so that the entire pipeline is Reactive (asynchronous and non-blocking). This is called Reactive Composition. +

+
+
+

Reactive Composition

+ +

+ Combining multiple Reactive Requests together is Reactive Composition. The StockSentiment controller does Reactive Composition since it receives a request, makes a request to Twitter for tweets about a stock, and then for each tweet it makes a request to a sentiment service. All of these requests are Reactive Requests. None use threads when they are waiting for a response. Scala's for comprehensions make it very easy and elegant to do Reactive Composition. The basic structure is: +

for {
+  tweets <- tweetsFuture
+  sentiments <- Future.sequence(futuresForTweetSentiment(tweets))
+} yield Ok(sentiments)
+ Because the web client library in Play, WS, is asynchronous and non-blocking, all of the requests needed to get a stock's sentiments are Reactive Requests. Combined together these Reactive Requests are Reactive Composition. +

+
+
+

Reactive UI - Sentiments

+ +

+ The client-side of Reactive Requests and Reactive Composition is no different than the non-Reactive model. The browser makes an Ajax request to the server and then calls a JavaScript function when it receives a response. In the Reactive Stocks application, when a stock chart is flipped over it makes the request for the stock's sentiments. That is done using jQuery's ajax method in the index.js file. When the request returns data the success handler updates the UI. +

+
+
+

Inspect the App

+ +

+ Use Inspect to see the requests that have been handled by the running Play application and to see the Akka Actors (if any) in the Actor System. Drilling down into an individual request will provide details about the request. Drilling down into an individual Actor will display a number of statistics and information about the Actor. Deviations will show you any issues with your Actors. +

+
+
+

Further Learning

+ +

+ The Reactive Stocks example combines Reactive Push, Reactive Requests, Reactive Composition, and a Reactive UI to create a Resilient, Interactive, Scalable, and Event-Driven application. Check out the Hello Scala!, Hello Play Framework!, and Hello Akka! templates to learn more about those technologies. Go back to the Activator home page to create a new application. +

+
+ +