Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Akka HTTP Websocket, how to identify connections inside of actor

I'm working on simple mulitplayer game in scala that I would like to expose through websockets for JS clients.

Here is my WebsocketServer class

class WebsocketServer(actorRef: ActorRef, protocol: Protocol, system: ActorSystem, materializer: ActorMaterializer) extends Directives {

    val route = get {
      pathEndOrSingleSlash {
        handleWebSocketMessages(websocketFlow)
      }
    }

    def websocketFlow: Flow[Message, Message, Any] =
      Flow[Message]
        .map {
          case TextMessage.Strict(textMessage) => protocol.hydrate(textMessage)
        }
        .via(actorFlow)
        .map(event => TextMessage.Strict(protocol.serialize(event)))


    def actorFlow : Flow[Protocol.Message, Protocol.Event, Any] = {
      val sink =
        Flow[Protocol.Message]
          .to(Sink.actorRef[Protocol.Message](actorRef, Protocol.CloseConnection()))

      val source =
        Source.actorRef[Protocol.Event](1, OverflowStrategy.fail)
          .mapMaterializedValue(actor => actorRef ! Protocol.OpenConnection(actor))

      Flow.fromSinkAndSource(sink, source)
    }
}

This is simplified code of my actor that should receive messages from websocket server.

class GameActor() extends Actor {

  private var connections: List[ActorRef] = List()

  override def receive: Receive = {

    case message: Protocol.OpenConnection => {
      this.connections = message.connection :: this.connections
      message.connection ! Protocol.ConnectionEstablished()
    }

    case message: Protocol.CloseConnection => {
      // how can I remove actor from this.connections ?
    }

    case message: Protocol.DoSomething => {
      // how can I identify from which connection this message came in?
    }
  }
}

So far so good, currently I'm able to respond with simple WelcomeMessage to a client however I still don't know how to:

  • remove actor from connections list whenever actor receive CloseConnection message?
  • identify from which connection message came to the actor?
like image 761
Norbert Orzechowicz Avatar asked Dec 01 '16 11:12

Norbert Orzechowicz


1 Answers

I think you need to have some sort of key or id to map with your connection actor.

def websocketFlow: Flow[Message, Message, Any] =
  val randomKey = Random.nextInt()
      Flow[Message]
        .map {
          case TextMessage.Strict(textMessage) => protocol.hydrate(textMessage)
        }
        .via(actorFlow(randomKey))
        .map(event => TextMessage.Strict(protocol.serialize(event)))


    def actorFlow(flowID: Int) : Flow[Protocol.Message, Protocol.Event, Any] = {
      val sink =
        Flow[Protocol.Message]
          .to(Sink.actorRef[Protocol.Message](actorRef, Protocol.CloseConnection(flowID)))

      val source =
        Source.actorRef[Protocol.Event](1, OverflowStrategy.fail)
          .mapMaterializedValue(actor => actorRef ! Protocol.OpenConnection(actor, flowID))

      Flow.fromSinkAndSource(sink, source)
    }

then in your actor you can store the connections in a Map instead of List, which also happen to be more efficient to be remove.

like image 169
Qingwei Avatar answered Sep 28 '22 05:09

Qingwei