Skip to content

Commit

Permalink
worldmap v2 poc
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Dec 23, 2015
1 parent 97272e2 commit 88efb22
Show file tree
Hide file tree
Showing 15 changed files with 162 additions and 363 deletions.
1 change: 1 addition & 0 deletions app/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ final class Env(
Env.video,
Env.shutup, // required to load the actor
Env.insight, // required to load the actor
Env.worldMap, // required to load the actor
Env.push // required to load the actor
)
play.api.Logger("boot").info("Preloading complete")
Expand Down
10 changes: 6 additions & 4 deletions app/controllers/WorldMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ object WorldMap extends LilaController {
Ok(views.html.site.worldMap())
}

def stream = Action {
Ok.chunked(
Env.worldMap.stream.producer &> EventSource()
) as "text/event-stream"
def stream = Action.async {
Env.worldMap.getStream map { stream =>
Ok.chunked(
stream &> EventSource()
) as "text/event-stream"
}
}
}
13 changes: 0 additions & 13 deletions app/views/site/worldMap.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,9 @@
<body>
<h1><a href="https://lichess.org">lichess<span class="extension">.org</span></a> network</h1>
<div id="worldmap"></div>
<div id="stats">
<div class="wrapL">
<div id="time">time: <span></span></div>
<div id="moves">moves: <span></span></div>
<div id="countries">countries: <span></span></div>
</div>
<div class="wrapL">
<div id="topCountries"></div>
</div>
</div>
@jQueryTag
@jsAt("worldMap/MIDIUtils.js")
@jsAt("worldMap/raphael-min.js")
@jsAt("worldMap/world.js")
@jsAt("worldMap/time.js")
@jsAt("worldMap/app.js")
@jsAt("worldMap/stats.js")
</body>
</html>
1 change: 0 additions & 1 deletion conf/base.conf
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ playban {
}
worldMap {
geoip = ${geoip}
players.cache_size = 8192
}
push {
collection.device = push_device
Expand Down
10 changes: 1 addition & 9 deletions modules/hub/src/main/ActorMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ trait ActorMap extends Actor {

def mkActor(id: String): Actor

def withEvents = false

def actorMapReceive: Receive = {

case Get(id) => sender ! getOrMake(id)
Expand All @@ -32,9 +30,7 @@ trait ActorMap extends Actor {
case Terminated(actor) =>
context unwatch actor
actors foreach {
case (id, a) if (a == actor) =>
actors -= id
if (withEvents) self ! ActorMap.Remove(id, a)
case (id, a) if (a == actor) => actors -= id
}
}

Expand All @@ -44,16 +40,12 @@ trait ActorMap extends Actor {
context.actorOf(Props(mkActor(id)), name = id) ~ { actor =>
actors += (id -> actor)
context watch actor
if (withEvents) self ! ActorMap.Add(id, actor)
}
}
}

object ActorMap {

case class Add(id: String, actor: ActorRef)
case class Remove(id: String, actor: ActorRef)

def apply(make: String => Actor) = new ActorMap {
def mkActor(id: String) = make(id)
def receive = actorMapReceive
Expand Down
9 changes: 5 additions & 4 deletions modules/hub/src/main/actorApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package actorApi

import lila.common.LightUser

import akka.actor.ActorRef
import play.api.libs.json._
import play.twirl.api.Html

Expand Down Expand Up @@ -194,9 +193,11 @@ case class NbRounds(nb: Int)
case class Abort(gameId: String, byColor: String)
case class Berserk(gameId: String, userId: String)
case class IsOnGame(color: chess.Color)
sealed trait DoorEvent
case class Open(gameId: String) extends DoorEvent
case class Close(gameId: String) extends DoorEvent
sealed trait SocketEvent
object SocketEvent {
case class OwnerJoin(gameId: String, color: chess.Color, ip: String) extends SocketEvent
case class Stop(gameId: String) extends SocketEvent
}
}

package evaluation {
Expand Down
5 changes: 0 additions & 5 deletions modules/round/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ final class Env(
lazy val eventHistory = History(db(CollectionHistory)) _

val roundMap = system.actorOf(Props(new lila.hub.ActorMap {
override val withEvents = true
def mkActor(id: String) = new Round(
gameId = id,
messenger = messenger,
Expand All @@ -72,10 +71,6 @@ final class Env(
case actorApi.GetNbRounds =>
nbRounds = size
hub.socket.lobby ! lila.hub.actorApi.round.NbRounds(nbRounds)
case lila.hub.ActorMap.Add(id, _) =>
system.lilaBus.publish(lila.hub.actorApi.round.Open(id), 'roundDoor)
case lila.hub.ActorMap.Remove(id, _) =>
system.lilaBus.publish(lila.hub.actorApi.round.Close(id), 'roundDoor)
}: Receive) orElse actorMapReceive
}), name = ActorMapName)

Expand Down
4 changes: 4 additions & 0 deletions modules/round/src/main/Socket.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ private[round] final class Socket(
override def postStop() {
super.postStop()
lilaBus.unsubscribe(self)
lilaBus.publish(lila.hub.actorApi.round.SocketEvent.Stop(gameId), 'roundDoor)
}

private def refreshSubscriptions {
Expand Down Expand Up @@ -146,6 +147,9 @@ private[round] final class Socket(
playerDo(color, _.ping)
sender ! Connected(enumerator, member)
if (member.userTv.isDefined) refreshSubscriptions
if (member.owner) lilaBus.publish(
lila.hub.actorApi.round.SocketEvent.OwnerJoin(gameId, color, ip),
'roundDoor)

case Nil =>
case eventList: EventList => notify(eventList.events)
Expand Down
21 changes: 13 additions & 8 deletions modules/worldMap/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package lila.worldMap

import com.typesafe.config.Config

import akka.actor._
import com.sanoma.cda.geoip.MaxMindIpGeo
import lila.common.PimpedConfig._

Expand All @@ -11,15 +12,19 @@ final class Env(

private val GeoIPFile = config getString "geoip.file"
private val GeoIPCacheTtl = config duration "geoip.cache_ttl"
private val PlayersCacheSize = config getInt "players.cache_size"

lazy val players = new Players(PlayersCacheSize)

lazy val stream = new Stream(
system = system,
players = players,
geoIp = MaxMindIpGeo(GeoIPFile, 0),
geoIpCacheTtl = GeoIPCacheTtl)
private val stream = system.actorOf(
Props(new Stream(
geoIp = MaxMindIpGeo(GeoIPFile, 0),
geoIpCacheTtl = GeoIPCacheTtl)))

def getStream = {
import play.api.libs.iteratee._
import play.api.libs.json._
import akka.pattern.ask
import makeTimeout.short
stream ? Stream.Get mapTo manifest[Enumerator[JsValue]]
}
}

object Env {
Expand Down
37 changes: 0 additions & 37 deletions modules/worldMap/src/main/Players.scala

This file was deleted.

117 changes: 73 additions & 44 deletions modules/worldMap/src/main/Stream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,62 +3,91 @@ package lila.worldMap
import akka.actor._
import com.google.common.cache.LoadingCache
import com.sanoma.cda.geoip.{ MaxMindIpGeo, IpLocation }
import lila.hub.actorApi.round.{ Open, Close, DoorEvent }
import lila.hub.actorApi.round.SocketEvent
import play.api.libs.iteratee._
import play.api.libs.json._
import scala.concurrent.duration._

final class Stream(
system: ActorSystem,
players: Players,
import lila.rating.PerfType

private final class Stream(
geoIp: MaxMindIpGeo,
geoIpCacheTtl: Duration) {
geoIpCacheTtl: Duration) extends Actor {

system.lilaBus.subscribe(system.actorOf(Props(new Actor {
def receive = {
case e: DoorEvent => channel push e
}
})), 'roundDoor)

private val (enumerator, channel) = Concurrent.broadcast[DoorEvent]

private val ipCache = lila.memo.Builder.cache(geoIpCacheTtl, localizeIp)
private def localizeIp(ip: String): Option[Location] =
geoIp getLocation ip flatMap Location.apply

def processOpen(id: String) = Input.Empty
// ipCache get move.ip match {
// case None => Input.Empty
// case Some(loc) =>
// val opponentLoc = players.getOpponentLocation(move.gameId, loc)
// Input.El(List(
// loc.country,
// loc.lat,
// loc.lon,
// opponentLoc.map(_.lat) getOrElse "",
// opponentLoc.map(_.lon) getOrElse "",
// move.move,
// move.piece,
// opponentLoc.map(_.country) getOrElse ""
// ) mkString "|")
// }
def processClose(id: String) = Input.Empty

private val processor: Enumeratee[DoorEvent, String] =
Enumeratee.mapInput[DoorEvent].apply[String] {
case Input.El(Open(id)) => processOpen(id pp "open")
case Input.El(Close(id)) => processClose(id pp "close")
case _ => Input.Empty
import Stream.gameWriter

override def preStart() {
context.system.lilaBus.subscribe(self, 'roundDoor)
}

val games = scala.collection.mutable.Map.empty[String, Stream.Game]

val ips =
List("31.37.30.208", "62.129.6.2", "109.24.166.168", "62.129.6.2", "109.24.166.168", "93.2.72.83", "93.2.72.83", "62.129.6.2", "209.141.138.167", "62.129.6.2", "177.21.101.151", "62.129.6.2", "58.107.225.215", "109.24.166.168", "62.129.6.2", "62.129.6.2", "62.129.6.2", "177.19.74.106", "41.142.99.133", "88.122.168.130", "37.160.4.18", "109.24.166.168", "58.107.225.69", "62.129.6.2", "177.158.175.244", "88.184.4.183", "177.21.101.226", "109.24.166.168", "60.242.16.37", "109.24.166.168", "122.108.140.228", "109.24.166.168", "88.184.4.183", "58.107.233.63", "216.58.58.56", "109.24.166.168", "41.140.91.160", "216.58.58.56", "135.0.154.66", "109.24.166.168", "109.24.166.168", "37.218.171.237", "58.107.241.144", "187.40.154.170", "109.24.166.168", "109.24.166.168", "88.184.4.183", "109.24.166.168", "109.24.166.168", "88.122.168.130", "177.21.101.151", "62.129.6.2", "82.242.133.237", "41.140.80.251", "220.239.190.247", "82.247.11.49", "109.24.166.168", "62.129.6.2", "109.24.166.168", "62.129.6.2", "58.107.251.186", "109.24.166.168", "109.24.166.168", "58.107.250.187", "122.108.137.120", "109.24.166.168", "109.24.166.168", "122.108.135.245", "109.24.166.168", "62.129.6.2", "62.129.6.2", "109.24.166.168", "62.129.1.150", "58.107.237.219", "58.107.251.186", "88.184.4.183", "109.24.166.168", "62.129.6.2", "125.253.49.130", "209.141.138.167", "122.108.137.120", "109.24.166.168", "109.24.166.168", "58.107.247.107", "62.129.6.2", "122.108.137.120", "62.129.6.2", "49.181.136.30", "109.24.166.168", "58.107.250.187", "88.184.4.183", "125.255.162.254", "109.24.166.168", "62.129.6.2", "62.129.6.2", "31.37.30.208", "109.24.166.168", "37.161.63.173", "125.253.49.130", "62.129.6.2", "125.253.49.130", "62.129.6.2", "58.107.235.69", "78.244.125.234", "58.107.251.186", "62.129.1.150", "88.184.4.183", "122.108.138.178", "37.162.53.77", "109.24.166.168", "60.242.16.37", "62.129.6.2", "62.129.6.2", "109.24.166.168", "109.24.166.168", "109.24.166.168", "62.129.6.2", "88.122.168.130", "109.24.166.168", "209.141.138.167", "62.129.6.2", "122.108.138.178", "62.129.6.2", "109.24.166.168", "79.81.250.122", "88.184.4.183", "109.24.166.168", "58.107.250.187", "58.107.247.107", "58.107.251.186", "31.37.30.208", "88.122.168.130", "125.255.162.254", "88.184.4.183", "62.129.6.2", "31.37.30.208", "209.141.138.167", "37.163.179.115", "41.140.78.14", "58.107.230.85", "122.108.134.105", "216.58.58.56", "58.107.248.197", "62.129.6.2", "109.24.166.168", "62.129.6.2", "177.55.241.56", "109.24.166.168", "109.24.166.168", "62.129.6.2", "109.24.166.168", "216.58.58.56", "49.181.139.201", "109.24.166.168", "109.24.166.168", "95.6.28.61", "115.64.76.89", "109.24.166.168", "109.24.166.168", "62.129.6.2", "58.107.251.186", "216.58.58.56", "60.242.16.37", "177.98.169.193", "216.58.58.56", "135.0.154.66", "90.63.135.91", "216.58.58.56", "58.107.250.128", "216.58.58.56", "177.21.97.50", "62.129.6.2", "216.58.58.56", "216.58.58.56", "88.250.52.219", "58.107.225.69", "109.24.166.168", "58.107.235.33", "109.24.166.168", "109.24.166.168", "58.107.235.69", "177.21.101.151", "216.58.58.56", "109.24.166.168", "216.58.58.56", "37.163.36.115", "153.107.97.153", "62.129.6.2", "62.129.6.2", "216.58.58.56", "93.2.72.83", "216.58.58.56", "93.2.72.83", "216.58.58.56", "88.184.4.183", "177.21.101.151", "129.94.158.10", "200.186.74.178", "31.37.30.208", "216.58.58.56", "135.0.154.169", "37.165.62.15", "88.184.4.183", "122.108.139.72", "122.108.138.178", "91.188.153.29", "122.108.137.120", "177.21.101.151", "135.0.154.66", "195.114.249.2", "109.24.166.168", "88.184.4.183", "78.244.125.234", "216.58.58.56", "80.12.59.84", "135.0.154.66", "216.58.58.56", "62.129.6.2", "216.58.58.56", "109.24.166.168", "88.184.4.183", "216.58.58.56", "88.184.4.183", "95.6.28.61", "216.58.58.56", "135.0.154.66", "192.168.0.10", "62.129.6.2", "122.108.134.105", "216.58.58.56", "216.58.58.56", "216.58.58.56", "88.184.4.183", "62.129.6.2", "58.107.231.116", "62.129.6.2", "58.107.250.187", "122.108.134.201", "41.248.63.182", "122.108.130.72", "109.24.166.168", "62.129.6.2", "109.24.166.168", "109.24.166.168", "62.129.6.2", "216.58.58.56", "109.24.166.168", "88.250.52.219", "109.24.166.168", "216.58.58.56", "88.184.4.183", "91.121.89.85", "88.122.168.130", "216.58.58.56", "216.58.58.56", "216.58.58.56", "135.0.154.66", "90.84.144.177", "58.107.229.201", "88.184.4.183", "58.107.231.116", "37.162.81.198", "62.129.6.2", "49.181.17.227", "122.108.129.132", "122.108.129.132", "135.0.154.66", "62.129.6.2", "109.24.166.168", "58.107.250.187", "58.107.245.184", "216.58.58.56", "82.228.49.145", "58.107.235.69", "177.97.116.243", "135.0.154.66", "58.107.250.187", "62.129.6.2", "58.107.251.186", "216.58.58.56", "91.121.89.85", "216.58.58.56", "88.184.4.183", "62.129.6.2", "135.0.154.66", "122.108.141.30", "109.24.166.168", "216.58.58.56", "216.58.58.56", "58.107.248.197", "216.58.58.56", "122.108.130.72", "88.184.4.183", "216.58.58.56", "58.107.248.197", "153.107.33.155", "58.107.231.116", "58.107.235.69", "109.24.166.168", "88.184.4.183", "122.108.130.15")

def receive = {
case SocketEvent.OwnerJoin(id, color, ip) =>
// ipCache get ip foreach { point =>
ipCache get scala.util.Random.shuffle(ips).head foreach { point =>
val game = games get id match {
case Some(game) => game withPoint point
case None => Stream.Game(id, List(point))
}
games += (id -> game)
channel push Stream.Event.Add(game)
}
case SocketEvent.Stop(id) =>
games -= id
channel push Stream.Event.Remove(id)
case Stream.Get => sender ! {
Enumerator enumerate games.values.map(gameWriter.writes) andThen producer
}
}

val (enumerator, channel) = Concurrent.broadcast[Stream.Event]

val producer = enumerator &> processor
val producer = enumerator &> Enumeratee.map[Stream.Event].apply[JsValue] {
case Stream.Event.Add(game) => Json toJson game
case Stream.Event.Remove(id) => Json.obj("id" -> id)
}

val ipCache = lila.memo.Builder.cache(geoIpCacheTtl, ipToPoint)
def ipToPoint(ip: String): Option[Stream.Point] =
geoIp getLocation ip flatMap Location.apply map { loc =>
Stream.Point(loc.lat, loc.lon)
}
}

object Stream {

case object Get

case class Game(
id: String,
points: List[Point]) {

def withPoint(point: Point) =
if (points contains point) this
else copy(points = point :: points.take(1))
}

private implicit def gameWriter: Writes[Game] = Writes { game =>
Json.obj(
"id" -> game.id,
"ps" -> Json.toJson {
game.points.map { p =>
List(p.lat, p.lon)
}
}
)
}

case class Point(lat: Double, lon: Double)

sealed trait Event
case class Open(id: String, white: Player, black: Player) extends Event
case class Close(id: String) extends Event
object Event {
case class Add(game: Game) extends Event
case class Remove(id: String) extends Event
}

case class Player(country: String, lat: Float, long: Float)
}
2 changes: 1 addition & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ object ApplicationBuild extends Build {
libraryDependencies ++= provided(play.api, RM, PRM)
)

lazy val worldMap = project("worldMap", Seq(common, hub, memo)).settings(
lazy val worldMap = project("worldMap", Seq(common, hub, memo, rating)).settings(
libraryDependencies ++= provided(play.api, maxmind)
)

Expand Down
Loading

0 comments on commit 88efb22

Please sign in to comment.